eDICOMViewer

ref를 활용한 void* 연동

2월 7, 2018 eDICOMViewer No comments

DcmFileFormat을 dll안에서 memory alloc후 javascript에서는 handle 값을 이용한 함수 사용

1. NodeDCMTK.dll에 OpenDcmFileFormat 함수 추가

int OpenDcmFileFormat(char* fileName, void** dcmptr)

fileName을 char*로 전달하고,
DcmFileFormat을 pointer로 alloc후 해당 file load후 handle을 2번째 void** dcmptr로 전달 구현

int OpenDcmFileFormat(char* fileName, void** dcmptr)
{
    DcmFileFormat *pDcmfileFormat = new DcmFileFormat();
    OFCondition status = pDcmfileFormat->loadFile(fileName);
    if (!status.good())
        return 0;

    *dcmptr = pDcmfileFormat;
    return 1;
}

2. NodeDCMTK.js javascript 파일에서 OpenDcmFileFormat 함수 선언 추가

var nodeDCMTK = ffi.Library('NodeDCMTK.dll', {
...
'OpenDcmFileFormat': ['int',['string',DcmFileFormatPtrPtr]]
});

2번째 parameter가 void**이기에 DcmFileFormatPtrPtr 형태로 선언

var DcmFileFormat = ref.types.void 
var DcmFileFormatPtr = ref.refType(DcmFileFormat);
var DcmFileFormatPtrPtr = ref.refType(DcmFileFormatPtr);

3.index.js에서 OpenDcmFileFormat을 DcmFileFormatPtrPtr으로 alloc된 변수를 전달
추후 해당 handle을 사용시는deref()를 이용해 해당 void*로 전달 사용

//call OpenDcmFileFormat
var DcmFileFormat = ref.types.void 
var DcmFileFormatPtr = ref.refType(DcmFileFormat);
var DcmFileFormatPtrPtr = ref.refType(DcmFileFormatPtr);
var dcmFileFormat = ref.alloc(DcmFileFormatPtrPtr);

var isSuccess = nodeDCMTK.OpenDcmFileFormat(fileName, dcmFileFormat);
console.log("OpenDcmFileFormat::return=" + isSuccess.toString());
nodeDCMTK.test_voidptr_paramter(dcmFileFormat.deref());

dcmtk library sample 연동

1월 31, 2018 eDICOMViewer No comments

NodeDCMTK.dll에 dcmtk library가 정상 load되는지 확인

0001.dcm DICOM file을 load하여 patient name을 read후 OutputDebugString()으로 출력 테스트

1.NodeDCMTK.dll에서 test_dcm()함수에 0001.DCM File read read 추가

DcmFileFormat dcmfileFormat;
OFCondition status = dcmfileFormat.loadFile("..\\etc\\sampleDICOM\\0001.DCM");
if (status.good())
{
...
}

2.정상 load되었다면 patient name가져온후 OutpuDebugString 출력

OFString patientName;
if (dcmfileFormat.getDataset()->findAndGetOFString(DCM_PatientName, patientName).good())
{
     char buffer[256];
     sprintf(buffer, "Patient:%s", patientName);
     OutputDebugStringA(buffer);
     return a * b;
}

3.NodeDCMTK project 속성 중 link library path에 ..\dll\DCMTK\lib 추가

4.library에 dcmdata.lib, oflog.lib, ofstd.lib, iphlpapi.lib, ws2_32.lib, netapi32.lib, wsock32.lib 추가

5.NodeDCMTK project 속성 중 include path에 ..\dll\DCMTK\include 추가

6.include header는 아래 처럼 추가

#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dctagkey.h"
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h"

7.build후 electron실행 output debug string은 log2console로 확인

8.추가로 dcm의 모든 element를 read

DcmFileFormat dcmfileFormat;
OFCondition status = dcmfileFormat.loadFile("..\\etc\\sampleDICOM\\0001.DCM");
if (status.good())
{
	for (long i = 0; i < dcmfileFormat.getDataset()->card(); i++)
	{
		DcmElement* pElement = dcmfileFormat.getDataset()->getElement(i);
		if (pElement == NULL)
			continue;

		OFString value;
		status = pElement->getOFString(value, 0);
		if (!status.good())
			continue;

		char buffer[256];
		sprintf(buffer, "[%04X:%04X] %s : %s", pElement->getGTag(), pElement->getETag(), ((DcmTag&)pElement->getTag()).getTagName(), value);
		OutputDebugStringA(buffer);
	}
}

9.결과 화면

NodeDCMTK library를 별도 .js로 export 하기

1월 30, 2018 eDICOMViewer No comments

DCMTK를 wrapping한 NodeDCMTK library용 javascript 만들기

1.nodeDCMTK.js 파일 생성

var oldPath = process.env.PATH;
///현재 path 기준은 electron.exe
var dllPath = '.\\dll';
process.env['PATH'] = `${process.env.PATH};${dllPath}`;
// binding to a nodeDCMTK functions...
var nodeDCMTK = ffi.Library('NodeDCMTK.dll', {
'test_dcmtk': [ 'int', [ 'int', 'int' ]]
});
process.env['PATH'] = oldPath;

2. nodeDCMTK.js파일 하단에 nodeDCMTK 을 export default 선언

...
export default nodeDCMTK;

3. 사용하고자 하는 index.js상단에 js import 추가 후 사용

import nodeDCMTK from './nodeDCMTK.js';

//call test_dcmtk
var result = nodeDCMTK.test_dcmtk(3,4);
console.log("3+4=" + result);

node-ffi로 test용 dll 함수 call 시도

1월 29, 2018 eDICOMViewer No comments

1.NodeDCMTK.dll project에 Test용 함수 추가

///header file
int test_dcmtk(int a, int b);

///cpp file
int test_dcmtk(int a, int b)
{
    return a + b;
}

2. ffi 사용을 위해 webpack이 수행되는 index.js에는 require(‘ffi’)를 추가 할 수 없기에 index.html에 ffi, ref를 미리 선언

<script type="text/javascript">
        var ffi = require('./JS/node_modules/ffi');
        var ref = require('./JS/node_modules/ref');
</script>
<script type="text/javascript" src="build/bundle.js"></script>

3.index.html에 ffi를 사용하여 nodeDCMTK 함수 선언

// binding to a nodeDCMTK functions...
var nodeDCMTK = ffi.Library('E:\\work\\DICOMViewer\\NodeDCMTK\\Release\\NodeDCMTK.dll', {
'test_dcmtk': [ 'int', [ 'int', 'int' ]]
});

4.해당 nodeDCMTK를 이용하여 함수 call

var result = nodeDCMTK.test_dcmtk(3,4);
console.log(result);

5.결과화면

6.추가로 dll path를 손쉽게 접근 하기 위해 environment의 PATH에 원하는 PATH를 추가 후 다시 원상 복구
설정하고자 하는 PATH는 electron.exe가 실행되는 위차가 기준

var oldPath = process.env.PATH;
///현재 path 기준은 electron.exe
var dllPath = '..\\Release';
process.env['PATH'] = `${process.env.PATH};${dllPath}`;
// binding to a nodeDCMTK functions...
var nodeDCMTK = ffi.Library('NodeDCMTK.dll', {
'test_dcmtk': [ 'int', [ 'int', 'int' ]]
});
process.env['PATH'] = oldPath;

node-ffi설치

1월 25, 2018 eDICOMViewer No comments

node-ffi를 사용한 dll 연동 을 위한 node-ffi설치

electron에서 dll 연동을 위해 node-ffi를 사용할 예정

먼저 npm에서 node-ffi인 ffi를 설치 수행시 error 발생

$npm install -s ffi
...\node_modules\ref>if not defined npm_config_node_gyp 
(node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js"
 rebuild )  else (node "" rebuild )

ffi를 사용하기 위해 node-gyp + window build tools 필요해서 발생

1.node-gyp를 global로 설치

$npm install -g node-gyp

2. window build tools를 global로 설치

$npm install --g windows-build-tools
//자동으로 python2.7 설치
Downloading BuildTools_Full.exe
Downloading python-2.7.13.msi
Starting installation...

6. ffi 설치시 설치 중 오류 발생

$npm install -s ffi
...
if not defined npm_config_node_gyp (node "C:\Users\scanhand\AppData\Roaming\npm\node_modules\npm\node_modules\npm-lifecycle\node-gyp-bin\....\node_modules\node-gyp\bin\node-gyp.js" rebuild ) else (node "C:\Users\scanhand\AppData\Roaming\npm\node_modules\npm\node_modules\node-gyp\bin\node-gyp.js" rebuild )
...
Building assembly file ......\deps\libffi\src\x86\win64.asm
Assembling: ......\deps\libffi\src\x86\win64.asm
prep_cif.c
types.c
raw_api.c
java_raw_api.c
closures.c
ffi.c
win_delay_load_hook.cc
ffi.vcxproj -> E:\work\DICOMViewer\electron\resources\app\JS\node_modules\ffi\build\Release
\libffi.lib
ffi.cc
callback_info.cc
threaded_callback_invokation.cc
win32-dlfcn.cc
win_delay_load_hook.cc
..\src\ffi.cc(111): error C2664: 'v8::Maybe v8::Object::ForceSet(v8::Localv8::Context
,v8::Localv8::Value,v8::Localv8::Value,v8::PropertyAttribute)': cannot convert argument 3
from 'v8::PropertyAttribute' to 'v8::Localv8::Value' [E:\work\DICOMViewer\electron\resourc
es\app\JS\node_modules\ffi\build\ffi_bindings.vcxproj]
..\src\ffi.cc(111): note: No user-defined-conversion operator available that can perform th
is conversion, or the operator cannot be called
...

node-ffi가 node v9.3.0를 아직 지원하지 않아 발생 되는듯 함

  • 쉬운 해결책으로는 node를 v8.9.4 Version으로 재설치
  • node-ffi의 github(https://github.com/node-ffi/node-ffi) 에서 manual install 시도

    1.먼저 github에서 clone 후 npm 설치 수행

    $npm install
    

    2.node-gyp와 build tool은 이미 설치 한 상태 이기에 node-gyp rebuild수행
    # 성공적으로 build 됨

    $node-gyp rebuild
    

    3. npm을 manual install 하기위해 npm형태로 package 수행
    # 자동으로 package name과 version정보이름으로 .tgz형태로 저장 됨

    $npm pack
    ffi-2.2.0.tgz
    

    4.해당 package파일을 원하는 위치로 copy후 manual install 수행

    $npm i -s ffi-2.2.0.tgz
    

    5. 설치 완료

    6. webpack 수행시 warning 발생

    Warning : Critical ....
    

    7. index.js source에 require(‘ffi’) 수행시 아직도 error발생

    8. webpack으로 bundle 수행시 정상적으로 bundle이 되지 않아 error 발생

    9. main page인 index.html page에 javascript source를 직접 삽입 시도

    <script type="text/javascript>
    var ffi = require('./JS/node_modules/ffi');
    </script>
    

    10. npm start를 통해 electron 수행시 error 발생
    Error: A dynamic link library (DLL) initialization routine failed.

    11.modules를 electron-rebuild를 통해 다시 rebuild를 수행 하라고 한다.
    먼저 electron-rebuild를 설치 후

    npm install --save-dev electron-rebuild
    

    12.electron-rebuild 수행. ffi module을 자동으로 rebuild수행 됨

    ./node_modules/.bin/electron-rebuild
    \ Building module: ffi, Completed: 0
    

    13.다시 재 수행시 정상 실행 됨

    중요) 결론적으로
    1. Node를 8.X.X로 설치하고
    2. node-gyp및 window build 설치 하고
    3. node-ffi를 webpack으로 사용하지 않고
    4. electron-rebuild를 통해 rebuild수행 하면 정상 작동

    file dialog 추가

    1월 24, 2018 eDICOMViewer No comments

    1.electron의 dialog를 사용하여 file open 기능 추가
    여기서 dialog사용시 renderer 에서 dialog를 사용하기에
    반드시 remote로 활용

    const {dialog} = require('electron').remote
    

    2.properties를 통해 openFile명시, filters를 통해 file extension 선택 추가

    files = dialog.showOpenDialog({
        properties: ['openFile'],
        filters: [
            {name: 'DICOM', extensions: ['dcm', 'dic']},
            {name: 'All Files', extensions: ['*']}
            ]
    }); 
    

    3.return 되는 files를 통해 선택된 파일 확인, null이면 cancel판단

    var files=[];
    files = dialog...
    if(files == null)
        return;
    
    console.log(files)
    

    electron page reload

    1월 24, 2018 eDICOMViewer No comments

    # 기존 menu에 debugging목적으로 menu에 reload 추가

    
    const {remote} = require('electron');
    ...
         //for debugging
         label: 'reload',
         click: ()=>{
                remote.getCurrentWindow().reload();
         }
    ...
    

    electron의 remote를 활용
    현재 currentWindow()의 reload를 call하여 현재 page reload수행

    개발 작업 중 간단한 수정 작업 후 결과 확인시 reload menu를 이용하여 쉽게 사용

    menu 추가

    1월 24, 2018 eDICOMViewer No comments

    1. electron을 사용하기 위해 npm을 통해 electron 설치

    $ npm install -s electron
    

    2.menu를 생성할 js파일 신규 생성(menu.js) 및 menu.js에서 electron을 사용하여 menu 생성

    const {remote} = require('electron');
    const {Menu, MenuItem} = remote;
    
    const menu = new Menu();
    menu.append(new MenuItem({
        label:'File', 
        submenu: [
            {
                label: 'Open File...',
                click: ()=>{
                    console.log("Click Open File.");
                }
            }
        ]
    }));
    
    menu.append(new MenuItem({
        label:'About',
        submenu: [
            {
                label: 'About DICOM Viewer...',
                click: ()=>{
                    console.log("Click About DICOM Viewer.");
                }
            }
        ]
    }));
    
    Menu.setApplicationMenu(menu);
    

    3.index.js에서 신규 추가된 menu.js import 추가

    import menu from './menu.js';
    

    3.webpack 수행시 아래와 같은 error발생

    PS E:\work\DICOMViewer\electron\resources\app\JS> webpack
    Hash: a1afdb75372191d5b939
    Version: webpack 3.10.0
    Time: 1677ms
        Asset    Size  Chunks                    Chunk Names
    bundle.js  685 kB       0  [emitted]  [big]  main
       [1] ./index.js 444 bytes {0} [built]
       [4] (webpack)/buildin/global.js 509 bytes {0} [built]
        + 11 hidden modules
    
    ERROR in ./node_modules/electron/index.js
    Module not found: Error: Can't resolve 'fs' in 'E:\work\DICOMViewer\electron\resources\app\JS\node_modules\electron'
     @ ./node_modules/electron/index.js 1:9-22
     @ ./index.js
    PS E:\work\DICOMViewer\electron\resources\app\JS>
    

    4. webpack.config.json에서 targer추가

    module.exports = {
        target: 'electron-renderer',
        entry: './index.js',
    ...
    

    5. webpack수행시 정상 수행 완료.

    bootsrap 추가

    1월 18, 2018 eDICOMViewer No comments

    1. index.html 에서 추가시에는 bootstrap.css, jquery.js, bootstrap.js 파일을 직접 설정

    <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.css"> 
    <script type="text/javascript" src="../node_modules/jquery/dist/jquery.js"></script>
    <script type="text/javascript" src="../node_modules/bootstrap/dist/js/bootstrap.js"></script>
    

    2. javascript에서는 import 를 통해 추가

    import $ from "jquery";
    import "bootstrap";
    

    3.bootstrap css 추가

    import "bootstrap/dist/css/bootstrap.css";
    

    하지만 webpack 수행시 아래와 같이 error발생

    ERROR in ./node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf
    ...
    ERROR in ./node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
    ...
    ERROR in ./node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff
    ...
    ERROR in ./node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.eot
    ...
    ERROR in ./node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.svg
    ...
    

    4.webpack.config.json에서 error의 확장자 파일에 대한 loader설정이 없어서 발생, 관련 loader 추가
    추가 설치 및 설정 loader : file-loader

    4-1. 우선 file-loader 설치
    $npm install -s file-loader

    4-2. webpack.config.json파일에 loader추가

    ...
    loaders: [
    ...
    { test: /\.html$/, loader: 'raw' },
    { test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/, loader: 'file-loader' }
    ...
    

    4-3. webpack 수행시 기존 woff2, ttf, svg파일이 아래 형태로 변환 후 bundle과 동일 folder에 write됨

    ...
    448c34a56d699c29117adc64c43affeb.woff2
    89889688147bd7575d6327160d64e760.svg
    e18bbf611f2a2e43afc071aa2f4e1512.ttf
    ...
    

    5.npm start를 통해 electron 실행 결과

    1) 추가적으로 bootstrap version 4.0.0인 경우 아래와 같이 설치 후
    $ npm install -save bootstrap@4.0.0-beta.3

    2) webpack 수행시 popper.js 관련 error 발생

    3) webpack.config.json에 popper plugin 추가

    plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                jQuery: 'jquery',
                'window.jQuery': 'jquery',
                'Popper': 'popper.js'
            }),
        ]
    

    4) bootstrap version 4.0.0사용시 webpack수행시 생성되던 부가적인 파일 사라짐…ㅡㅡ;;
    굳이 위에 loader추가 불필요 할듯 함.

    jquery import 추가 후 #id 변경

    1월 17, 2018 eDICOMViewer No comments

    1. jquery npm으로 설치

    $ npm install -s jquery
    

    2.index.html에 test 용 #id 추가

    <div id="JQTest">Test</div>
    

    3.index.js에 “Test”를 “Index.js First Write!”로 변경

    $("#JQTest").html("<h1>Index.js First Write!</h1>");
    

    4.bootstrap 사용을 위해 npm설치 후 아래와 같이 추가

    import $ from "jquery";
    import boots from "bootstrap";
    

    5.하지만 JQuery를 찾을 수 없다는 error발생

    Uncaught ReferenceError: jQuery is not defined
        at Object.dismiss (bundle.js:10420)
        at __webpack_require__ (bundle.js:20)
        at Object.$.fn.emulateTransitionEnd.called (bundle.js:10345)
        at __webpack_require__ (bundle.js:20)
        at Object.<anonymous> (bundle.js:74)
        at __webpack_require__ (bundle.js:20)
        at Object.defineProperty.value (bundle.js:63)
        at bundle.js:66
    

    6.해결책
    webpack.config.json 파일에 webpack plugin 설정 추가

    var path = require('path');
    var webpack = require('webpack');
    
    ...
    
    module.exports = {
    ...
     plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                jQuery: 'jquery',
                'window.jQuery': 'jquery'
            }),
        ]
    }