• 검색 결과가 없습니다.

3장에서는 GIVI의 기본적인 동작에 대해 설명하고, GIVI에서 데이터 를 관리하는 방식에 대해 설명한 뒤,

가. 전체

GIVI는 앞서 설명했듯이 GLORE에 사용자 요청을 전달하고 그 결과 를 GLORE로부터 받아서 디스플레이 장치에 전달하는 역할을 수행한 다. 이 때, 모든 데이터 전송은 메시지를 기반으로 하며, GIP의 Requ est와 Response 메시지를 이용해서 서로 통신을 수행하게 된다. [그 림 3-1]은 GIVI의 전체적인 동작을 보여주는 Sequence 다이어그램 을 보여주고 있다.

[그림 3-1]에서 알 수 있듯이, GIVI는 메시지 아이디와 관련 파라미 터를 보냄으로써 GIPTransaction과의 트랜잭션을 생선한다. 일단 트

void GIVI::ProcessMsg(void) {

unsigned short msgId = 0;

int nRet;

char errMsg[80];

nRet = gipResQ->Receive(&msgId, 0);

if ( nRet >= 0) {

sprintf(errMsg, "msgId(%u)\n", msgId);

glvLog::Write(errMsg, 0);

gipTrans *trans;

trans = gipResDB->GetTransaction(&msgId);

if (trans == NULL) {

printf("GetTransaction(%u) fail\n", msgId);

sprintf(errMsg, "GetTransaction(%u) fail\n", msgId);

glvLog::Write(errMsg, 0);

gipMsg = ProcessCmdRes(trans->trans, trans->trLen);

gContextData->SetButton2On();

break;

default :

printf("Unknown gip primitive\n");

gipMsg = NULL;

break;

}

nRet = gipTrDB->Delete(&msgId, sizeof(unsigned short));

if (nRet < 0) {

printf("gipTrDB->Delete(%u) fail\n", msgId);

} else {

printf("gipTrDB->Delete(%u) success\n", msgId);

}

if (gipMsg != NULL) {

gipMsg->SetMessage(NULL, 0);

gipMessage *GIVI::ProcessCmdRes(unsigned char *cpMsg, unsigned int nLen) {

double boundValue[6];

vtkColorTransferFunction *tmpTF;

switch (cpMsg[CMD_RES_TYPE_POS]) {

case CMD_RES_POLY_DATA :

cmdResPolyData = new gipCmdResPolyData();

cmdResPolyData->Decode(cpMsg, nLen);

ProcessCmdResPolyData(cmdResPolyData);

return cmdResPolyData;

case CMD_RES_LOAD :

cmdResLoad = new gipCmdResLoad();

cmdResLoad->Decode(cpMsg, nLen);

gContextData->SetEndTime(cmdResLoad->GetEndTimeStep());

gContextData->SetDeltaAngle(cmdResLoad->GetDeltaAngle());

polydata = cmdResLoad->GetHistogram();

size = cmdResLoad->GetHistogramLen();

histo = gloreHistogram::New(polydata, size);

gContextData->SetPressureTF(histo->GenTF("Pressure"));

tmpTF = histo->GenTF("Velocity");

if (tmpTF == NULL) {

tmpTF = histo->GenTF("Velocity Magnitude");

}

gContextData->SetVelTF(tmpTF);

tmpTF = histo->GenTF("Vorticity");

if (tmpTF == NULL) {

tmpTF = histo->GenTF("Vorticity Magnitude");

}

gContextData->SetVorTF(tmpTF);

tmpTF = histo->GenTF("Q-Criteria");

if (tmpTF == NULL)

{

bounds = cmdResLoad->GetBounds();

boundValue[0] = bounds->minX;

boundValue[1] = bounds->maxX;

boundValue[2] = bounds->minY;

boundValue[3] = bounds->maxY;

boundValue[4] = bounds->minZ;

boundValue[5] = bounds->maxZ;

gContextData->SetPolyBounds(boundValue);

msgId = cmdResLoad->GetMsgId();

gipResDB->Delete(&msgId, sizeof(msgId));

/// delete loading message from dotree

if (gRoot->SearchNode("LoadMsgPanelTransform") != NULL) {

dotree::Transform *lmPanelTransform = gRoot->SearchNode("LoadM sgPanelTransform")->GetTransform();

dotree::Object *lmPanelObject = lmPanelTransform->GetChild(0)->G etObject();

lmPanelObject->GetPanel()->DeleteAll();

lmPanelTransform->RemoveChildren();

gRoot->RemoveChild(lmPanelTransform);

}

return cmdResLoad;

case CMD_RES_IMAGE :

cmdResImage = new gipCmdResImage();

cmdResImage->Decode(cpMsg, nLen);

cmdResImage->Print();

ProcessCmdResImageData(cmdResImage);

return cmdResImage;

case CMD_RES_STATUS :

cmdResStatus = new gipCmdResStatus();

cmdResStatus->Decode(cpMsg, nLen);

cmdResStatus->Print();

msgId = cmdResStatus->GetMsgId();

if (msgId == gContextData->GetProbByPlaneId()) {

// if message id is equal to probe by plane msgId ProcessProbeByPlane();

gPlaneWidget->GetPlaneActor()->GetProperty()->SetRepresentationT oSurface();

호출해서 오브젝트에 대한 렌더링을 수행한다.

메시지가 CMD_RES_LOAD 타입의 메시지인 경우, 즉, 데이터 로딩 요청에 대한 응답인 경우에는 메시지를 디코딩한 뒤, 메시지에 들어있 는 기본적인 컨텍스트 데이터를 설정하고, 히스토그램 정보를 해석해 서 컨텍스트 데이터 내에 Transfer Function을 저장한다. 이 때, Tran sfer Function의 종류는 Pressure, Velocity, Velocity Magnitude, Vo rticity, Vorticity Magnitude, Q-Criteria 등이 있으며, 이런 히스토그

[그림 3-2] cmd.req 메시지 상세

[그림 3-3] cmd.Res 메시지 상세

Data와 Status, 그리고 Transfer function key의 이 3가지가 존재한 다. [그림 3-3]은 cmd.Res 메시지를 나타낸다.

다. 초기화

GIVI가 GIP과의 커넥션을 초기화하는 과정은 [그림 3-4]의 Sequenc e 다이어그램에서 설명돼 있다.

GIVI에서는 Read()함수를 실행해서 초기화에 필요한 설정 정보를 가 져온 뒤, GetShmKey(), GetMaxTr() 함수를 실행함으로써 초기화에 필요한 공유 메모리의 키와 트랜잭션 DB의 크기를 가져온다. 이 값으 로 Initialize를 수행한 뒤에는 GetQKey()와 GetMaxItem() 함수를 수행함으로써 QDB의 키 값과 maxItem 값을 가져오고, 이 값을 토대 로 최종적으로 GIPConnector와의 커넥션을 얻게 된다.

GIVI에서 이 과정은 GIVI 클래스의 init() 함수에서 수행하게 되는데, 이에 대한 구체적인 소스 코드는 [소스 3-3]에서 볼 수 있다.

void GIVI::init()

gipTrDB = new gipTransactionDB(gipConf->GetTrDBKey());

nRet = gipTrDB->CreateTable("GipTransaction", 250, 10, 1, sizeof(short));

if (nRet < 0) {

sprintf(errMsg, "gipTrDB->CreateTable(%x) fail\n", gipConf->GetTrDBKey ());

glvLog::Write(errMsg, 0);

}

sprintf(errMsg, "gipTrDB->CreateTable(%x) success\n", gipConf->GetTrDBKey ());

glvLog::Write(errMsg, 0);

if (strcmp(host, gipConf->GetMasterHost()) == 0) {

gContextData->SetMasterTrue();

gipMsgQ = new gipMessenger();

nRet = gipMsgQ->Create(gipConf->GetMsgQKey());

if (nRet < 0) {

printf("gipMsgQ->Create(%x) fail\n", gipConf->GetMsgQKey());

sprintf(errMsg, "gipMsgQ->Create(%x) fail\n", gipConf->GetMsgQKey(

));

glvLog::Write(errMsg, 0);

} }

gipResDB = new gipVariableTrDB(gipConf->GetResDBKey());

nRet = gipResDB->CreateTable("ResTable", gipConf->GetResTrSize(), gipConf ->GetMaxResTrNum(), 1, sizeof(unsigned short));

if (nRet < 0) {

sprintf(errMsg, "resTrDB->CreateTable(%x) fail\n", gipConf->GetResDBKey ());

glvLog::Write(errMsg, 0);

}

sprintf(errMsg, "resTrDB->CreateTable(%x) success\n", gipConf->GetResDBK ey());

glvLog::Write(errMsg, 0);

gipResQ = new gipMessenger();

nRet = gipResQ->Create(gipConf->GetResMsgQKey());

if (nRet < 0) {

printf("msgQ->Create(%x)\n", gipConf->GetResMsgQKey());

sprintf(errMsg, "msgQ->Create(%x)\n", gipConf->GetResMsgQKey());

glvLog::Write(errMsg, 0);

[그림 3-5] 데이터 로딩 과정 생성하는 것으로 GIP과의 커넥션을 초기화한다.

이렇게 커넥션이 초기화되고 나면 사용자와의 인터랙션에 따라 여러 동작을 수행하게 된다. 여기에서는 데이터 로딩과 폴리곤 데이터의 렌 더링, 그리고 Probe By Plane의 동작 기제에 대해 설명하기로 한다.

라. 데이터 로딩

GIVI는 실행 초기 단계에서 데이터를 로딩하고 그 로딩된 데이터를 기반으로 모든 동작을 수행한다.

데이터를 로딩하는 과정은 [그림 3-5]의 Sequence 다이어그램과 같 다.

GIVI에서 사용자가 Load 버튼을 클릭하면 내부적으로 LoadDataCB() 함수가 실행된다. 그러면 GIVI는 사전에 설정 파일에 설정돼 있는 데 이터 디렉토리의 파일 목록을 사용자에게 보여준다. 그런 다음, 사용 자가 선택한 파일명을 기반으로 트랜잭션을 생성, 데이터를 로딩하는 작업을 수행한다.

[그림 3-6] cmdReq: Load

void LoadFileCB(vtkObject *cObject, unsigned long eid, void *clientdata, void * calldata)

{

char *filename = (char*)(clientdata);

unsigned short msgId = gContextData->GetMsgId();

msgId++;

gContextData->SetMsgId(msgId);

gipCmdReqLoad *loadMsg = new gipCmdReqLoad();

우선 GIVI는 사용자가 클릭한 파일명을 기반으로 파일을 읽는 Read 명령을 수행한다. 이 때, 새로운 메시지가 생성되는데, 이 메시지에는 Load 명령과 함께 데이터의 위치와 디렉토리 정보가 함께 수록된다.

이 메시지를 기반으로 request를 보내면 GIP은 GLORE로 request를 전달한 뒤, GLORE가 로딩한 데이터를 GetCmd()와 GetRes() 명령을 통해 가져와서 데이터의 초기화 정보를 관리하게 된다.

이 때, Load 작업을 수행하는 request 메시지 내에는 로딩할 데이터 의 파일명이 실리게 된다. Load 작업에 대한 request 메시지는 [그림 3-6]에 나와 있다.

실질적으로 Load 메시지를 생성하고 전송하는 명령을 수행하는 코드 는 다음의 [소스 3-4]와 같다.

loadMsg->SetPrimitive(CMD_REQ);

gContextData->SetEndTime(cmdResLoad->GetEndTimeStep());

gContextData->SetDeltaAngle(cmdResLoad->GetDeltaAngle());

polydata = cmdResLoad->GetHistogram();

마. 폴리곤 데이터의 처리

데이터 로딩이 끝나면 GIVI는 사용자의 요구사항을 GLORE로 전송하 고, 그 처리 결과를 화면에 디스플레이한다. 가장 흔한 경우가 폴리곤 데이터를 처리하는 경우로, 여기에서는 Rotor 메뉴의 Pressure 버튼 을 클릭했을 경우를 예제로 들어보기로 한다.

[그림 3-7]은 Pressure 메뉴를 클릭했을 때의 GIVI의 동작을 보여주 는 Sequence 다이어그램이다.

GIVI는 메시지에 폴리곤 데이터를 로딩하는 데 필요한 정보(타임 스 텝, 데이터의 종류 등)를 담아서 인코딩하고, GIP Manager를 통해 G LORE에 전송하게 된다.

여기에 필요한 request 메시지는 [그림 3-8]에서 볼 수 있다. 여기에 는 Scalar Distribution의 타입과 현재 디스플레이되고 있는 컨텐츠의 타임스텝이 나와 있으며, GLORE는 이 값에 근거해서 적당한 데이터 를 response 메시지로 전송하게 된다.

폴리곤 데이터를 요청하는 request 메시지 중에는 scalar distribution

[그림 3-8] cmdReq: Scalar Distribution

[그림 3-9] cmdReq: Iso-Surface

외에도 isosurface를 요청하는 메시지도 있다. isosurface를 요청하는 메시지는 [그림 3-9]에 나와 있다. isosurface의 종류에는 Vorticity, Q-Criteria, Velocity의 3가지 유형이 있으며, 이 메시지에는 값의 범 위를 지정한 값도 함께 인코딩돼서 전송되므로, GLORE에서는 해당

void BladePressureCB( vtkObject* cObject, unsigned long eid, void* clientdata, void *calldata )

{

/// Request message setting

gContextData->SetBladeModeOn();

unsigned short msgId = gContextData->GetNextMsgId();

gContextData->GetReqScalar()->SetMsgId(msgId);

case CMD_RES_POLY_DATA :

cmdResPolyData = new gipCmdResPolyData();

cmdResPolyData->Decode(cpMsg, nLen);

ProcessCmdResPolyData(cmdResPolyData);

[소스 3-7] GIVI에서의 폴리곤 데이터 요청에 대한 response 처리

[그림 3-10] cmdReq: Probe by Plane Body

바. Probe Plane

Probe Plane에 대해 request 메시지를 작성하고 응답으로 온 데이터 를 로딩하는 과정은 기본적으로 폴리곤 데이터를 로딩하는 과정과 동 일하다 볼 수 있다.

Probe Plane에 대한 request 메시지는 [그림 3-10]에 나와 있다.

request message에는 Presentation Type, Time Step, Plane Coordin ate 및 Presentation Body가 포함된다. 이 때, Probe Plane의 타입으 로는 Contour line과 Scalar distribution이 있으며, 여기에 GIVI에서 계산한 평면의 네 꼭지점의 좌표가 Plane Coordinate 부분에 인코딩 된다. 만약 Presentation Type이 Scalar인 경우에는 Presentation Bo dy 정보가 생략되며, Contour line의 경우엔 사용자가 지정한 값이 입 력, 인코딩된다.

gipCmdReqProbeByPlane *cmdPbyPlane = new gipCmdReqProbeByPlane();

cmdPbyPlane->SetPrimitive(CMD_REQ);

cmdPbyPlane->SetMsgType(CMD_REQ_PROBE_BY_PLANE);

unsigned short msgId = gContextData->GetNextMsgId();

cmdPbyPlane->SetMsgId(msgId);

struct gipPlane plane;

plane.upperLeft.x = origin[0];

plane.upperLeft.y = origin[1];

plane.upperLeft.z = origin[2];

plane.upperRight.x = point1[0];

plane.upperRight.y = point1[1];

plane.upperRight.z = point1[2];

plane.lowerRight.x = point3[0];

plane.lowerRight.y = point3[1];

plane.lowerRight.z = point3[2];

plane.lowerLeft.x = point2[0];

plane.lowerLeft.y = point2[1];

plane.lowerLeft.z = point2[2];

cmdPbyPlane->SetPlaneCoordinate(plane);

cmdPbyPlane->SetTimeStep(gContextData->GetCurrentTimeStep());

cmdPbyPlane->SetGenMethod(gipCmdReqProbeByPlane::Auto);

switch (probMode) {

case PROBE_CONTOUR_VORTICITY :

cmdPbyPlane->SetValueType(gipCmdReqProbeByPlane::Vorticity);

[소스 3-8]에서는 Probe Plane에 대한 request 메시지가 인코딩되는 과정을 볼 수 있다.

cmdPbyPlane->SetPresentationType(gipCmdReqProbeByPlane::Cont ourLine);

cmdPbyPlane->SetLineNum(probLineNum);

break;

case PROBE_CONTOUR_QCRITERIA :

cmdPbyPlane->SetValueType(gipCmdReqProbeByPlane::QCriteria);

cmdPbyPlane->SetPresentationType(gipCmdReqProbeByPlane::Cont ourLine);

cmdPbyPlane->SetLineNum(probLineNum);

break;

case PROBE_CONTOUR_PRESSURE :

cmdPbyPlane->SetValueType(gipCmdReqProbeByPlane::Pressure);

cmdPbyPlane->SetPresentationType(gipCmdReqProbeByPlane::Cont ourLine);

cmdPbyPlane->SetLineNum(probLineNum);

break;

case PROBE_SCALAR_VORTICITY :

cmdPbyPlane->SetValueType(gipCmdReqProbeByPlane::Vorticity);

cmdPbyPlane->SetPresentationType(gipCmdReqProbeByPlane::Scal arDistribution);

break;

case PROBE_SCALAR_QCRITERIA :

cmdPbyPlane->SetValueType(gipCmdReqProbeByPlane::QCriteria);

cmdPbyPlane->SetPresentationType(gipCmdReqProbeByPlane::Scal arDistribution);

break;

case PROBE_SCALAR_PRESSURE :

cmdPbyPlane->SetValueType(gipCmdReqProbeByPlane::Pressure);

cmdPbyPlane->SetPresentationType(gipCmdReqProbeByPlane::Scal arDistribution);

break;

default :

printf("Unknown Probing Contents Type\n");

return;

[소스 3-8] GIVI에서의 Probe by Plane에 대한 request

사. GIVI의 컨텍스트 관리

GIVI는 실행시간 동안 현재 디스플레이하고 있는 컨텐츠들에 관한 정 보를 유지하고 업데이트하면서 디스플레이 정보를 관리하며, 이 정보 에 근간해서 GLORE에 오브젝트 관련 메시지를 요청하게 된다.

이를 위해 GIVI에서는 giviContext라는 클래스를 전역변수로 생성, G IVI가 실행되는 동안의 디스플레이 상황을 이 변수를 통해 관리한다.

이렇게 GIVI가 관리하는 정보로는

w 현재 디스플레이되고 있는 오브젝트의 종류 및 해당 오브젝트가 전송된 메시지의 메시지 아이디

w 타임스텝 관련 정보: 현재 타임스텝, Start Time, End Time w 델타 앵글

w 바운딩 박스 정보

w 컷팅 플레인을 생성하는 Probe Plane에 관한 정보: 메시지 아이디, Contour의 경우 contour의 개수, 현재 Probe하고 있는 컨텐츠의 종류 등

w 애니메이션 관련 정보

w GIVI에서 사용하는 각종 request 메시지 등이 있다.

다음은 giviContext 클래스의 멤버 변수를 나타낸다.

w mMsgId: unsigned short w mAniMsgId: unsigned short w mProbByPlaneId: unsigned short w mBladePressureId: unsigned short w mVelIsoId: unsigned short

w mVorIsoId: unsigned short w mVelStreamId: unsigned short w mVorStreamId: unsigned short

w mProbByPlaneAniId: unsigned short w mBladePressureAniId: unsigned short w mVelIsoAniId: unsigned short

w mVorIsoAniId: unsigned short w mVelStreamAniId: unsigned short w mVorStreamAniId: unsigned short w mQCriteriaAniId: unsigned short w mCurrentTimeStep: unsigned short w mStartTime: unsigned short

w mEndTime: unsigned short w mDeltaAngle: float

w mBladeMode: bool w mVelIsoMode: bool w mVorIsoMode: bool w mStreamlineMode: bool w mQCriteriaMode: bool w mGraphMode: bool w mPbyPlaneMode: bool w mIsAniNextFirst: bool w mProbContext: int

w mProbLineNum: unsigned short w mIsoValue: double

w mNumAniCache: int

w mReqScalar: gipCmdReqScalar * w mReqIsoS: gipCmdReqIsoS *

w mReqStreamLine: gipCmdReqStreamLine * w mReqGraph: gipCmdReqGraph *

w mReqProbeByPlane: gipCmdReqProbeByPlane * w mTF: vtkColorTransferFunction **

w mAniMaster: bool w mIsAniStart: bool w mButtonStatus: bool[3]

w mPolyBounds: double[6]

w mGiviAni: giviAnimation *

다음은 giviContext 클래스의 멤버 함수다.

w SetMsgId(value: unsigned short): void

w mMsgId 값을 지정된 값으로 설정한다. 이 mMsgId 값은 GIVI 에서 메시지 아이디를 부여하기 위해 사용하는 변수로, 현재의 메시지 아이디중 최대값을 나타낸다.

w GetMsgId(value: void): unsigned short w mMsgId값을 리턴한다.

w GetNextMsgId(value: void): unsigned short

w GIVI에서 메시지 아이디를 부여하기 위해 사용하는 함수로, m MsgId값에 1을 더한 값을 리턴한다.

w SetAniMsgId(value: unsigned short): void w mAniMsgId값을 지정된 값으로 설정한다.

w GetAniMsgId(value: void): unsigned short w mAniMsgId값을 리턴한다.

w GetNextAniMsgId(value: void): unsigned short

w GIVI에서 애니메이션 메시지 아이디를 부여하기 위해 사용하 는 함수로, mAniMsgId값에 1을 더한 값을 리턴한다.

w SetObjId(value: unsigned short, unsigned char, unsigned char):

void

를 설정하는 함수

w mVelIsoId, mVorIsoId, mBladePressureId, mVelStreamId, m VorStreamId, mProbByPlaneId에 대한 값을 파라미터에 따라 설정함으로써, GIVI에서 현재 디스플레이하고 있는 모든 컨텐 츠에 대한 오브젝트 아이디를 관리할 수 있다.

w GetObjId(value: unsigned char, unsigned char): unsigned short w 파라미터에 따라 mVelIsoId, mVorIsoId, mBladePressureId,

mVelStreamId, mVorStreamId, mProbByPlaneId 중 적절한 값을 리턴함으로써 GIVI에서 오브젝트 컨텐츠를 관리할 때 사 용한다.

w SetProbByPlaneId(value: unsigned short): void w mProbByPlaneId값을 지정된 값으로 설정한다.

w GetProbByPlaneId(value: void): unsigned short

w mProbByPlaneId값을 리턴함으로써 GIVI에서 probe plane을

w mProbByPlaneId값을 리턴함으로써 GIVI에서 probe plane을

관련 문서