上一篇文章《libiec61850在OpenSCADA系统中的使用》介绍了libiec61850和OpenSCADA系统的混合编译,编译是没有问题,但是做成模块的话运行起来还是会出现问题,其中最主要的问题就是库的链接,导致运行时函数找不到,原因出在C和C++函数相互调用的关系,libiec61850是C库,OpenSCADA是C++。
本文采用将libiec61850编译成动态库的方式供OpenSCADA系统调用,步骤如下:
动态库的编译安装
1. 修改libiec61850的 "lib61850\make\target_system.mk" 文件,编译选项添加fPIC
CFLAGS += -g CFLAGS += -fPIC
2. 修改libiec61850 的“lib61850\Makefile”文件,添加生成动态库的参数soname
$(DYN_LIB_NAME): $(LIB_OBJS) $(CC) $(LDFLAGS) -lpthread -shared -Wl,-soname -Wl,libiec61850.so -o $(DYN_LIB_NAME) $(LIB_OBJS) $(LDLIBS)
3. 编译生成动态库
$ make dynlib
生成 “lib61850\build\libiec61850.so”文件
4. 安装动态库到系统目录
$ /usr/bin/install -c lib61850/build/libiec61850.so /usr/lib/i386-linux-gnu/libiec61850.so
5. 修改OpenSCADA SP61850 module的 "Makefile.am" 文件,动态链接libiec61850.so
#!!! The module link flags spec_SP61850_la_LDFLAGS = -module -avoid-version -no-undefined -Llib61850/build -liec61850 $(SP61850_LDLAGS) ############ for lib61850 include header file and lib file ################## AM_CPPFLAGS = -Ilib61850/inc -Ilib61850/src/common -Ilib61850/src/mms/iso_presentation -Ilib61850/src/mms/iso_session -Ilib61850/src/mms/iso_cotp -Ilib61850/src/mms/iso_acse -Ilib61850/src/mms/iso_mms/common -Ilib61850/src/mms/iso_mms/client -Ilib61850/src/mms/iso_mms/server -Ilib61850/src/mms/iso_client -Ilib61850/src/mms/iso_server -Ilib61850/src/mms/asn1 -Ilib61850/src/iedcommon -Ilib61850/src/iedserver/mms_mapping -Ilib61850/src/iedserver/model -Ilib61850/src/iedserver -Ilib61850/src/iedclient -Ilib61850/src/hal -Ilib61850/src/hal/thread -Ilib61850/src/hal/socket -Ilib61850/src/goose #############################################################################
测试代码的编写
1. 修改“SP61850\module.h”头文件,添加libiec6180的常用头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ///////////////////////////////////////////////////////// extern "C" { #include "iec61850_server.h" #include "iso_server.h" #include "acse.h" #include "thread.h" #include <stdlib.h> #include <stdio.h> #include "static_model.h" /* import IEC 61850 device model created from SCL-File */ extern IedModel iedModel; } //////////////////////////////////////////////////////// ////C++类(class Lib : public TSpecial)内部添加属性 /////////////////////////////////////////////////////////// int running; IedServer iedServer; /////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////// extern "C" { #include "iec61850_server.h" #include "iso_server.h" #include "acse.h" #include "thread.h" #include <stdlib.h> #include <stdio.h> #include "static_model.h" /* import IEC 61850 device model created from SCL-File */ extern IedModel iedModel; } //////////////////////////////////////////////////////// ////C++类(class Lib : public TSpecial)内部添加属性 /////////////////////////////////////////////////////////// int running; IedServer iedServer; ///////////////////////////////////////////////////////////
2. 修改“SP61850\module.cpp”文件,控制服务器的启动与停止
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //////// 启动服务器 void Lib::modStart( ) //61850 server. if(! running) { iedServer = IedServer_create(&iedModel); /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); /* Instruct the server that we will be informed if a clients writes to a * certain variables we are interested in. */ IedServer_observeDataAttribute(iedServer, IEDMODEL_Device1_DSCH1_NamPlt_vendor, observerCallback); IedServer_observeDataAttribute(iedServer, IEDMODEL_Device1_DSCH1_NamPlt_swRev, observerCallback); if (!IedServer_isRunning(iedServer)) { mess_err(nodePath().c_str(),_("Start 61850 Server error.")); IedServer_destroy(iedServer); } else { running = 1; } } ////// 停止服务器 void Lib::modStop( ) // if(running) { /* stop MMS server - close TCP server socket and all client sockets */ IedServer_stop(iedServer); /* Cleanup - free all resources */ IedServer_destroy(iedServer); running = 0; } |
//////// 启动服务器 void Lib::modStart( ) //61850 server. if(! running) { iedServer = IedServer_create(&iedModel); /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); /* Instruct the server that we will be informed if a clients writes to a * certain variables we are interested in. */ IedServer_observeDataAttribute(iedServer, IEDMODEL_Device1_DSCH1_NamPlt_vendor, observerCallback); IedServer_observeDataAttribute(iedServer, IEDMODEL_Device1_DSCH1_NamPlt_swRev, observerCallback); if (!IedServer_isRunning(iedServer)) { mess_err(nodePath().c_str(),_("Start 61850 Server error.")); IedServer_destroy(iedServer); } else { running = 1; } } ////// 停止服务器 void Lib::modStop( ) // if(running) { /* stop MMS server - close TCP server socket and all client sockets */ IedServer_stop(iedServer); /* Cleanup - free all resources */ IedServer_destroy(iedServer); running = 0; }
3. 注册数据监听函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | extern "C" { void observerCallback(DataAttribute* dataAttribute) { if (dataAttribute == IEDMODEL_Device1_DSCH1_NamPlt_vendor) { printf("GGIO.NamPlt.vendor changed to %s\n", MmsValue_toString(dataAttribute->mmsValue)); } else if (dataAttribute == IEDMODEL_Device1_DSCH1_NamPlt_swRev) { printf("GGIO.NamPlt.swRef changed to %s\n", MmsValue_toString(dataAttribute->mmsValue)); } } } |
extern "C" { void observerCallback(DataAttribute* dataAttribute) { if (dataAttribute == IEDMODEL_Device1_DSCH1_NamPlt_vendor) { printf("GGIO.NamPlt.vendor changed to %s\n", MmsValue_toString(dataAttribute->mmsValue)); } else if (dataAttribute == IEDMODEL_Device1_DSCH1_NamPlt_swRev) { printf("GGIO.NamPlt.swRef changed to %s\n", MmsValue_toString(dataAttribute->mmsValue)); } } }
4. 生成测试数据,修改数据节点
1 2 3 4 5 6 7 8 9 10 11 12 | ////////////////////for test///////////////////////////// if(running) { MmsValue* td = MmsValue_newIntegerFromInt32 (200); IedServer_updateAttributeValue(iedServer, IEDMODEL_Device1_DSCH1_SchdSt_stVal, td); MmsValue_delete(td); td = MmsValue_newIntegerFromInt32 (202); IedServer_updateAttributeValue(iedServer, IEDMODEL_Device1_DSCH1_Health_stVal, td); MmsValue_delete(td); } //////////////////////////////////////////////// |
////////////////////for test///////////////////////////// if(running) { MmsValue* td = MmsValue_newIntegerFromInt32 (200); IedServer_updateAttributeValue(iedServer, IEDMODEL_Device1_DSCH1_SchdSt_stVal, td); MmsValue_delete(td); td = MmsValue_newIntegerFromInt32 (202); IedServer_updateAttributeValue(iedServer, IEDMODEL_Device1_DSCH1_Health_stVal, td); MmsValue_delete(td); } ////////////////////////////////////////////////
系统测试
1. 编译SP61850 模块
$cd openscada-0.8.0.5/src/moduls/special/SP61850 $ make clean; make $ ~/project/openscada-0.8.0.5/src/moduls/special/SP61850$ ldd .libs/spec_SP61850.so linux-gate.so.1 => (0x00542000) libiec61850.so => /usr/lib/i386-linux-gnu/libiec61850.so (0x007e7000) libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0x00bc0000) libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0x00e84000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00110000) libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0x00273000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0x0028f000) /lib/ld-linux.so.2 (0x00af2000) $ sudo make install
2. 运行测试
$ sudo openscada
3. 利用61850客户端软件进行连接确认,例如IEDScout,连接[192.168.1.160:102]
参考资料
1. http://libiec61850.com/api/
2. http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html