Link Search Menu Expand Document

External Task - Component Task (C to Fortran Interface)

(enuSpace for saturn)

본 문서는 External Task를 Component 기반으로 제작하는 방법에 있어 C++과 Fortran의 구조체 변수를 이용한 연동 방에 대하여 설명합니다.

reference sample : https://github.com/EXPNUNI/enuSpace-Article/tree/master/enuSpace%20for%20Saturn/component_task_fortran_x64

Component의 입출력 정보에 대하여 구조체로 C++ 해더에 선언을 수행합니다.

구조체 선언

#pragma once

#include "..\model\model\GlobalHeader.h"
#include <string>
#include <vector>
#include <afxcoll.h>
#include "EnuObj.h"

#ifndef _MODEL_HEADER__
#define _MODEL_HEADER__

extern CPtrArray *enuObject;
extern double g_DT;

#pragma pack(push, 1) 

struct component1 : EnuObject
{
    int iCount;
    double fValue;
    double fArray[5][10];
    component1()
    {
        iCount = 0;
        fValue = 0;
    }
    void Simulation(void);
};
struct component2 : EnuObject
{
    int input;
    int output;
    component2()
    {
        input = 0;
        output = 0;
    }
    void Simulation(void);
};
#pragma pack(pop) 

///////////////////////////////////////////////////////////////////////////
void InitModel();
void TaskModel();
///////////////////////////////////////////////////////////////////////////


#endif

구조체 선언시 EnuObject를 부모로 부터 상속 받습니다.

선언된 구조체를 이용하여 enuSpace와 연동하기 위한 C++ 코드는 아래와 같습니다.

// CoreTask.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "CoreTask.h"

#include "..\model.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CCoreTaskApp

BEGIN_MESSAGE_MAP(CCoreTaskApp, CWinApp)
END_MESSAGE_MAP()


// CCoreTaskApp construction

CCoreTaskApp::CCoreTaskApp()
{
    // TODO: add construction code here,
    // Place all significant initialization in InitInstance
}

// The one and only CCoreTaskApp object

CCoreTaskApp theApp;


// CCoreTaskApp initialization

CString g_strDllPath;
HANDLE g_hConsole = NULL;
CPtrArray *enuObject;
double g_DT;

BOOL CCoreTaskApp::InitInstance()
{
    CWinApp::InitInstance();

    HINSTANCE hInstance = AfxGetInstanceHandle();
    wchar_t szPath[MAX_PATH];
    GetModuleFileName(hInstance, szPath, MAX_PATH);

    wchar_t drive[MAX_PATH];               // 드라이브 명
    wchar_t dir[MAX_PATH];                 // 디렉토리 경로
    wchar_t fname[MAX_PATH];               // 파일명
    wchar_t ext[MAX_PATH];                 // 확장자 명

    _wsplitpath_s(szPath, drive, dir, fname, ext);
    g_strDllPath.Format(L"%s%s", drive, dir);

    if (AllocConsole())
    {
        freopen("CONIN$", "rb", stdin);
        freopen("CONOUT$", "wb", stdout);
        freopen("CONOUT$", "wb", stderr);

        g_hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTitle(L"Fortran");
    }

    return TRUE;
}

int CCoreTaskApp::ExitInstance()
{
    ::FreeConsole();
    return CWinApp::ExitInstance();
}
///////////////////////////////////////////////////////////////////////////////////////////
void PrintMessage(CString strMessage, CString strID = L"");
CString MBToWC(char * str);

///////////////////////////////////////////////////////////////////////////////////////////
extern "C"
{
    void _Message(char* pMessage);

    void TASK_MAIN_COM1(component1* pStruct);
    void TASK_MAIN_COM2(component2* pStruct);
    void TASK_INIT();
    void TASK_LOAD();
    void TASK_UNLOAD();
};
//////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////
// enuSpace interface function pointer.
void(*g_fcbSetValue)(wchar_t*, double) = NULL;
VariableStruct(*g_fcbGetValue)(wchar_t*) = NULL;
void(*g_fcbSetArrayValue)(wchar_t*, void*, int, int) = NULL;
void(*g_fcbSetReShapeArrayValue)(wchar_t*, void*, int, int) = NULL;
void(*g_fcbSetPinInterfaceVariable)(wchar_t*, void*, int, int) = NULL;
VariableStruct(*g_fcbGetArrayValue)(wchar_t*) = NULL;
void(*g_fcbPrintMessage)(wchar_t*, wchar_t*) = NULL;
//////////////////////////////////////////////////////////////////////////////////////////////
// enuSpace interface functions.
extern "C" __declspec(dllexport) void SetCallBack_SetValue(void fcbSetValue(wchar_t*, double));
extern "C" __declspec(dllexport) void SetCallBack_GetValue(VariableStruct fcbGetValue(wchar_t*));
extern "C" __declspec(dllexport) void SetCallBack_SetArrayValue(void fcbSetArrayValue(wchar_t*, void*, int, int));
extern "C" __declspec(dllexport) void SetCallBack_GetArrayValue(VariableStruct fcbGetArrayValue(wchar_t*));
extern "C" __declspec(dllexport) void SetCallBack_SetReShapeArrayValue(void fcbSetReShapeArrayValue(wchar_t*, void*, int, int));
extern "C" __declspec(dllexport) void SetCallBack_SetPinInterfaceVariable(void fcbSetPinInterfaceVariable(wchar_t*, void*, int, int));
extern "C" __declspec(dllexport) void SetCallBack_PrintMessage(void fcbPrintMessage(wchar_t*, wchar_t*));

extern "C" __declspec(dllexport) int GetTaskType();
extern "C" __declspec(dllexport) bool IsEnableTransfer(wchar_t* pMyType,wchar_t* pFromType, wchar_t* pToType);
extern "C" __declspec(dllexport) bool IsTaskStopWhenModify();

extern "C" __declspec(dllexport) bool OnInit();
extern "C" __declspec(dllexport) bool OnLoad();
extern "C" __declspec(dllexport) bool OnUnload();
extern "C" __declspec(dllexport) bool OnTask(__int64 time);
extern "C" __declspec(dllexport) void OnModeChange(int iMode);
extern "C" __declspec(dllexport) void ExecuteFunction(wchar_t* pStrFunction);

extern "C" __declspec(dllexport) void OnEditComponent(wchar_t* pStrSymbolName, wchar_t* pStrID);
extern "C" __declspec(dllexport) void OnShowComponent(wchar_t* pStrSymbolName, wchar_t* pStrID);
extern "C" __declspec(dllexport) bool OnShowHelp(wchar_t* pStrSymbolName);

extern "C" __declspec(dllexport) void OnSetObjectArray(CPtrArray* Object);
//////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////
extern "C" __declspec(dllexport) void SetCallBack_SetValue(void fcbSetValue(wchar_t*, double))
{
    g_fcbSetValue = fcbSetValue;
}

extern "C" __declspec(dllexport) void SetCallBack_GetValue(VariableStruct fcbGetValue(wchar_t*))
{
    g_fcbGetValue = fcbGetValue;
}

extern "C" __declspec(dllexport) void SetCallBack_SetArrayValue(void fcbSetArrayValue(wchar_t*, void*, int, int))
{
    g_fcbSetArrayValue = fcbSetArrayValue;
}

extern "C" __declspec(dllexport) void SetCallBack_GetArrayValue(VariableStruct fcbGetArrayValue(wchar_t*))
{
    g_fcbGetArrayValue = fcbGetArrayValue;
}

extern "C" __declspec(dllexport) void SetCallBack_SetReShapeArrayValue(void fcbSetReShapeArrayValue(wchar_t*, void*, int, int))
{
    g_fcbSetReShapeArrayValue = fcbSetReShapeArrayValue;
}

extern "C" __declspec(dllexport) void SetCallBack_SetPinInterfaceVariable(void fcbSetPinInterfaceVariable(wchar_t*, void*, int, int))
{
    g_fcbSetPinInterfaceVariable = fcbSetPinInterfaceVariable;
}

extern "C" __declspec(dllexport) void SetCallBack_PrintMessage(void fcbPrintMessage(wchar_t*, wchar_t*))
{
    g_fcbPrintMessage = fcbPrintMessage;
}

extern "C" __declspec(dllexport) void OnSetObjectArray(CPtrArray* Object)
{
    if (Object)    enuObject = Object;
}

extern "C" __declspec(dllexport) bool OnLoad()
{
    TASK_LOAD();
    return true;
}

extern "C" __declspec(dllexport) bool OnInit()
{
    try
    {
        if (g_fcbGetValue)
        {
            VariableStruct data;
            data = g_fcbGetValue(L"dt.model");
            if (data.type == DEF_DOUBLE)
                g_DT = *(double*)data.pValue;
        }
        // InitModel();
        TASK_INIT();
        return true;
    }
    catch (...)
    {

    }
    return false;
}

extern "C" __declspec(dllexport) bool OnTask(__int64 time)
{
    try
    {
        if (g_fcbGetValue)
        {
            VariableStruct data;
            data = g_fcbGetValue(L"dt.model");
            if (data.type == DEF_DOUBLE)
                g_DT = *(double*)data.pValue;
        }

        // TaskModel();            // C++ 함수 호출.

        for (int index = 0; index < enuObject->GetSize(); ++index)
        {
            EnuObject *pobj = (EnuObject *)(enuObject->GetAt(index));

            if (wcscmp(pobj->type, L"component1") == 0)
                TASK_MAIN_COM1((component1*)pobj);    // fortran 함수 호출.
            else if (wcscmp(pobj->type, L"component2") == 0)
                TASK_MAIN_COM2((component2*)pobj);    // fortran 함수 호출.
        }

        return true;
    }
    catch (...)
    {

    }
    return false;
}

extern "C" __declspec(dllexport) bool OnUnload()
{
    TASK_UNLOAD();
    return true;
}

extern "C" __declspec(dllexport) void OnEditComponent(wchar_t* pStrSymbolName, wchar_t* pStrID)
{

}

extern "C" __declspec(dllexport) void OnShowComponent(wchar_t* pStrSymbolName, wchar_t* pStrID)
{

}

extern "C" __declspec(dllexport) void OnModeChange(int iMode)
{

}

extern "C" __declspec(dllexport) void ExecuteFunction(wchar_t* pStrFunction)
{

}

extern "C" __declspec(dllexport) bool OnShowHelp(wchar_t* pStrSymbolName)
{
    return true;
}

extern "C" __declspec(dllexport) int GetTaskType()
{
    return TASK_TYPE_OBJECTARRAY;
}

extern "C" __declspec(dllexport) bool IsEnableTransfer(wchar_t* pMyType,wchar_t* pFromType, wchar_t* pToType)
{
    return true;
}

extern "C" __declspec(dllexport) bool IsTaskStopWhenModify()
{
    return true;
}
////////////////////////////////////////////////////////////////////////////
void PrintMessage(CString strMessage, CString strID)
{
    if (g_fcbPrintMessage)
    {
        strMessage = L"model->" + strMessage;
        g_fcbPrintMessage(strMessage.GetBuffer(0), strID.GetBuffer(0));
    }
}

CString MBToWC(char * str)
{
    wchar_t* strResult = new wchar_t[strlen(str) + 1];
    MultiByteToWideChar(CP_ACP, 0, str, (int)strlen(str) + 1, strResult, (int)strlen(str) + 1);
    CString strReturn;
    strReturn = strResult;
    delete[] strResult;
    return strReturn;
}

void _Message(char* pMessage)
{
    PrintMessage(MBToWC(pMessage));
}

태스크의 타입을 지정하는 함수 GetTaskType()에서 리턴값을 TASK_TYPE_OBJECTARRAY로 반환을 수행하면, enuSpace 프로그램은 OnSetObjectArray()함수를 통하여 CPtrArray를 통하여 객체의 리스트 정보를 제공합니다.

OnTask()함는 시뮬레이션 실행시 매 주기 호출되는 함수 입니다.

extern "C" __declspec(dllexport) bool OnTask(__int64 time)
{
    try
    {
        if (g_fcbGetValue)
        {
            VariableStruct data;
            data = g_fcbGetValue(L"dt.model");
            if (data.type == DEF_DOUBLE)
                g_DT = *(double*)data.pValue;
        }

        // TaskModel();            // C++ 함수 호출.

        for (int index = 0; index < enuObject->GetSize(); ++index)
        {
            EnuObject *pobj = (EnuObject *)(enuObject->GetAt(index));

            if (wcscmp(pobj->type, L"component1") == 0)
                TASK_MAIN_COM1((component1*)pobj);    // fortran 함수 호출.
            else if (wcscmp(pobj->type, L"component2") == 0)
                TASK_MAIN_COM2((component2*)pobj);    // fortran 함수 호출.
        }

        return true;
    }
    catch (...)
    {

    }
    return false;
}

전체 오븍젝트의 개수에 대하여 각 타입에 따라서 개별 처리를 수행합니다. enuSpace는 오브젝트의 컴포넌트의 타입에 따라서 type정보를 제공합니다. 자신의 타입에 맞는 형으로 변환하여 컴포넌트의 메인 함수를 호출합니다.

Fortran 연동

fortran 코드와 연동하기 위해서 extern 함수를 선언합니다.

///////////////////////////////////////////////////////////////////////////////////////////
extern "C"
{
    void _Message(char* pMessage);

    void TASK_MAIN_COM1(component1* pStruct);
    void TASK_MAIN_COM2(component2* pStruct);
    void TASK_INIT();
    void TASK_LOAD();
    void TASK_UNLOAD();
};
//////////////////////////////////////////////////////////////////////////////////////////////

Fortran Code

MODULE Interface_to_C_Func

   INTERFACE

      SUBROUTINE Message(string) BIND(C, NAME="_Message")
         USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_INT, C_CHAR, C_PTR, C_DOUBLE
         !.. Return value

         !.. Argument list
         CHARACTER(KIND=C_CHAR)     :: string(*)     
      END SUBROUTINE Message 

   END INTERFACE

END MODULE Interface_to_C_Func

MODULE IO_DATA
    USE, INTRINSIC :: ISO_C_BINDING
    IMPLICIT NONE

    TYPE :: COMPONENT1
          SEQUENCE
          REAL(C_DOUBLE) :: DUMMY(18)   ! EnuObject Dummy
          INTEGER(C_INT) :: ICOUNT
          REAL(C_DOUBLE) :: FVALUE
          REAL(C_DOUBLE) :: FARRAY(10,5)
    END TYPE COMPONENT1

    TYPE :: COMPONENT2
          SEQUENCE
          REAL(C_DOUBLE) :: DUMMY(18)   ! EnuObject Dummy
          INTEGER(C_INT) :: INPUT
          INTEGER(C_INT) :: OUTPUT
    END TYPE COMPONENT2

END MODULE IO_DATA


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    SUBROUTINE TASK_MAIN_COM1(INPUT) BIND(C,NAME='TASK_MAIN_COM1')

        USE Interface_to_C_Func
        USE IO_DATA

        TYPE(COMPONENT1) INPUT

        INPUT.ICOUNT = INPUT.ICOUNT + 1
        INPUT.FVALUE = INPUT.FVALUE + 0.1
        INPUT.FARRAY(1,1) = INPUT.FARRAY(1,1) + 0.1

        CALL Message('Call Main Proc-Component1')      

        RETURN
    END

    SUBROUTINE TASK_MAIN_COM2(INPUT) BIND(C,NAME='TASK_MAIN_COM2')

        USE Interface_to_C_Func
        USE IO_DATA

        TYPE(COMPONENT2) INPUT

        INPUT.INPUT = INPUT.INPUT + 1
        INPUT.OUTPUT = INPUT.INPUT

        CALL Message('Call Main Proc-Component2')      

        RETURN
    END    

    SUBROUTINE TASK_INIT
        ! CALL INIT_PROC()
        RETURN
   END  

    SUBROUTINE TASK_LOAD
        ! CALL LOAD_PROC()
        RETURN
    END      

    SUBROUTINE TASK_UNLOAD
        ! CALL UNLOAD_PROC()
        RETURN
    END

C++ 코드에서 호출한 TASK_MAIN_COM1((component1*)pobj) 함수에서 구조체의 주소값을 전달을 수행한다.

Fortran 코드에서 동일한 구조체를 선언하고, 선언된 구조체를 입력으로 받는다.

Fortran에서 구조체 선언 (C++에서 선언된 동일한 메모리 사이즈를 이용하여 적용)

MODULE IO_DATA
    USE, INTRINSIC :: ISO_C_BINDING
    IMPLICIT NONE

    TYPE :: COMPONENT1
          SEQUENCE
          REAL(C_DOUBLE) :: DUMMY(18)   ! EnuObject Dummy
          INTEGER(C_INT) :: ICOUNT
          REAL(C_DOUBLE) :: FVALUE
          REAL(C_DOUBLE) :: FARRAY(10,5)
    END TYPE COMPONENT1

    TYPE :: COMPONENT2
          SEQUENCE
          REAL(C_DOUBLE) :: DUMMY(18)   ! EnuObject Dummy
          INTEGER(C_INT) :: INPUT
          INTEGER(C_INT) :: OUTPUT
    END TYPE COMPONENT2

END MODULE IO_DATA

REAL(C_DOUBLE) :: DUMMY(18) ! EnuObject Dummy 선언은 EnuObject로 부터 상속받은 구간에 대하여 더미 영역으로 할당한 내용이다.

입력받은 구조체의 정보를 이용하여 계산을 수행한다.

계산된 결과는 enuSpace의 심볼테이블 다이얼로그를 통하여 확인 및 값을 변경할 수 있다.