페이지

레이블이 modbus인 게시물을 표시합니다. 모든 게시물 표시
레이블이 modbus인 게시물을 표시합니다. 모든 게시물 표시

2016년 4월 1일 금요일

pvbrowser MySQL 사용하기 - Modbus 통신 데이터 Database 에 저장하기


자, Database에 연결하였고, DB에 데이터도 넣어보았습니다.

데이터를 자동으로 입력해 보죠.
제가 사용하는 방법이 틀릴수도 있습니다. 작동이 되지않는다 그런 문제가 아니라 시스템에 부하가 많이 가해질수도 있고, 에러가 발생한 우려도 있다는 겁니다. 그러니 이 사람은 이런 식으로 사용하는구나... 라고 생각하시기 바랍니다.

SCADA, HMI, 기타... 등등, 대부분의 데이터 수집 장치는 백그라운드에서 일정 주기 간격으로 데이터를 수집합니다. 사용자가 수집된 데이터를 버튼을 이용해 저장하는 것은 극히 드물죠

가장 중요한 개념은 백그라운드에서 일정주기 간격으로 저장한다.

pv서버 프로그램에서 백그라운드는 무엇일까요, 뭐, 서버프로그램 자체가 백그라운드라고 말한다면, 할 말없고, 여하튼 main, mask,... 제가 볼때 main이 백그라운드 입니다. mask 프로그램은 호출받을때만 실행되므로, mask에 주기적으로 데이터를 저장하는 코드를 집어넣어도 제대로 작동하지 않습니다. 만약 mask가 한개만 있다면 가능하죠. 그러나 여러 mask를 사용하는 경우는 제대로 작동하지 않습니다.

일정주기 간격으로 저장할려면 어떻게 해야 할까요? 이건 이벤트 혹은 인터럽트 개념인데...

여하튼 골치 아픈 문제 입니다.
QTimer와 signal Slot을 이용하여 해결해 보려고 했는데 yahoo Group 에 있는 pvbrowser 항목을 살펴보니 이건...OTL... pvbrowser는 Qt를 이용해 개발했지만 다른 쪽은 아니라는 식으로 설명되어 있네요.

그냥 스레드를 하나 만들어 처리하는 식으로 방향을 잡고 진행해 보도록 하겠습니다.

스레드 만들기

그럼 스레드 만드는 법을 살펴보죠.
스레드를 만들기 위해서 사용하는 라이브러리가 "rlthread.h"입니다. 헤더 파일에 정의해 놓고 넘어가죠.

메인 함수에 다음과 같은 코드를 집어 넣었습니다.





간단히 말하면 timerBase라는 rlThread 객체를 만들었습니다. 그리고 객체(timerBase)가 사용할 함수(timerBaseThread())를 하나 만든 후 rlthread에서 지원하는 create()함수를 이용하여 스레드를 만들었습니다.
간단하죠, 우선은 인자로 NULL을 넘겨 주었습니다.

timerBaseThread() 함수를 수정하여 5초마다 '>>I'm alive." 문장을 출력하도록 해보죠.
만들어진 Thread가 주기적으로 작동하는지 확인하기 위해서...



timer를 사용하기 위해 C 표준함수인 <ctime.h>을 사용하였습니다. 헤더파일 수정해 주시기 바랍니다.

서버를 시작한 후 확인해 보았습니다.

그림 1. 서버 실행화면

5초마다 자기가 살아있다는 메시지를 출력하는 군요. 시간은 정확한 5초가 아니고 5초 이상에서 한번씩 나타납니다...(이것은 본인 실력이 이것 밖에 되지 않으니 이해해 주시기를...)



Modbus 입력 데이터 Database에 저장하기

그럼 timeBaseThread() 함수에서 지난번에 만든 Arduino Modbus에서 수집한 데이터를 읽어 들여오도록 코드를 수정해보죠. 5초 이상의 간격으로 데이터를 수집하는 구문입니다. 




modbus에서 넘어오는 dataInterval 변수로 지정한 주기로 저장하기 위해 unsigned short 형식의 sqlData 배열을 정의하였습니다. 살펴보시면 특이 사항이 없기 때문에 실행해보죠.

그림 2. 서버실행화면-Modbus Data
Arduino에서 난수 형태의 데이터가 넘어오는 것을 확인할 수 있습니다. 잘 동작하네요.

지금까지의 내용이 "백그라운드에서 일정주기 간격으로 저장한다." 라는 개념을 반영한 것입니다.



자, 그럼 최종적으로 이 데이터를 Database에 저장해보죠.

지금부터의 내용은 본인이 제대로 이해하지 못하면서, 작동하게만 만들어 놓은 코드입니다.
즉, 정확한 내용(혹은 방법)이 아닐 수 있습니다. 하지만 동작하는데 이상은 없었습니다. 마음에 드는 코드는 아니지만...

우선 DataType을 하나 정의 했습니다.



특이한 것 없습니다. 나중을 위해.... 특이한 것은 PARAM *p 를 이용했다는 것.




Header 함수에 string 함수를 사용하기 위해 다음과 같이 하여 주시기 바랍니다.



여기서 이해 되지 않는 부분이

myThreadData *d = (myThreadData *) p->user;

db.query(d->p, "INSERT INTO Arduino .....");

myThreadData 로 d 를 정의하여야 query에서 인식을 하는데 왜 그런지다시 한번 확인해 보자...여하튼 지금은 그냥 넘어가자.

컴파일 후

그림 3 pvbrowser 실행화면

그림3과 같이 데이터가 주기적으로 계속 저장되고 있습니다.
비록 Arduino에서 넘어온 데이터는 아니지만....

자 Arduino에서 넘어온 데이터를 Database에 넣어 보죠.

설명하기 귀찮아서아래와 같은 코드를 작성하였습니다.




코드 최적화를 해야하는데 실력이 되지 않네요. 여하튼 실행시키면 그림 4와 같은 화면이 나타납니다.

그림 4. 서버 실행화면

그림 5. pvbrowser 실행화면

pvbrowser 화면에서 확인해 보았습니다. 잘 되는 군요.

뒤로 갈수록 설명이 영 엉망이네요. thread관련 부분은 다시 한번 검토해 보아야 할 것 같습니다.

2016년 3월 27일 일요일

pvbrowser를 이용한 Arduino 통신 예제 (Modbus 모드)

MODBUS 통신

MODBUS 읽기, 쓰기 관련 함수들은 rlModbus 에 정의되어있습니다.
작성하는 mask에서 이 함수들을 사용하기 위해서는 slot 에 주석처리되어 있는 다음 구문의 주석처리를 제거하시기 바랍니다.

//extern rlModbusClient modbus; //Change if applicable

modbus 객체를 만들어 주는 구문입니다.  

읽기 함수
  • readShort()
    Word Data 읽기
  • readByte()
    Byte Data 읽기
  • readBit()
    Bit Data 읽기

먼저, 워드형(Word type) 데이터를 읽는 방법에 대해 알아보죠. readShort()함수는 다음과 같이 정의되어 있습니다.




offset, number 인자를 이용하여 정수형 데이터를 얻고 있습니다. 이 함수의 소스 코드를 확인해 보죠.


 Modbus 데몬을 만들때, 정의한 rlsharedMemory 에서 offset 과 number를 이용하여 데이터를 읽어 들입니다.

rlsharedMemory에서 원하는 데이터를 읽기 위한 함수의 정의입니다.


이 함수를 내부를 대강 둘러보면 rlsharedMemory (Modbus 통신데이터 저장 파일)에 있는 데이터를 mutex 방식을 이용하여 데이터를 읽어내는 코드를 포함하고 있습니다.

너무 깊숙이 들어온 것 같은데, 여하튼 rlsharedmemory에서 원하는 데이터를 가지고 오는 군요.


readShort() 함수 인자 중 offset은 베이스 주소 라고 생각해도 무방합니다. 실제 rlSharedMemory에서 읽어 오는 값의 위치는 offset + (number*2) 이며, 이 위치의 값을 정수형으로 읽어들이죠.
rlSharedMemory에 저장되어 있는 내용들은 기본적으로 Byte입니다.


예를 들어 50 번지의 내용을 읽고 싶다면

modbus.readShort(50, 0 );
modbus.readShort(45, 5 );
modbus.readShort(40, 10 );

이런 식으로 표현할 수 있습니다. 전체적으로 동일한 50번째 위치에서(정확하게는 x2) 두개의 byte를 읽어와 정수형 데이터를 반환해 줍니다.

써넣고 보니, 무슨 말을 하려고 하는 건지 잘 모르겠다.



잠깐, modbus 데몬 정의에 대해 알아보죠.

modbus를 정의할 때 cycle 구문을 한개만 사용한다면 별의미가 없지만, 두 개인 경우는 어떤식으로 구성해야 할까요. 또, 워드형만 읽지않고, 코일 형(bit)도 읽어들이고 싶다면.
한번 살펴보죠.

사이클 1개로 워드형 데이터만 읽어들이면 될 경우 아래와 같은 형태로 쓰면 됩니다.


Cycle1, 한개로 Address 0 ~ 28 까지 29개의 데이터를 읽어들이는 구문입니다.
이것을 두개의 Cycle로 읽어 들인다면.



Cycle1에서 0 ~9 까지 10개의데이터를 읽어들이고, cycle2에서 10 ~ 28 까지 19개의 데이터를 읽어들이고 있습니다.
이때 주의할 점은 cycle2의 시작 주소(start_adr)입니다. 이 값을 기준으로 shm파일(rlSharedMemory)의 저장 위치가 결정되는 것 같습니다. 연속으로 저장하기 위해 이 값을 cycle1 기준으로 맞춰주는 것이 중요합니다.

그림 1. Modbus Daemon 동작상태
%컴파일된 modbus daemon 실행파일은 프로젝트 폴더에 만들어지며, 개별 실행하시면 됩니다.

그림 1과 같은 경우가 modbus 데몬이 통신기기와 정상 연결된 상태입니다. 만약 정상 연결이 되지 않았다면 ret 값이 -1 로 나타납니다. 즉 읽어들인 데이터가 없다

그림 1은 stat_adr=0, num_register=1 즉 , 1 word 데이터를 읽어 들이는 경우 입니다. 1 word는 2byte 이므로 ret값에 2가 나타납니다.

그림2. Modbus Daemon 동작상태 (Cycle=1)

그림 2는 stat_ad=0, num_register=29로 설정한 경우 입니다. 29x2=58byte 따라서 ret 값이 58입니다.

그림 3. Modbus Daemon 동작상태(Cycle =2)
그림1, 2는 cycle을 한개만 사용한 경우지만, 그림3은 cycle을 2개 사용한 경우 입니다. ret 값이 58, 40이 반복적으로 계속 나타나는 군요....

자, 그럼 간단한 GUI 화면을 작성하여 Arduino에서 넘어온 데이터를 확인해 보았습니다.

그림 4. pvbrowser 실행 화면

지금까지는 cycle에서 function 3을 이용하여 word 데이터 만 읽어 들였습니다. 이 경우 만약 word 데이터를 이용하여 bit 처리(INPUT/OUTPUT)을 하려고 하면 많이 번거롭죠. 어떻게 해야 할까요. Single Coil 형태로 읽어 들이면 간단히 해결할 수 있습니다.


아두이노에서 32개의 INPUT Coil (32/16 = 2 Word), 32개의 OUTPUT Coil (32/16 = 2 Word)값을 입력받는다고 생각해보죠,
우선, 아두이노 modbus 통신용 데이터를 다음과 같이 정의하였습니다.

uint16_t au16data[29];


아두이노 Modbus 통신 데이터 au16data[0], au16data[1]은 Input Coil, au16data[2], au16data[3]은 Output Coil, 22 개는  측정데이터 혹은 내부 설정데이터, 나머지 3개는 mobus 상태로 구성하겠습니다.

au16data[0]    <--Input coil  (Bit 16)
au16data[1]    <--Input coil  (Bit 16)
au16data[2]    <--Output coil  (Bit 16)
au16data[3]    <--Output coil  (Bit 16)
au16data[4]    <--Data  (Word)
     |
au16data[26]    <--Data  (Word)
au16data[27]    <--Modbus Status  (Word)
au16data[28]    <--Modbus Status  (Word)
au16data[29]    <--Modbus Status  (Word)

이 경우 Cycle은 어떻게 설정해야 할 까요.



저는 이런 식으로 정의했습니다.

그림 5. Modbus daemon 실행 화면

Modbus daemon 작동상태를 확인해 보니 ret 값이 4, 4, 50 반복되고 있습니다. 정확히 동작하는 것 같군요.
자, 그럼 이것을 이용해 다음 GUI를 조금 더 수정해 보았습니다.



QImage 위젯을 이용하여 LED 형태를, 대강 만든 후, 비트 값을 검사하여 1인 경우 파란색, 0인경우 흰색으로 나타나게 해 보았습니다. 잘 작동하는 군요.


GUI 제어용 slot 코드입니다.



Arduino 소스 코드 입니다.



추가적인 것은 직접 해보시기 바랍니다.