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을