페이지

2016년 4월 1일 금요일

pvbrowser 서버 백그라운드 실행

pvbrowser 서버를 백그라운드에서 실행하고 싶다.

무엇을 잘못 해서 인지는 몰라도 백그라운드 실행이 아음대로 되지않는다.
pvbrowser에서 공식적으로 제시하는 방법은

  •  pvservice
  •  pcontrol

여기다 start_pvapp 을 이용하여 할 수 있을 것 같은데

 우선 pvservice 사용법을 잘 모르겠다. 윈도우에서 공식적으로 지원해 주는 프로그램을 이용해서 하는 것 같은데 머리 용량이 딸려서 몇번 시도하다 포기.
pcontrol 도 시도하다 포기.
.
.
.OTL
.
.
start_pvapp을 이용하여 서버를 돌리고 pvbrowser에서 작동하는 것 까지는 확인했는 데, 이상하게 재접속하면 되지 않는다.

그래서 윈도우 OS 에서 서비스 등록하는 방법을 검색하다, 발견한 것이

NSSM - the Non-sucking Service Manager

이 프로그램을 이용하여 윈도우 서비스를 등록하니 잘 된다. modbusdaemon도 서비스 등록이 된다. 더 좋은 것은 부팅 단계부터자동실행하게 할 수 있다.

바로 내가 찾던 프로그램....그럼 한번 사용해 보자


NSSM 홈페이지에서 압축파일 다운로드 한 후, 압축을 풀면 소스, 32비트, 64비트 폴더가 생성된다. 자신의 OS에 따라 실행하면 된다.설치도 필요 없다.

단 조심하자.
event log message를 nssm 프로그램이 사용하기 때문에 다른 위치에서 실행하던가. 버전이 다른 것을 사용하면 충돌이 발생할 수 있다고... 설명되어 있다.
동일 폴더에서 nssm을 실행시키면 문제가 없는 것 같다.

기본적으로 도스 모드에서 동작하는 프로그램으로 홈페이지에서 설명한 방법으로 서비스 등록하면 NSSM 설정창이 나타나는 데, 설정창에서 필요한 설정을 하면 서비스 등록이 된다.
서비스 등록, 시작, 정지, 제거와 같은 것들은 CMD 창에서(관리자 권한으로) 수행하여야 한다.

1.서비스 등록 (install)

nssm install <servicename>

서비스이름을 pvs로 지정하고 실행해 보았다.

그림 1. NSSM

그림2. NSSM install 실행화면

다음과 같은 Tab으로 구성되어 있다.
  • Application
  • Details
  • Log on
  • Dependencies
  • Process
  • Shutdown
  • Exit actions
  • I/O
  • File rotation
  • Environment


Application Tab에서 프로그램을 등록하면 된다.

그림 3.application 설정
  • Path
    실행파일의 전체 경로를 적어준다.
    본인의 경우 프로젝트/release 폴더에 생성된 실행파일을 지정해준다.
  • Startup directory
    실행파일이 실행될 디렉토리를 적어준다
    본인의 경우 프로젝트 폴더를 지정해준다. 서버 프로그램 개발시 사용하는 파일들이 프로젝트 폴더에 있기 때문. 예)SVG,....
  • Arguments
    서버 실행시 필요한 인자들을 적어준다.
    예) -port=5050

Detail 탭에서는 서비스 창에 나타날 정보들을 입력한다.
이 탭에서 중요한 것은 startup_type이다. 컴퓨터 부팅 단계부터 실행하고 싶으면 Automatic을 설정하면 된다. 본인의 경우 manual을 선호한다.

그림 4.Detail 탭

  • Display name
    서비스에 나타나는 이름을 적어 준다
  • Description
    서비스에 나타나는 설명을 적어준다.
  • Startup_type
     Automatic
     Automatic ( Delayed Start)
     Manual
     Disabled

Dependences 탭은 이 서비스를 실행하기 위해 함께 실행하여야 되는 서비스를 넣어준다.
함께 실행하고자하는 서비스 역시 서비스로 등록되어 있어야 한다.
본인의 경우 modbusdaemon도 함께 실행하고 싶기 때문에 pvsModbus라는 서비스를 등록한후 dependences 탭에 넣어 주었다.

그림 5. Dependences 탭


이 정도만 설정하고 Install Service 버튼을 누르면 서비스에 등록된다.
등록된 서비스는  윈도우 서비스 매니저에서 확인하면 된다. 수동으로 서비스를 등록한 경우 서비스 매니저에서 시작 혹은 정지할 수 있다.

그림 6. 윈도우 서비스 매니저

 서비스 매니저 이름 항목에 나타나는 것이 Displayname, 설명 항목에 나타나는 것이 Description 항목이다. 본인이 보기 쉬운 것(확인하기 쉬운 것)으로 써주면 된다.

너무 간단하게 설명해서 죄송.
NSSM 홈페이지 메뉴얼에 너무 자세하게 설명되어 있어 통과합니다.

저는 배치파일로 만들어사용합니다.




참조만 하시기 바랍니다.
batch쪽은 거의 사용하지 못합니다.

여하튼 가장 많이 쓴 명령어를 한번살펴보죠.

  • nssm install <servicename>
    서비스 등록
  • nssm start <servicename>
    등록된 서비스 시작
  • nssm stop <servicename>
    등록된 서비스 정지
  • nssm remove <servicename>
    등록된 서비스 제거
  • nssm set <servicename> <parameter> <subparameter>
    서비스 항목 편집
    사용 예)
    nssm set pvs AppDirectory c:\...\...
    서비스로 사용하고 싶은 프로그램의 디폴트 디렉토리
    .<parameter>에 대한 자세한 내용은 nssm 홈페이지 메뉴얼 참조
  • nssm edit <servicename>
    서비스 항목은 이 명령을 이용하면 보다 쉽게 할 수 있습니다.
    GUI 창이 나타나니까.

여하튼 배치로 만들어 사용하니 매우 편리합니다.

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관련 부분은 다시 한번 검토해 보아야 할 것 같습니다.

pvbrowser MySQL 사용하기 - Database(MariaDB) 연결

DATABASE에 연결 방법 정리

하도 오래 간만에 하려고 하니, 했던 방법을 다 잊어버려 개고생...하다. 나중에 같은 일이 발생할까봐 정리하게 되었습니다.

잠깐, 삼천포로 빠지겠습니다.
 pvbrowser는 Qt를 이용하여 만들어진 프로그램입니다. 즉, Qt에 있는 모듈을 모두 사용할 수 있습니다. Qt에서 기본적으로 지원하는 QtSql 모듈은 플랫폼(OS), DATABASE의 종류에 무관하게 사용할 수 있을 뿐 아니라. Qt의 모델/뷰(Model/View) 구조를 지원합니다.
아직 저도 Model/View 개념은 혼동이 있는 상태지만, 익숙해지면 쉬워지겠죠.

Qt에서 Database와 관련된 모듈들을 한번 살펴보면
  • QSqlDatabase
  • QSqlQuery
  • QSqlTableModel
  • QSqlRelationalTableModel
  • ...
여하튼 많이 있습니다. 이번에는 데이터베이스를 연결하고, 테스트하는 것이 목적이므로 QSqlDatabase, QSqlQuery가 주 관심사이며, pvbrowser에서 어떻게 코딩하여야 하는지 알아보죠.

오늘 하는 내용은 "pvaddon\templates\qtDatabase" 에 있는 내용을 쉽게 풀어 쓴 것이라고 생각해 주시기 바랍니다. pvaddon 패키지는 pvbrwoser 홈페이지에서 다운로드 받으시기 바랍니다. 아니면 GitHub에서 검색하셔도 됩니다. 여하튼, 이 패키지에 있는 qtdatabase.h, qtdatabase.cpp 파일이 필요합니다.


먼저 Qt의 SQL을 모듈을 사용할 수 있게 project 파일을 수정하여야 합니다.

새로 만든 프로젝트 파일의 기본 내용입니다.


아래와 같이 수정해 주시기 바랍니다.


pvbrowser 프로젝트를 생성하면 기본적으로 Qt 관련 내용을 사용하지 않는 것으로 설정되어 있습니다. 하지만 Qt에서 지원하는 SQL 관련 모듈을 사용하기 위해서는 위와 같이 수정해 주셔야 합니다.


두번째로 pvaddon 에 있는 qtdatabase 코드를 사용하도록 설정하겠습니다.
사용하기 전에 한번 qtdatabase 헤더파일을 살펴보죠.



qsqldatabase, qsqlrecord,qsqlfield, qsqlquery, qsqlerror 를 이용하여 qtDatabase 객체를 만들었고, 다음과 같은 함수들을 지원해주는 군요.
  • open()
  • close()
  • query()
  • populateTable()
  • recordFieldValue()
  • nextRecord()
 오늘은 qtDatabase 객체에서 지원하는 이 함수들을 이용하여 MariaDB에 접속하고자 합니다


만들어준 pvbrowser 프로젝트 폴더에, 이 함수들을 이용할 수 있게 설정한 후, 사용하고자하는 mask에서 extern 명령을 이용하면 됩니다.(글이 많이 꼬여 있네요...) 여하튼 다음과 같은 코드를 프로젝트 파일에 넣어 주시기 바랍니다.



수정된 프로젝트 코드입니다.


코드를 수정하신 후 다운받은 qtdatabase.h, qtdatabase.cpp를 프로젝트 폴더에 복사하여 주십시오. 만약 위치가 틀릴 경우 HEADERS, SOURCES 에 경로를 함께 적어 주셔야 합니다.
저는 그냥 복사하여 사용하였습니다.

이 단계까지 수정한 후 컴파일 해 보았습니다.

그림 1. 컴파일 화면
별로 특이한 것이 없는 것 같은데... 여하튼 넘어가죠.


프로젝트 파일에 필요한 수정은 다 끝난 상태입니다. 두가지만 기억하죠.
qtsql 모듈을 사용하도록 설정해야한다는 것과 외부 파일(qtdatabase Header & Source)의 경로를 설정한다는 것.
Mask에 database관련 코드를 직접 코딩할 경우 외부파일이 필요 없습니다. 하지만 그러지 않아도 복잡한 Mask 파일에 부하를 감소시키는 방법이 있으면 활용하는 것이 좋겠죠.

다음은 Main() 함수에서 DATABASE와 연결해 보겠습니다.
Main() 함수에서 qtDatabase 객체를 사용하기 위해 다음과 같이 정의해 주십시오.



Main()함수에 qtDatabase 클래스를 사용하려면 pvapp.h에 다음과 같이 입력해 주셔야 합니다.


컴파일해볼까요.

그림 2. 컴파일 화면
아직까지는 에러가 발생하지 않고 있네요. 만약 "qtDatabase does not name a type"  에러가 발생하면 pvapp.h 파일에서 qtdatabase.h 파일을 include 하지 않아서 입니다. 주의하시기 바랍니다.


자, SQL 관련 기본 준비는 끝난 것 같군요. 데이터베이스에 접속해 보겠습니다.
데이터베이스 접속코드는 일반적으로 Main() 함수에 넣어줍니다. 그래야 다른 mask에서도 사용할 수 있으니까.

만약, INETD를 사용하는 경우(LINUX,.. 계열)에는 #ifdef USE_INETD 밑에 있는 main()함수에, 윈도우 계열에서는 #else 밑에 있는 main()함수에 다음과 같은 코드를 넣어 주십시오.



qtDatabase class 정의된 open() 함수를 이용하여 "192.168.0.***"에 설치된MariaDB에 MySQL 커넥터를 이용하여 접속하고 있습니다.
open() 함수의 인자는 다음과 같습니다.
  • DBType
    QMYSQL, QOCI, QODBC, QSQLITE,...
  • Hostname
    SQL server
  • User
  • Pass

db.open()이 정상적으로 수행되는 경우ret = 0, 에러가 발생하면 -1 값을 반환합니다. 물론 정상적으로 수행되어야 db 객체가 생성됩니다.

컴파일 해보죠

그림3. 컴파일 화면
Make를 이용하여 컴파일 한 결과 입니다. 정상적으로 되고 있습니다.

그럼 pvbrowser에서 테스트하기 위해 서버를 시작해 볼까요. Action->Start Server를 눌러 주십시오.

그림 4. 서버 실행 화면
화면 하단부에 보면 다음과 같은 문구가 나타나내요.



우선 "db.open ret = -1 " DATABASE 를 열지(연결)하지 못했다는 메시지가 나오네요.
이것은 위에 있는 메시지를 보면 해결방법이 나옵니다.
"QMYSQL driver not load" 즉, 드라이버가 설치되어 있지 않기 때문입니다. 이 문제는
Database Connector를 설치하면 해결됩니다.
각 DB마다 필요한 connector를 설치해야합니다. 저의 경우 mysql connector for C/C++을 설치하여야 이 문제가 해결되죠.
그럼 connector는 어떻게 설치하죠. 두가지 방법이 있습니다.
  • DB 서버 구축

    그림 5.MariaDB lib폴더

    MariaDB가 설치된 위치에 있는 lib 폴더에 보면 libmysql.dll이 설치되어 있습니다.
  • DB connector 단독 설치
    홈페이지에서 connector 설치프로그램 다운로드 및 설치
MySQL Database도 동일하다고 생각하시면 됩니다.

여하튼 libmysql.dll 라이브러리가 필요합니다. 이 파일을 현재 프로젝트 폴더에 넣어주시거나, pvb가 설치된 디렉토리 하위에 있는 다음의 디렉토리에 복사해 넣어주십시오.

pvb\win-mingw\bin

Action->start server를 눌러 서버를 실행해 보죠

그림 6. 서버 실행 화면

"QMYSQL driver not load" 메시지가 사라지고 "db.open ret=0" 이라는 메시지가 출력되었네요.
서버에 정상 접속되었습니다.
만약 이 단계에서도 ret=-1이 나타나면 db 서버 상태, 사용자/PASSWORD, DB Connector 지원 방식을 다시 확인해 보시기 바랍니다.

기본적으로 qt를 사용한다면(CONFIG += qt) qt 관련 이벤트를 처리하기 위한 모듈을 로딩하는 것이 필요합니다. "QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins" 이 메시지와 연관이 있는 것 같습니다. 무시해도 사용은 되지만, 잘 모릅니다. 찜찜하죠. 그래서 다음과 같은 것을 넣어 주죠.

#include <QCoreApplication>
QtGUI 를 사용하지 않는 프로그램에서 Qt 이벤트 처리를 지원해주는 것입니다. 위치는 main.cpp에 넣어주시기 바랍니다.



여기까지 하면 qtDatabase 객체인 db가 생성된 것입니다. 이제 이 객체를 이용하면 되죠.


qtdatabase.h 에 정의 되어 있는 query()와 populateTable()를 사용해 보겠습니다.

테스트용으로 아래와 같은 GUI를 하나 만들었습니다.

그림 7. 기본 GUI 화면

사용하는 mask에 다음의 내용을 입력해 주십시오.



설명을 하지 않았는데, db접속시 multi thread 개념을 사용하여야 합니다. DATABASE는 혼자가 아닌 여러 프로그램들이 사용하므로...
여하튼 rlMutex를 이용해야 하므로 이 객체가 정의되어 있는 rlthread.h를 사용할 수 있게 하여야 합니다. 헤더와 메인 프로그램에 다음과 같이 넣어 주십시오.

>> 헤더 프로그램



>>Main 프로그램



테이블 위젯에 DB 에서 읽어들인 데이터를 표시하기 위해 다음과 같은 코드를 사용하였습니다. pvaddon에 있는 예제를 인용했습니다.



Database 쿼리 관련 작업은 간단할 수도 있지마, 많은 데이터를 처리하는 경우 시간이 소요되는 작업이기 때문에 mutex 방식으로 데이터를 보호하고 있습니다.

sql 명령어 쿼리는 db.query() 함수를 이용하는 군요. 이 함수 내부를 살펴보면 db->exec() 함수를 이용합니다. Qt에서 SQL 명령을 보내기 위한 것과 동일한 함수를 사용하는 것을 알 수 있습니다.

저는 query() 함수에 DB 서버의 arduino 테이블에서 데이터를 몽땅 가져오라는 명령을 주었습니다. 정상적으로 명령이 수행되면 qtDatabase 객체에 정의된 result 변수에 (QVariant)형식의 데이터가 입력됩니다. 쿼리 수행결과에 에러가 발생하면 pvbrowser status에 다음과 같이 에러를 표시해 줍니다.

그림 8. Query 실행 에러 표시

그림 9는 Query가 정상적으로 처리되었을 때 나타난 화면입니다.

그림 9. Query 정상 처리
Arduino라는 테이블만 만들어 놓고, 데이터를 입력하지 않았기 때문에 이런식으로 나타나는 군요. Arduino 테이블의 column들이 제대로 표시되고 있습니다.


Database에 연결되었으면 데이터를 넣어보죠.
Database에 데이터를 입력하기 위한 INSERT 버튼과, DB를 다시 읽어 들이기 위한 RELOAD 버튼을 만들었습니다.

RELOAD 버튼 관련 함수는 지난번 것을 그대로 이용하면 되므로 패스...
INSERT 버튼 관련 함수는 다음과 같이 만들었습니다.



똑같죠. 단지 db.query() 함수에 들어가는 것이 다르네요.

insert into Arduino (data00, data01, data02) values (10, 20, 30);

테이블에 데이터를 입력하기 위한 SQL 구문을 그대로 넣으면 됩니다.


그림 10. 실행화면

pvbrowser 실행화면입니다. 데이터가 잘 들어가네요.

2016년 3월 30일 수요일

pvbrowser 설치

1. pvbrowser 설치


pvbrowser는 리눅스, 윈도우, OS-X, OpenVMS, Maemo,... 등 여러가지 배포판을 지원해 줍니다. 소스코드를 다운받아 컴파일하여 사용할 수 도 있고(많이 번거롭죠...), 실행파일을 다운받아 설치할 수 도 있습니다.


윈도우용 배포판의 경우 두 가지가 제공되고 있습니다.
  • install-pvbrowser-client.exe
    pvbrowser 클라이언트 프로그램 만 설치할 경우

  • install-pvbrowser.exe
    pvbrowser, pvdeveloper,...를 설치할 경우
pvbrowser 서버 프로그램을 개발할 경우에는 install-pvbrowser.exe를 홈페이지에서 다운받아야 합니다.

저는 윈도우 운영체제에서 서버 프로그램을 만들고 싶기 때문에 install-pvbrowser.exe를 다운 받았습니다.(홈페이지에서 다운받는 속도는 조금 느립니다.)

윈도우용은 여기서 다운 받으시기 바랍니다.

pvbrowser는 GPL, LPGL 라이센스 규약을 따릅니다. 자세한 라이센스 내용은 확인해 보시기 바랍니다.
pvdeveloper를 이용하여 서버 프로그램을 만든 경우, pvbrowser, pvdevelop 프로그램 자체를 돈 받고 팔 수는 없지만(기본 제공 하여야 합니다.), 프로그래밍 비용은 청구할 수 있습니다. (자세한 내용은 라이센스 규약에서 확인하시기 바랍니다.)

여하튼 서버 프로그램을 설치해 보죠.

그림 1. install-pvbrowser 실행화면

설치가 끝나면 메모장이 열리면서 다음과 같은 메시지가 나타납니다.



간단히 살펴볼까요.
1. 레지스트리 설정이 필요없기 때문에 바로 실행할 수 있다.
2. start_pvdevlop.bat 를 실행하기 위해 환경변수 설정이 필요하다.
3. 독립모드(standalone)로 서버를 실행하기 위해 환경변수 "PATH"에 \PVDIR\win-mingw\bin 이 있어야 한다.
4. pvdevelop를 위해 Qt, MinGW 소프트웨어가 설치되야 한다.

추가적으로
소스코드에서 pvbrowser를 컴파일하는 방법이 설명되어 있습니다.


먼저 환경 변수 PATH를 확인해 보았습니다.
c:\pvb\win-mingw\bin
이 경로가 추가되어 있군요. 자 2번째 항목인 "start_pvdevlop.bat" 관련 내용은 자동으로 추가 되는 군요.  3번째 항목도 이 값으로 해결.

시스템변수를 살펴보면
PVBDIR c:\pvb
저는 pvbrowser를 설치할때 기본경로는 이용하였기 때문에, 생성된 PVBDIR 시스템변수의 경로가 기본 경로로 설정되어 있군요.

그러니까, 설치프로그램을 실행하면 1, 2, 3은 신경쓸 필요가 없습니다.

단지 C++, python을 이용하여 서버 프로그램을 작성하기 위한 4번째 항목만 설치하면 됩니다.

우선을 테스트를 위해 실행해 보죠
Desktop 바탕화면에 pvbrowser, start_pvdevelop 두 프로그램의 바로가기가 만들어져 있네요. start_pvdevelp를 실행하겠습니다.


그림 2. start_pvdevelop 도스창

그림3.pvdevelop 창


start_pvdevelop는 확장자가 "bat" 입니다. 즉, 배치파일이기 떄문에 그림 2와 같은 화면이 나타난 후 그림 3이 나타납니다.
도스창에서 아무 키보드 키를 누르면 창이 사라집니다.



배치프로그램을 이용하여 pvdevelop 환경 설정을 한 후 pvdevelop 프로그램을 실행합니다.


설치 테스트를 위해 기본 프로젝트를 만들어 보죠


그림4. 프로젝트 생성창

그림 4와 같은 화면이 나타납니다. 프로젝트를 저장할 폴더 지정, 프로젝트 이름을 지정한후사용할 프로그래밍 언어를 선택하면 됩니다. pvdevelop에서 지원하는 프로그래밍 언어는  C/C++, Lua, Python 3가지 입니다. 이중 C/C++이 기본입니다.

저는 C/C++ 기본값으로 프로젝트를 만들었습니다. OK 버튼을 누르니 다음과 같은 화면이 나타나네요.




그림 5. 디자이너 모드

그림 5와 같이 기본적으로 디자이너 모드( UI용 위젯을 삽입할 수 있는 모드)에서 시작하네요.

그림 6. 에디터 모드

툴박스에 위치한 Editor 아이콘을 누르면 그림6 과 같은 화면이 나타납니다. 그림 3 비교해 보시면, 빈 공간에 Qt pro 파일과 유사한 내용이 들어가 있는 것을 알 수 있습니다.

우선은 그냥 컴파일 해보죠.(Action->Make)


그림 6.Make 실행 도스창

 




한마디로 Make 파일을 실해할 수 없으니 mingw32-make 파일을 설치해 달라는 내용이네요.

이것은 C/C++을 컴파일하기 위한 MinGW이 설치되어 있지 않기 떄문입니다. MinGW을 설치하는 방법은 MinGW를 설치하거나 Qt-SDK를 설치하면 해결 됩니다.

윂페이지에보면 MinGW 만 설치하여도 "fake-qmake" Qt Development package 설치없이 사용할 수 있다고 되어 있는데...(방법을 몰라 그런지 몰라서 겠죠.. 저는 에러가 발생하네요.)

여하튼 Qt-SDK 하나만 설치하면 됩니다. (MinGW 단독으로 설치할 필요 없습니다.),



2. Qt 설치

Qt Download 센터에서 원하는 버전의 Qt 설치파일을 다운로드하신 후 실행하십시오.
저는, 좀 소심한 편이라, pvbrowser Window 버전 설치 화면에 나타나 있는 버전을 다운로드 하였습니다. 여하튼 다른 버전이라도 특이사항은 없는 것 같습니다.


그림 7. Qt 설치화면

필요하신 분은 설정하시면 됩니다. 여하튼 저는 Next->SKIP 하겠습니다.

그림 8.Qt 설치화면


그림 9. Qt 설치-설정화면


MinGW Toolkit이 필요하기 때문에 Tools 에 있는 MinGW 4.9.2 를 체크하여 주시기 바랍니다.

저는 가끔가다. Qt 를 이용하기 때문에, 나머지는 기본 설정으로 설치하였습니다.


그림 10. Qt 설치완료 화면

설치도 되고 해서 한번 실행해 보았습니다.

그림 11. Qt 실행 화면

잘 설치 된 것 같네요..

그림 12.Qt 폴더 내용

설치 완료 후 Qt 폴더 용량을 확인해 보니 3.45G... 엄청나네요...
C/C++ 연습용으로 쓰기에는 너무 큰가?
여하튼 그림 12에 있는  "mingw32-make.exe" 파일을 위해, 이 큰 프로그램을 설치하였습니다..(물론 다른 이유도 있습니다...)



3. start_pvdevelop 편집

start_pvdevelop.bat 파일은 pvdevelop.exe 파일을 실행하기 위한 선처리 구문을 정의한 파일입니다.
이곳에서 C/C++ 컴파일러 위차, Pathon 위치와 같은 값들을 변경할 수 있습니다.
내용을 편집할 것이 떄문에 만일의 사태에 대비하기 위해 저는 복사본을 하나 만들었습니다.

그림 13. start_pvdevelop.bat

C/C++을 사용할 경우, 파일 내용중 ":environment_not_set" 밑에 있는 QTDIR, MINGWDIR 두 값만 수정하면 됩니다. 만약 Python을 이용하시기 경우에는 PYDIR값을 변경하셔야 합니다.
% ":start_pvdevelop" 밑에 있는 변수들은 그대로 사용하십시오.

기본 설정 내용은 다음과 같습니다.



변경한 설정 내용입니다. Qt, MinGW 관련 경로 같을 수정하였습니다.



자, 그럼 start_pvdevelop를 실행하여 pvdevelop를 실행 시킨 후, 처음에 만들어 준 프로젝트를 열어 Make 해보죠,


그림 14.Make 실행파일
그림 14와 같이 실행되면 정상입니다.

그림 15. Start Server

Action->Start server를 눌러 실행하니 그림 15와 같은 화면이 나오는 군요. 더불어 다음과 같은 화면도 나타납니다.

그림 16. 보안 경고 창


방화벽에서 차단하겠다고 해서 액세스 허용하라고 했습니다. 허용하지 않으면 pvbrowser에서 서버 화면이 나타날까요? (확인해 보세요...)


그림 17.pvbrowser 실행화면


pvbrowser 클라이언트 프로그램을 실행하여, 현재 localhost에서 실행되고 있는 서버에 접속한 화면입니다. 정상적으로 동작하네요...

자,  pvdevelop 관련 설정이 모두 끝났습니다. 이제 부터 원하는 SCADA, HMI 를 직접 만들어 보시기 바랍니다.

그냥, 단순하게 C/C++ (표준) 연습용으로 사용하여도 무방합니다.

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 소스 코드 입니다.



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

2016년 3월 26일 토요일

pvdevelop 프로젝트 -UI 편집 방법 - Qwt 위젯

기타 Qwt 위젯들....

Qwt 위젯에 대해 한번, 스윽 보고 가죠. 상세한 설정에 대해서는 설명하지 않습니다.
사용할때 꼭 필요한 연관 함수들의 정의만 살펴보도록 하죠, 위젯 설명 중 기타 함수들이라고 되어 있는 부분은 막상 적용할려고 해보면 반응하지 않거나, 별로 의미가 없다고 본인이 판단한 함수들입니다. 지극히 본인의 판단...(다른 분들은 꼭 필요한 기능일 수 도 있지만)


그림 1. Qwt 위젯


Qwt 위젯의 경우 속성창에서 배경색을 변경하여도 변경되지 않는다.

1. QwtKnob

그림 2. QwtKnob 위젯

knob 값 설정



knob 설정값을 집어 넣습니다.

knob 기호 설정



개인적으로 knobDot가 더 보기 좋네요. 기본은 knoLine입니다.


knob 범위 설정



knob 읽기 전용 설정



기타 knob 함수
  • qwtKnobSetOrientation(p, id, orientation);
    의미 없음

  • qwtKnobSetBorderWidth(p, id, width);
  • qwtKnobSetTotalAngle(p, id, angle);
  • qwtKnobSetKnobWidth(p, id, width);
  • qwtKnobSetMass(p, id, mass);



2. QwtSlider

그림 3. QwtSlider 위젯

 Slider 범위 설정



%뭐가 잘못 됬나 범위 설정이 되지않고 무조건 0, 100의 값이 튀어나온다. 아마 작동하지 않는 기능같다.

 

Slider 방향설정



Slider 스케일 위치




Slider 배경 모양



개인적으로 SliderBgBoth가 가장 마음에 든다.



Slider 값 설정




Slider 읽기 전용 설정





기타 Slider 함수들
  • qwtSliderSetMass(p, id, mass);
  • qwtSliderSetThumbLength(p,id,length);
  • qwtSliderSetThumbWidth(p,id,width);
  • qwtSliderSetBorderWidth(p,id,width);
  • qwtSliderSetMargins(p,id,x,y);




3.QwtCounter

그림 4. QwtCounter 위젯

Counter 최소값 설정 (초기값 : 0)



Counter 최대값 설정 (초기값 : 1)



Counter 증가값 설정 (초기값 : 0.001)



Counter 초기값 설정 (초기값 : 0)




Counter 버튼 증분 설정 (우측, 좌측 화살표 1 버튼)




Counter 버튼 증분 설정 (우측, 좌측 화살표 2 버튼)




기타 QwtCouter 함수
  • qwtCounterSetStepButton3(p,id,n);
  • qwtCounterSetNumButtons(p,id,n);
  • qwtCounterSetIncSteps(p,id,button,n);




4. QwtWheel

그림 5.QwtWheel 위젯

Wheel 방향 설정



Wheel 읽기 전용 설정



Wheel 관성 설정



Wheel 회전 각도 설정 (360도(초기값) 최대)



Wheel 눈금 마크 설정



Wheel 내부 간격 지정(기본값이 좋다)




Wheel 초기값 지정 (0 ~ 100 사이)




기타 QwtWheel 함수
  • qwtWheelSetViewAngle(p,id,angle);
  • qwtWheelSetWheelWidth(p,id,width);



5. QwtThermo

그림 6. QwtThermo 위젯

QwtThermo 위젯 표시 범위 설정



QwtThemo 범위 설정


이 값은 qwtThermoSetScale()함수에서 설정한 min, max 값과 동일하거나 작은 값이어야한다. 실제 qwtThermoSetValue() 함수에서 설정한 값이 이 값 영역을 벗어나면, (작으면 맨 밑바닥에 조금, 크면 전체적으로 색상이 나타난다.)

QwtThemo 값 설정



QwtThemo 위젯, 라벨이 나타나는 방향 설정



QwtThemo 색상 설정



QwtThemo 알람 색상 설정



QwtThemo 알람 Level 설정



QwtThemo 알람 Enable 설정



QwtThemo 파이프 폭 설정




기타 qwtThermo 함수
  • qwtThermoSetBorderWidth(p,id,width);
  • qwtThermoSetMargin(p,id,margin);



6. QwtAnalogClock

그림 7. QwtAnalogClock 위젯

AnalogClock 시간 설정






Value는 초 개념입니다. (5분 30초 인 경우 330)


AnalogClock 모드 설정


기계 지침뿐 아니라 Scale (숫자)도 회전 가능합니다.

AnalogClock 테두리 두께 설정




기타 AnalogClock 다음과 같은 함수들이 있습니다. 사용방법은 잘 모르겠네요.

qwtAnalogClockSetMass(p,id,mass);
qwtAnalogClockSetReadOnly(p,id,rdonly);
qwtAnalogClockSetFrameShadow(p,id,shadow);
qwtAnalogClockShowBackground(p,id,show);
qwtAnalogClockSetWrapping(p,id,wrap);
qwtAnalogClockSetScale(p,id,maxMajIntv,maxMinIntv,step);
qwtAnalogClockSetScaleArc(p,id,min,max);
qwtAnalogClockSetOrigin(p,id,orig);
qwtAnalogClockSetNeedle(p,id,which,r1,g1,b1,r2,g2,b2,r3,g3,b3);





7. QwtDial

그림 8. QwtDial 위젯

Dial 값 설정



Dial 범위 설정



최소, 최대, interval
interval은 값 증가분인 것 같다. 1씩 증가하는 경우 1을 입력하자.

Dial 스케일 설정



maxMajIntv, maxMinIntv, step 세가지 인자가 있지만 테스트 결과 maxMajIntv 값에는 반응하지 않는 것같다.
내가 원하는 것 기준으로 한다면, maxMinintv, step 이 두가지만 사용하여도 된다.
  • maxMinIntv (부 눈금 제어)
    주 눈금 사이에 나타나는 눈금  제어
    예) 주 눈금 간격이 25일때 만약 5개의 눈금을 나타내고 싶으면 5, 10개를 나타내고 싶으면 10
    (주의)  주 눈금 값을 눈금 간격으로 나눈 값이 정수와 같이 떨어져야( 25/10 = 2.5) 제대로된 눈금이 나타난다. 떨어지지 않으면 자동으로 설정값보다 큰 수량으로 나타난다.
  • Step 주 눈금 제어
    예) 만약 qwtDialSetRange()에서 설정한 범위가 0 ~ 100, 25 간격으로 주 눈금을 나타내고 싶으면 25 를 step 값에 넣어주면된다.


Dial ReadOnly



GUI 화면에서 마우스로 값을 변경하고 싶으면 0, 기본적으로 Readonly =1 로 설정되어GUI 마우스 조작으로 변경할 수 없다.


Dial Scale Arc 설정



시계방향으로 값이 증가하며 시작 위치는 하부, 즉 하부가 0입니다. 거의 사용하지 않을 것 같다.


기타 QwtDial 관련 함수들입니다.
  • qwtDialSetMass(p,id,mass);
  • qwtDialSetFrameShadow(p,id,shadow);
  • qwtDialShowBackground(p,id,show);
  • qwtDialSetLineWidth(p,id,width);
  • qwtDialSetMode(p,id,mode);
  • qwtDialSetWrapping(p,id,wrap);
  • qwtDialSetOrigin(p,id,orig);
  • qwtDialSetNeedle(p,id,which,r1,g1,b1,r2,g2,b2,r3,g3,b3);




8. QwtCompass

그림 9. QwtCompass 위젯



QwtCompass 관련 함수들입니다.
  • qwtCompassSetSimpleCompassRose(p,id,numThorns,numThornLevels,width);
  • qwtCompassSetMass(p,id,mass);
  • qwtCompassSetReadOnly(p,id,rdonly);
  • qwtCompassSetFrameShadow(p,id,shadow);
  • qwtCompassShowBackground(p,id,show);
  • qwtCompassSetLineWidth(p,id,width);
  • qwtCompassSetMode(p,id,mode);
  • qwtCompassSetWrapping(p,id,wrap);
  • qwtCompassSetScale(p,id,maxMajIntv,maxMinIntv,step);
  • qwtCompassSetScaleArc(p,id,min,max);
  • qwtCompassSetOrigin(p,id,orig);
  • qwtCompassSetNeedle(p,id,which,r1,g1,b1,r2,g2,b2,r3,g3,b3);
  • qwtCompassSetValue(p,id,value);


이상으로 Qwt 관련 위젯에 대해 알아보았습니다.
위젯 자체에는 함수가 정의되어 있지만, 막상 적용해 보면 동작하지 않는 것 처럼 보이는 함수들이 많이 있었습니다. 하지만 기본적인 기능은 충분히 수행할 수 있습니다.