RESTful API - getvalue

특정변수에 대하여 값을 요청

--------------------------------------------

Description

enuSpace 서버측에 데이터베이스의 변수값을 요청하는 API

--------------------------------------------

Request

HTTP Method : POST

URI : http://localhost:8080/getvalue?devicekey=043DFEDEFD&variable=A0

URI : http://localhost:8080/getvalue?tagid=@043DFEDEFD.A0

URI : http://localhost:8080/getvalue?page=main.svg&variable=ID_LOGIC.A0

Query Parameters

        devicekey : device name

        variable : device variable

        or

        tagid : database tagid

        or

        page : picture name
        variable : variable name

Example : ?devicekey=043DFEDEFD&variable=A0

Example : ?tagid=@043DFEDEFD.A0

Example : ?page=main.svg&variable=ID_LOGIC.A0

Content-Type : application/json; charset=UTF-8

--------------------------------------------

Response

Body

json file format

Body Example

{

“RESULT”:”OK”,

“RESULT_CODE”:”RESULT_OK”,

“DEVICE_KEY":”043DFEDEFD”,

“VARIABLE":”A0”,

"TAGID":"043DFEDEFD.A0",

"TYPE":"int",

"VALUE":"50"

}

--------------------------------------------

Sample Call

JavaScript

function getvalue()

{

var devicekey= document.getElementById("devicekey").value;

var variable = document.getElementById("variable").value;

var xmlHttp = new XMLHttpRequest();

var strUrl = "getvalue" ;

var strParam= "devicekey="+devicekey + "&" + "variable="+ variable;  


xmlHttp.onreadystatechange=function()

{

if (xmlHttp.readyState==4 && xmlHttp.status==200)

{    

    var msg = xmlHttp.responseText;

var arr = JSON.parse(msg);

if (arr.RESULT == "OK")

{

alert(arr.VALUE);

}

else

{

if (arr.RESULT_CODE == "CODE_VARIABLE_NOUT_FOUND" )

{

alert(" 등록된 디바이스의 변수를  검색하지 못하였습니다.");

}

if (arr.RESULT_CODE == "CODE_UNKNOWN_DATATYPE" )

{

alert("알수없는 데이터 타입니다..");

}

}

}

};

xmlHttp.open("POST",strUrl,true);

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");

xmlHttp.setRequestHeader("Cache-Control","no-cache, must-revalidate");

xmlHttp.setRequestHeader("Pragma","no-cache");

xmlHttp.send(strParam);

}


RESTful API - requestpage

서버에 대한 svg 픽쳐 파일 요청

--------------------------------------------

Description

enuSpace 서버측에 화면구성된 svg 파일을 웹 브라우져에서 현시하기 위하여 픽쳐파일을  요청하는 API

--------------------------------------------

Request

HTTP Method : POST

URI : http://localhost:8080/requestpage?page=picture.svg

Query Parameters

        page : picture file name

Example : ?page=picture.svg

Content-Type : text/html; charset=UTF-8

--------------------------------------------

Response

Body

    svg file contents

    or

{“RESULT”:”FAIL”,

“RESULT_CODE”:”CODE_INVALID_PARAMETER”,

“MESSAGE":”Invalid requestpage parameter”}

Body Example

<?xml version="1.0" encoding="UTF-16"?>

<svg

stroke="rgb(0,0,0)"

stroke-opacity="1.00"

stroke-width="1.00"

transform="translate(0.00,0.00) rotate(0.00) scale(1.0000, 1.0000)"

pg-xcenter="0.00"

pg-ycenter="0.00"

style="stroke:rgb(127,127,127);stroke-opacity:1.00;stroke-width:2.00;stroke-dasharray:1,1,1;background-color:rgb(61,61,59);"

xmlns="http://www.w3.org/2000/svg"

xmlns:xlink="http://www.w3.org/1999/xlink"

width="1920"

height="1080"

>

<rect

id="ID_1ct3IF0"

stroke="rgb(0,119,189)"

stroke-opacity="0.00"

stroke-width="2.00"

transform="translate(-8.43,-4.31) rotate(0.00) scale(1.0000, 1.0000)"

pg-xcenter="0.00"

pg-ycenter="0.00"

stroke-linecap="butt"

  stroke-linejoin="miter"

  x="0.00"

y="-4.15"

width="1942.11"

height="1100.24"

rx="0.00"

ry="0.00"

fill="url(#ID_1ct3IF0_GRAD)"

fill-opacity="1.00"

>

</rect>

</svg> 

--------------------------------------------

Sample Call

JavaScript

function requestpage(var picture) 

{

var xmlHttp = new XMLHttpRequest();

var strUrl = "requestpage" ;

var strParam= "page="+picture;  

xmlHttp.onreadystatechange=function()

{

if (xmlHttp.readyState==4 && xmlHttp.status==200)

   {    

    var msg = xmlHttp.responseText;

var arr = JSON.parse(msg);

if (arr.RESULT == "FAIL")

{

location = "http://localhost:8080/fail.html";

}

else

{

// data processing

}

   }

};

xmlHttp.open("POST",strUrl,true);

xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");

xmlHttp.setRequestHeader("Cache-Control","no-cache, must-revalidate");

xmlHttp.setRequestHeader("Pragma","no-cache");

xmlHttp.send(strParam);

}


enuSpace Installation Guide


1. enuSpace 프로그램 설치 (enuSpace for mars - student version)


download link : http://enuspace.tistory.com/entry/enuSpace-for-Mars-2017

다운로드 링크 페이지에서 최신 파일을 다운로드 합니다.

다운로드 파일을 더블클릭하여 설치를 수행합니다.

바탕화면에 두개의 단축키가 생성됩니다.

enuSpace 아이콘을 더블클릭하여 실행합니다.


더블클릭시 회원가입 및 로그인을 수행하여 프로그램을 활성화합니다.

처음 사용자분은 Sign in 버튼을 클릭하여 회원가입을 수행합니다.


2. enuSpace IoT 프로그램 설치 (Windows 10 iot core용 응용프로그램)


Windows 10 IoT Core가 설치된 Raspberry PI에 본 응용프로그램을 설치하며, 아래 그림과 같이 연계됩니다. enuSpace 플랫폼에서 디바이스의 모니터링 및 제어로직을 구성할수 있습니다. 

download link : http://enuspace.tistory.com/entry/enuSpace-for-Mars-2017

Windows 10 Core App : enuSpace_IoT (enuSpace_IoT_1.0.3.0_x86_x64_arm.appxbundle)


 Windows 10 Core에 다운로드한 응용프로그램을 설치합니다.

설정해상도는 1920 *1080으로 설정하여야 정상적인 디스플레이가 가능합니다.

App package 에서 다운로드한 파일을 선택합니다.

Go 버튼을 클릭하여 설치를 수행합니다.

설치를 완료합니다.


3. Arduino에 StandardFirmata 프로그램 설치.

아두이노 디바이스에 아두이노 IDE에서 제공하는 StandardFirmata 프로그램을 설치합니다. 

아두이노 IDE : https://www.arduino.cc/en/Main/Software

라즈베리 파이(Raspberry PI)와 아두이노(Arduino)간에는 StandardFirmata를 이용하여 통신을 수행합니다.


4. 적용 

  4.1 enuSpace 프로그램을 기동합니다. enuSpace introduce를 실행후 웹서버를 기동합니다.


  4.2 라즈베리 파이에 설치한 enuSpace iot 응용프로그램을 실행합니다.

사용자 로그인 및 웹서버 주소 설정 과정

신규 생성한 프로젝트에 사용자를 등록하는 과정입니다. 프로젝트에 설정된 사용자는 로그인을 통하여 enuSpace에 로그인을 수행합니다.

디바이스 등록

신규 디바이스를 등록하는 경우에는 관련 디바이스의 정보와 변수 정보를 enuSpace에 등록하는 과정입니다. 디바이스 등록 버튼을 클릭합니다.

클릭후 enuSpace응용 프로그램의 디바이스 리스트와 데이터베이스 TAG 정보를 보시면, 등록된 내용을 확인할 수 있습니다. 

시리얼 포트 리스트에서 아두이노와 연계된 시리얼 포트를 선택후 연결 버튼을 클릭하여 연결 수행

연결이 정상적으로 수행이 되면, 아두이노의 센서 데이터값이 현시되며, enuSpace에 등록된 서버에 전송을 수행하여 서버 사이드에서 데이터베이스화 되어 저장됩니다.


4.3 enuSpace introduce 활용 예시

enuSpace introduce에서 제공되는 picture의 iot_family.svg 아이템을 선택합니다. 

좌측 상단의 아두이노 심볼 아이콘 상단에 노란색 원을 선택후 속성창의 Event Widnow를 선택하여 ontask의 함수 일부를 주석처리합니다. 본 로직은 시뮬레이션용으로 임의의 데이터를 생성하기 위한 로직에 대하여 주석을 처리합니다.

아두이노 디바이스 심볼 객체를 선택하고 속성창의 interface 설정값을 true로 설정하고, interface-id 항목에 #+디바이스 이름을 입력합니다.

리본 메뉴에서 Script Operation 버튼을 Run 상태에서 실제 동작되는 화면을 확인하실수 있습니다.


웹 브라우져 버튼을 클릭하거나, 웹브라우져 (Chrome 최적화)에서 아래 그림과 같이 현시됨을 확인하실수 있습니다.


아래 링크 사이트에서 보다 자세한 설명을 보실수 있습니다.

참고 : http://enuspace.tistory.com/entry/enuSpace-for-Mars2017-%EC%A3%BC%EC%9A%94%EA%B8%B0%EB%8A%A5-%EC%86%8C%EA%B0%9C








enuSpace for mars(2017)에서 확장용 web 모듈 사용하기


enuSpace for mars(2017)버젼부터 확장용 web 모듈을 추가하여 개발할 수 있는 기능을 제공합니다. 본 내용은 개발자에게 web 모듈 확장 프로젝트 사용 방법에 대하여 설명합니다.

확장 웹모듈 인터페이스 구조도

동작 방법 : enuSpace가 설치된 동일한 디렉토리에 제공되는 샘플용 프로젝트(WebExtension.sln)를 컴파일하여 WebExtension.dll 생성, enuSpace의 실행 프로그램 위치에 복사하여 붙여넣기를 수행합니다. 

enuSpace 프로그램을 실행하고 기존의 프로젝트를 로드하거나 신규 프로젝트를 생성후 웹 서버 시작 버튼을 클릭하면 enuSpace프로그램에서 확장 WebExtension.dll 파일을 로드합니다.  

사용 방법 : 기본 제공되는 RASTful API 이외의 함수에 대해서는 WebExtension.dll의 Requst함수에 의하여 처리되므로, 개발자가 클라이언트에서 전달된 데이터의 데이터를 전달받아 요청된 명령에 따른 처리를 수행하면 됩니다. 

아래 기본 샘플은 사용자가 웹서버를 통하여 전달받은 데이터를 확장 모듈에서 ECHO 데이터를 반환하는 샘플이다. 

void GetParamValue(CString strInput, CString *strAttr, CString *strParam)

{

int ipos = strInput.Find('=');

if (ipos > 0)

{

CString strElement;

*strAttr = strInput.Left(ipos);

*strParam = strInput.Right(strInput.GetLength() - ipos - 1);

}

}

char* WCToMB(CString str)

{

char * temp = new char[_tcslen(str) * 2 + 1];

WideCharToMultiByte(CP_ACP, 0, str, (int)_tcslen(str) + 1, temp, (int)_tcslen(str) * 2 + 1, 0, 0);

return temp;

}

extern "C" __declspec(dllexport) void Request(WebSocketObject* pSocketObject, wchar_t* funtionname, wchar_t* contents)

{

// SAMPLE CODE

CString strParam = contents;

CString Seperator = _T("&");

int charpos = 0;

CString token;

token = strParam.Tokenize(Seperator, charpos);


// 개별 입력 속성및 값을 취득하는 SAMPLE

while (token != L"")

{

token.Trim();

CString attribute; // attribute값 취득

CString value; // value값 취득

GetParamValue(token, &attribute, &value);

token = strParam.Tokenize(Seperator, charpos);

}


// 반환값 코드.

CString strResult;

strResult.Format(L"RESULT:%s, %s" ,funtionname, contents);

char* pstr = WCToMB(strResult.GetBuffer(0));


pSocketObject->response.sendlen = strlen(pstr);

pSocketObject->response.pSendBuffer = pstr;

pSocketObject->response.connent_type = L"application/json; charset=UTF-8";

return;

클라이언트 웹 브라우져에서 주소줄에 다음과 같이 입력하였을 경우, http://127.0.0.1:8080/myfunction?dfdf=444&dfefe=999

아래 그림과 같은 결과를 획득할 수 있다. 

주) 반환값을 전달할 때 char*의 메모리 값을 동적으로 생성하였으며, 별도의 메모리 해제를 수행하지 말아야 한다. enuSpace 웹 서버모듈에서 처리가 완료되면, 메모리를 자동으로 해제를 수행하기 때문이다. 


SVG와 JavaScript를 이용한 다이나믹 심볼 제작 (온도 Gauge)


라이브러리 생성

엔유스페이스를 실행하고 Project Explorer에서 Library하위에서 Logic선택후 Add New logic Library를 선택합니다. 라이브러리 파일이름을 입력후 Create를 수행하면, 새로운 로직파일이 생성됩니다.

생성된 파일을 선택후 팝업메뉴를 이용하여 Add Node를 통하여 새로운 심볼을 생성합니다. 생성된 심볼을 디자인합니다. 디자인 객체를 이용하여 아래 그림과 같이 디자인을 수행합니다. 

각각의 객체의 속성에 id를 위 그림과 같이 설정합니다. 핀객체에 대해서는 name의 속성값을 설정합니다. 핀객체는 로직블럭과 로직블럭간의 연결선을 이용하여 연결이 가능합니다. 연결선을 이용하여 핀객체 연결시 데이터 전송 역할을 수행합니다.


심볼의 자체에 이벤트 함수를 등록합니다. _ontask()함수에 아래의 코드를 추가합니다. 입력핀의 객체 변수값을 출력핀의 객체 변수값으로 할당하는 코드입니다.

function _ontask()

--TODO Add your lua script code here

output = input

end

심볼에 입력데이터의 min, max를 정의하는 변수를 추가합니다. 

float min = 0

float max = 100

다음으로 ID_GAGUE객체를 선택하고 이벤트함수 _ontaskview()함수를 추가합니다. 이때 추가할 스크립트는 JavaScript를 이용하여 추가합니다. JavaScript를 이용하는 경우에는 웹 현시에 있어 동일한 스크립트가 동작되어 수행됩니다.

function _ontaskview()

{

//TODO Add your javascript code here

var datagap = max-min;


var data = output;

if (data < min)

data = min;

else if (data >max)

data = max;


var h = (43 * (data-min)) / (max - min);

ID_GAUGE.height = h;


ID_LABEL_VALUE.textContent = output.toString();

ID_LABEL_MIN.textContent = min.toString();

ID_LABEL_MAX.textContent = max.toString();

}

위 코드를 간단하게 보면, 출력 데이터의 값이 심볼변수를 설정한 min, max의 범위안에서 처리가 되도록 조건을 걸어줍니다. 다음으로 ID_GAUGE의 초기 Height값 43을 기준으로 출력 데이터의 실제 게이지의 높이값을 계산하여, ID_GAUGE객체의 높이값으로 치환합니다.

ID_LABEL_VALUE, ID_LABEL_MIN, ID_LABEL_MAX의 값 현시를 위한 코드를 추가합니다. 이와같은 방법으로 SVG 그래픽 과 JavaScript 코드를 이용하여 다양한 그래픽 심볼 라이브러리를 생성할 수 있습니다. 


화면 드로잉

생성된 라이브러리를 이용하여 픽쳐페이지에 적용합니다. Project Explorer에서 Picture를 선택후 Add New Picture Item 메뉴를 선택하여 픽쳐페이지를 생성합니다.

생성된 픽쳐페이지에 생성한 라이브러리를 마우스를 이용하여 드래그 & 드랍으로 객체를 생성합니다. 선택객체를 더블클릭하여 Value Table의 입력값을 조정하여 정상적으로 라이브러리가 생성되었는지 확인합니다.

입력값을 50으로 설정시 게이지바의 위치가 50의 위치하였음을 확인할 수 있습니다.

리본 메뉴의 Data/Communication에서 웹서버 기동 메뉴를 클릭하여, 웹서버를 기동한 후 웹브라우져를 통하여 제작한 픽쳐페이지가 정상적으로 현시되는지 확인합니다. 


다양한 형태의 게이지를 생성하여, 아래 그림과 같이 적용 및 활용할 수 있습니다.

enuSpace for Mars(2017)은 HMI/SCADA/DCS/IoT분야에 강력한 기능을 활용하여 적용할 수 있습니다. 


본 기술개발의 일부는 중소기업청의 창업기술개발사업의 일환으로 수행된 결과입니다.


enuSpace for Mars(2017) - 주요기능 소개 (ENU Co.ltd)


추가되는 기능


1. IoT 디바이스를 로직 심볼 형태로 제공합니다.

2. 웹 서버기능이 엔유스페이스 프로그램에서 자체 지원합니다.

3. IoT 디바이스 심볼과 로직 블럭을 이용하여 연산 및 제어처리가 가능합니다. 

4. 웹 브라우져를 통하여 로직 처리 결과 확인이 가능합니다. 


상세기능.

IoT 디바이스를 심볼로 구성하여 그래픽적인 연산처리를 수행한후 실시간 디바이스의 센서값을 웹을 통하여 현시 및 제어를 수행할 수 있습니다. 

enuSpace for Mars의 데이터 인터페이스 구성도

enuSpace for Mars는 

Raspberry PI 전용 Windows 10 IoT Core기반의 전용 응용프로그램,

Arduino의 StandardFirmata 프로그램을 설치하여 연계를 수행합니다.

디바이스 등록과정을 거친후 서버에서 모든 제어 및 모니터링을 수행합니다.

● Raspberry PI 응용프로그램을 이용한 디바이스 등록 방법

사용자 로그인 및 웹서버 주소 설정 과정

디바이스 등록 과정

디바이스 등록후 StandardFirmata를 이용한 Arduino 시리얼 통신 연결 

디바이스 심볼 객체를 이용하여 디바이스 모니터링 및 제어기 만들기.

※ 디바이스 심볼은 기 제공되는 심볼을 이용하거나, 사용자가 직접 심볼을 제작하여 등록후 사용이 가능합니다. 


● 디바이스 심볼 만들기 방법

ProjectExplorer에서 Library하위 Logic에서 신규 라이브러리 파일을 생성하고, Add Node 메뉴를 이용하여 신규 라이브러리를 생성합니다.

생성된 신규 라이브러리에 관련 이미지 또는 기본 드로잉 객체를 이용하여 심볼을 디자인 합니다. 디자인된 심볼에 외부의 데이터 인터페이스를 위한 PIN객체를 추가하고, PIN객체의 속성 name을 디바이스의 A0, A1, A2, A3, A4, A5, D0, ~, D13까지 생성합니다. 

본 단계는 사용자가 직접 제어를 위한 방법이며, 본 프로그램에서 제공되는 라이브러리를 이용하는 경우에는 별도의 작업이 필요치 않습니다.  

IoT 디바이스 라이브러리 생성 방법

라벨의 객체에 실시간 디바이스 센서값 표현하기 위해서는 신규 text 객체를 추가합니다. 추가된 객체에 javascript 함수를 추가합니다. javascript는 웹 모니터링시에 동일한 형태로 브라우져에서 현시됩니다.

텍스트 객체에 스크립트를 추가하는 방법

위 그림과 같이 ontaskview() 함수를 추가하고, textContent = A0.toString(); 코드를 추가함으로서 A0의 변수값을 text의 라벨값으로 현시하기 위한 코드를 추가합니다.

위와 같이 사용자가 다양한 형태의 게이지, 컴포넌트 등을 구성하여 라이브러리를 구성할 수 있습니다.


● 디바이스 심볼활용 방법 (픽쳐 드로잉)

ProjectExplorer에서 Picture의 하위에 Add New Picture Item 버튼을 클릭하여 새로운 픽쳐페이지를 생성합니다. 생성된 페이지에 Logic 객체와 HMI 객체를 이용하여 화면 드로잉을 수행합니다. 

IoT 디바이스 심볼 객체를 이용한 드로잉 방법

등록된 디바이스에 대하여 IoT 디바이스 심볼 객체에 대한 속성 설정을 통한 화면 연계를 수행합니다.

디바이스 심볼을 선택하고, interface 속성값을 true 설정, interface-id의 속성값을 디바이스 키값을 입력합니다. 인터페이스 설정이 완료되면 디바이스의 정보가 심볼객체를 통하여 데이터가 현시됩니다.


모든 화면 구성 및 그래픽적인 로직화면 구성이 완료되었다면 저장을 수행합니다.


● enuSpace for Mars의 웹 서버연동

위 작업이 마무리 되었으면, 엔유스페이스 프로그램이 실행된 컴퓨터의 서버주소를 브라우져를 실행하여 주소를 입력합니다.

서버의 정보가 클라이언트에 실시간 데이터가 모니터링됨을 확인 할 수 있습니다.

enuSpace for Mars(2017) 실행 화면

크롬 브라우져에서 enuSpace에 접속한 화면


본 기술개발의 일부는 중소기업청의 창업기술개발사업의 일환으로 수행된 결과입니다.

Windows 10 IoT Core Arduino Firmata 연계


1. Create a New Project (Visual Stuidio 2015)

   WinRT Universal Windows Template 선택하여 신규 프로젝트를 생성.


2. Windows-Remote-Arduino Nuget Package 설치.

   Project explorer 오른쪽 버튼 클릭 팝업메뉴에서 Manage Nuget Package 선택.

   Windows Remote Arduino 검색 및 설치.


3. Arduino Firmata 프로그래밍

   관련 소스 : https://github.com/firmata/arduino/blob/master/examples/StandardFirmata/StandardFirmata.ino


4. Windows 10 IoT Core 응용프로그램 개발 (WinRT C#)

   WinRT C# Firmata 프로그래밍.

   관련 문서 : https://github.com/ms-iot/remote-wiring



Windows 10 IoT Core C# Using SQLite


Step 1. Install the SQLite Visual Stuidio extension for Visual Studio 2015

http://sqlite.org/download.html


Step 2. SQLite.Net-PCL NuGet Installation.


Step 3. Add Reference


Step 4. using SQLite.Net.Attributes;


Step 5. SQLite DB Connection and Query.


SQLite DB Create DB Table Sample.

        public class Configuration

        {

            [PrimaryKey, AutoIncrement]

            public int Id { get; set; }

            public string Attribute { get; set; }

            public string Value { get; set; }

        }


        void LoadSqliteDB()

        {

            String path = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "enuspace.sqlite");

            m_conn = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), path);

            m_conn.CreateTable<Configuration>();

        }

위 코드를 이용하면, 테이블명은 Configuration, 컬럼명은 Id, Attribute, Value에 해당하는 테이블을 생성한다.


SQLite DB Check Table Exists and Create DB Table

        void LoadSqliteDB()

        {

            String path = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "enuspace.sqlite");

            m_conn = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), path);


            if (TableExist("Configuration", m_conn) == false)

                m_conn.CreateTable<Configuration>();

        }


        public bool TableExist(String tableName, SQLite.Net.SQLiteConnection conn)

        {

            String tableExistsQuery = "SELECT name FROM sqlite_master WHERE type='table' AND name='" + tableName + "';";

            String result = conn.ExecuteScalar<string>(tableExistsQuery);


            if (result == tableName)

                return true;

            else

                return false;

        }

프로그램 기동시 테이블의 존재유무를 체크하고자 하는 경우에는 ExecuteScalar를 이용하는 방법이 있다. 이때 리턴받는 문자열 result값에는 테이블이 존재하는 경우, 테이블 이름이 전달되며, 테이블 이름이 없다면 null이 리턴된다.


SQLite DB Table data query

        void LoadSqliteDB()

        {

            String path = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "enuspace.sqlite");

            m_conn = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), path);


            if (TableExist("Configuration", m_conn) == false)

                m_conn.CreateTable<Configuration>();

            else

            {

                var query = m_conn.Table<Configuration>();

                 foreach (var message in query)

                {

                    String strAttribute = message.Attribute;

                    String strValue = message.Value;

                }

            }

        } 

데이터 테이블 쿼리 요청에 따른 결과값 확인을 수행한다.


SQLite DB Data Insert


        m_conn.Insert(new Configuration() { Attribute = "auto-login", Value = "true" });



SQLite DB Data Update


                        var query = m_conn.Table<Configuration>().Where(x => x.Attribute == "auto-login");

                        var result = query.ToList();

                        if (result.Count == 0)

                            m_conn.Insert(new Configuration() { Attribute = "auto-login", Value = "true" });

                        else

                        {

                            result[0].Value = "true";

                            m_conn.Update(result[0]);

                        }

 


SQLite DB Data Delete


       var query = m_conn.Table<Configuration>().Where(x => x.Attribute == "user-pw");

        var result = query.ToList();

        foreach (var item in result)

        {

              m_conn.Delete(item);

        }

 



SQLite DB 조건을 이용하여 Query 정보를 확인.

 

         var query = m_conn.Table<Configuration>().Where(x => x.Attribute == "auto-login");

         var result = query.ToList();

         foreach (var item in result)

         {

                String test1 = item.Attribute;

                String test2 = item.Value;

        }






Windows 10 IoT Core Remote Server 사용 Tip.

라즈베리 파이 응용 프로그램을 리모트 컨트롤을 수행하기 위해서 아래 그림의 링크를 따라서, 어플을 설치후 라즈베리 파이의 IP주소를 입력하여 실행을 수행하면, 리모트 컨트롤이 가능합니다.

Windows 10 IoT Core에서 WinRT C#용 Chart


1. WinRTXamlToolkit.Controls.DataVisualization.UWP를 이용한 Chart.


차트를 드로잉하기 위해서 nuget에 있는 https://www.nuget.org/packages/WinRTXamlToolkit.Controls.DataVisualization.UWP/

Toolkit을 이용하는 방법이 있습니다.  

Window 10 iot core 용으로 다운 받을 경우 UWP 버젼으로 다운 받아야 한다. 정상적으로 받았을 경우 xaml 파일과 cs 소스 코드를 이용하여 개발을 수행합니다. 

        <WinRT_Charting:Chart x:Name="LineChart1" Title="My Chart">

            <WinRT_Charting:LineSeries Title="T1"

                        ItemsSource="{Binding Items}"

                        IndependentValueBinding="{Binding Name}"

                        DependentValueBinding="{Binding Value}"

                        IsSelectionEnabled="True"/>

            <WinRT_Charting:LineSeries Title="T2"

                        ItemsSource="{Binding Items}"

                        IndependentValueBinding="{Binding Name}"

                        DependentValueBinding="{Binding Value}"

                        IsSelectionEnabled="True"/>

        </WinRT_Charting:Chart>


private void updateChart_Click(object sender, RoutedEventArgs e)

        {

            try

            {

                List<NameValueItem> newitems1 = new List<NameValueItem>();

                List<NameValueItem> newitems2 = new List<NameValueItem>();

 

                int num = items1.Count / 250;

                int con = 0;

                int value1 = 0;

                int value2 = 0;

                string name;

                for (var i = 0; i < items1.Count; i++)

                {

                     name = Convert.ToString(newitems1.Count);

                     newitems1.Add(new NameValueItem { Name = name, Value = items1[i].Value });

                     newitems2.Add(new NameValueItem { Name = name, Value = items2[i].Value });


                }

                ((LineSeries)this.LineChart1.Series[0]).ItemsSource = newitems1;

                ((LineSeries)this.LineChart1.Series[1]).ItemsSource = newitems2;

 

                newitems1 = null;

                newitems2 = null;

            }

            catch (Exception ex)

            {

                status.Text = ex.Message;

            }

        } 

Window 10 IoT Core에서 센서값을 차트로 표현결과, 라즈베리 파이에서 그래픽 리소스를 많이 잡아먹는 WinRTXamlToolkit.Controls.DataVisualization를 이용할 경우에는 많은 양의 데이터를 표현은 어렵다는것을 확인하였습니다. 약 1000개의 데이터를 현시할 경우, 약 30초정도 소요됩니다. 아래 그림은 1000개의 데이터를 차트에 표현하여 보았습니다. 

실시간으로 데이터 표현이 어렵기 때문에, 데이터를 취득하는 버튼과 데이터 취득 종료버튼을 별도로 이용하여 데이터를 분석하는 모듈로 구성하였습니다. 

만약 실시간으로 차트를 표현하고자 한다면, Canvas를 이용하여 직접 Drawing을 수행하면 빠른 현시가 가능합니다. 


2. Canvas객체를 이용하 WinRT C# Chart 드로잉

private void DrawChart(Canvas canGraph, List<NameValueItem>[] pList, double axisx_min, double axisx_max, double data_min, double data_max)

        {

            canGraph.Children.Clear();


            double[] RectWall;

            RectWall = new double[4];

            RectWall[0] = 0;                // left 

            RectWall[1] = 0;                // top

            RectWall[2] = canGraph.ActualWidth;   // right

            RectWall[3] = canGraph.ActualHeight;  // bottom


            double[] RectGap;

            RectGap = new double[4];

            RectGap[0] = 30;    // left 

            RectGap[1] = 20;    // top

            RectGap[2] = 20;    // right

            RectGap[3] = 20;    // bottom


            double[] RectChart;

            RectChart = new double[4];

            RectChart[0] = RectWall[0] + RectGap[0]; // left 

            RectChart[1] = RectWall[1] + RectGap[1]; // top

            RectChart[2] = RectWall[2] - RectGap[2]; // right

            RectChart[3] = RectWall[3] - RectGap[3]; // bottom


            double fChartWidth = RectChart[2] - RectChart[0];

            double fChartHeight = RectChart[3] - RectChart[1];


            int xGridNum = 5;

            int yGridNum = 10;

            double fGridGapX = fChartWidth / xGridNum;

            double fGridGapY = fChartHeight / yGridNum;


            // 차트 그리기.

            Rectangle pChartRect = new Rectangle();

            pChartRect.StrokeThickness = 1;

            pChartRect.Fill = new SolidColorBrush(Colors.Black);

            pChartRect.Stroke = new SolidColorBrush(Colors.Yellow);

            pChartRect.Width = fChartWidth;

            pChartRect.Height = fChartHeight;

            Canvas.SetLeft(pChartRect, RectChart[0]);

            Canvas.SetTop(pChartRect, RectChart[1]);

            canGraph.Children.Add(pChartRect);


            // x축 그리기.

            int iCount = 0;

            GeometryGroup xaxis_geom = new GeometryGroup();

            for (double x = 0; x <= fChartWidth; x += fGridGapX)

            {

                LineGeometry xtick = new LineGeometry();

                xtick.StartPoint = new Point(x + RectChart[0], RectChart[3] + 5);

                xtick.EndPoint = new Point(x + RectChart[0], RectChart[1]);

                xaxis_geom.Children.Add(xtick);


                TextBlock xlabel = new TextBlock();

                int ivalue = (int)axisx_min + (int)(axisx_max - axisx_min) / xGridNum * iCount;

                xlabel.Text = ivalue.ToString();

                xlabel.FontSize = 10;

                Canvas.SetLeft(xlabel, x + RectChart[0]);

                Canvas.SetTop(xlabel, RectChart[3] + 5);

                canGraph.Children.Add(xlabel);

                iCount = iCount + 1;

            }


            Path xaxis_path = new Path();

            xaxis_path.StrokeThickness = 1;

            xaxis_path.Stroke = new SolidColorBrush(Colors.Green);

            xaxis_path.Data = xaxis_geom;

            canGraph.Children.Add(xaxis_path);


            // y축 그리기.

            iCount = 0;

            GeometryGroup yxaxis_geom = new GeometryGroup();

            for (double y = 0; y <= fChartHeight; y += fGridGapY)

            {

                LineGeometry ytick = new LineGeometry();

                ytick.StartPoint = new Point(RectChart[0] - 5, RectChart[3] - y);

                ytick.EndPoint = new Point(RectChart[2], RectChart[3] - y);

                yxaxis_geom.Children.Add(ytick);


                TextBlock ylabel = new TextBlock();

                int ivalue = (int)data_min + (int)(data_max - data_min) / yGridNum * iCount;

                ylabel.Text = ivalue.ToString();

                ylabel.FontSize = 10;

                Canvas.SetLeft(ylabel, RectChart[0] - 20);

                Canvas.SetTop(ylabel, RectChart[3] - y - ylabel.FontSize);

                canGraph.Children.Add(ylabel);

                iCount = iCount + 1;

            }


            Path yaxis_path = new Path();

            yaxis_path.StrokeThickness = 1;

            yaxis_path.Stroke = new SolidColorBrush(Colors.Green);

            yaxis_path.Data = yxaxis_geom;

            canGraph.Children.Add(yaxis_path);


            // data 그리기.

            double x1 = 0;

            double y1 = 0;

            double x2 = 0;

            double y2 = 0;

            

            int idim = 0;

            idim = pList.Length;

            if (idim > 0)

            {

                for (int i = 0; i < idim; i++)

                {

                    GeometryGroup data_geom = new GeometryGroup();


                    List<NameValueItem> newitems = pList[i];

                    double xstep = fChartWidth / newitems.Count;


                    for (int j = 0; j < newitems.Count-1; j++)

                    {

                        LineGeometry vline = new LineGeometry();


                        x1 = RectChart[0] + xstep * j;

                        y1 = RectChart[3] - fChartHeight * ((newitems[j].Value- data_min) / (data_max - data_min));

                        if (y1 < RectChart[1])

                            y1 = RectChart[1];

                        if (y1 > RectChart[3])

                            y1 = RectChart[3];

                        x2 = RectChart[0] + xstep * (j+1);

                        y2 = RectChart[3] - fChartHeight * ((newitems[j+1].Value - data_min) / (data_max - data_min));

                        if (y2 < RectChart[1])

                            y2 = RectChart[1];

                        if (y2 > RectChart[3])

                            y2 = RectChart[3];

                        vline.StartPoint = new Point(x1, y1);

                        vline.EndPoint = new Point(x2, y2);

                        data_geom.Children.Add(vline);

                    }


                    Path value_path = new Path();

                    value_path.StrokeThickness = 1;

                    if (i == 0)  

                        value_path.Stroke = new SolidColorBrush(Colors.Red);

                    else if (i == 1)

                        value_path.Stroke = new SolidColorBrush(Colors.Green);

                    else if (i == 2)

                        value_path.Stroke = new SolidColorBrush(Colors.Blue);

                    else

                        value_path.Stroke = new SolidColorBrush(Colors.Yellow);

                    value_path.Data = data_geom;

                    canGraph.Children.Add(value_path);

                }

            }

        }

위와 같이 직접 Canvas에 직접 차트를 그릴경우에는 약 3000개의 데이터를 현시하는데 있어 2~3초정도 소요됨을 확인하였습니다. 

차트를 이용하여 대용량을 현시하고자 할 경우에는 Canvas에 직접 그리는 것을 권고합니다.

Canvas를 이용하여 WinRT용 C# 차트 응용프로그램 실행 화면.



+ Recent posts