基于第三方开源库的OPC服务器开发指南(2)——LightOPC的编译及部署
前文已經說過,OPC基于微軟的DCOM技術,所以開發OPC服務器我們要做的事情就是開發一個基于DCOM的EXE文件、一個代理/存根文件,然后就是寫一個OPC客戶端測試一下我們的服務器了。對于第一項工作,我們需要借助一個俄羅斯大神開源的OPC開發庫——LightOPC展開。不過,從LightOPC的主網站看,這個庫已經許久沒更新了,時間定格在了2004年:
http://www.ipi.ac.ru/lab43/lopc-en.html
雖然很久沒有更新了,但這個庫在64位系統大行其道的今天依然能用并且好用,“最新”版本的下載地址為:
ftp://ftp.ipi.ac.ru/pub/LightOPC/lightopc-0.888-0313.tar.gz
由于LightOPC使用了第三方的日志庫unilog,我們還需要把配套版本的unilog庫的源碼下載下來:
ftp://ftp.ipi.ac.ru/pub/LightOPC/unilog-0.55-1227.tgz
兩個源碼文件分別解壓,然后打開VS:“文件”->“新建”->“項目”->“Win32項目”,建立unilog的源碼工程:
“確定”->“下一步”->“下一步”,然后選擇建立“DLL”工程并勾選“空項目”:
工程建立完畢后,我們把剛才解壓后得到的unilog的源碼添加到工程中,要添加的頭文件和C文件參見如下列表:
頭文件 condsb.h、logger.h、lwsynch.h、rwlock.h、unilog.h
C文件 advini.c、dllmain.c、lwsynch.c、raserr.c、reglog.c、sockerr.c、ssperr.c、unilog.c
其中,logger.h并不包含在我們剛才下載的unilog源碼中,還需要我們再單獨下載。這也是網上資源存在的一個普遍現象,無論是Blog文章還是各種下載資源,有相當一部分存在這樣那樣的問題,要么是文章提供的操作步驟不完整或者講得不清不楚,要么是提供的源碼缺少某個函數,要么干脆就是提供的源碼存在邏輯錯誤根本無法編譯或者編譯成功卻無法正常執行,要么就像現在這種情況提供的源碼缺少某個文件……等等不一而足。按理說,程序猿應該是這個世界上做事最嚴謹的群體之一,分享技術時至少應該內部測試無誤后再發布,這應該是這個群體最基本的工作守則之一,否則你分享它干嘛呢,但結果卻總是差強人意。只能說無論哪個群體,優秀的畢竟還是少數,大部分只能是最普通的那些。對于缺少的這個文件我們需要在github上找,地址如下:
https://github.com/jonathanxavier/IndigoSCADA/tree/4008ccc08d091bdd78a9b409f390ee710d09b359/src/drivers/opc_server_da/lo-0.888/unilog
你可以打開查閱這個文件的內容并復制,然后自己再手動創建一個logger.h文件把內容復制進來,這是最省事的方法。不想這樣做的話你也可以clone整個工程或者使用SVN獲取單個unilog目錄下的所有文件。做完這項工作后我們還需要手動調整幾個地方:
1、lwsynch.h文件,將頭部兩個#include語句改為對本地頭文件的引用:
#include <rwlock.h>
#include <condsb.h>
改為
#include "rwlock.h"
#include "condsb.h"
2、unilog.h文件找到“# define UL_PUBLIC”一行,放開“UL_PUBLIC”宏定義,也就是去掉注釋符號,使能該宏:
1 #ifndef UL_PUBLIC 2 # define UL_PUBLIC /*extern*/ 3 /* This silly thing is not required when we use .DEF file 4 # define UL_PUBLIC __declspec(dllexport) 5 # define UL_PUBLIC __attribute__((dllexport)) */ 6 /* This one may be useful for caller optimization 7 # define UL_PUBLIC __declspec(dllimport) */ 8 #endif /*UL_PUBLIC*/ 9 10 /* 上面是原來的代碼,下面是改后的 */ 11 #ifndef UL_PUBLIC 12 #define UL_PUBLIC /*extern*/ 13 /* This silly thing is not required when we use .DEF file */ 14 #define UL_PUBLIC __declspec(dllexport) 15 /* # define UL_PUBLIC __attribute__((dllexport)) */ 16 /* This one may be useful for caller optimization 17 # define UL_PUBLIC __declspec(dllimport) */ 18 #endif /*UL_PUBLIC*/這樣做的目的是建立unilog.dll文件的導出符號文件,以備LightOPC編譯之用。
?
3、工程屬性的“附加依賴項”增加“ws2_32.lib”庫文件:
4、在解決方案的根目錄下建立兩個文件夾“bin”、“lib”,用于存放生成的LIB和DLL文件:
5、“unilog”工程屬性增加“后期生成事件”,以便VS在編譯完成后自動將生成的LIB和DLL文件分別復制到剛才建立的文件夾中:
copy $(SolutionDir)$(Configuration)\$(ProjectName).dll $(SolutionDir)bin\$(ProjectName).dll copy $(SolutionDir)$(Configuration)\$(ProjectName).lib $(SolutionDir)lib\$(ProjectName).lib6、調整“工程屬性”的“字符集”為“未設置” ,如果不調整該項,使用缺省值“使用Unicode字符集”將導致系統日志顯示為亂碼,無法用“Windows事件查看器”查看OPC服務器運行情況:
?
至此,所有工作調整完成,編譯生成LIB和DLL即可,最終我們會在剛才建立的兩個文件夾下找到生成的文件。接下來開始編譯LightOPC庫。
由于LightOPC的開發環境是VC6,所以我們不需要再單獨為此建立VS工程了,只需雙擊LightOPC文件夾下的VC6工程文件“LightOPC.dsw”,然后用機器上已安裝的VS2010或VS2015打開后任憑VS自動轉換為當前版本的VS工程即可。在打開的LightOPC工程中,我們刪掉工程對“unilog.lib”和“stunilog.lib”兩個文件的引用:
按住“CTRL”鍵,鼠標左鍵點選兩個LIB文件后,鼠標右鍵菜單選擇“移除”后即可完成該操作。接著我們需要下載opc頭文件,有兩個地方可以下載,一個是gtihub:
https://github.com/jonathanxavier/IndigoSCADA/tree/4008ccc08d091bdd78a9b409f390ee710d09b359/src/drivers/opc_server_da/lo-0.888/opc
另一個是cnblogs:https://www.cnblogs.com/opcconnect/archive/2010/12/20/1911604.html,在這里博主提供的頭文件比github多了生成DCOM代理/存根文件必須的IDL文件:
http://files.cnblogs.com/opcconnect/OpcInclude.zip
其實你如果理解了DCOM開發,不需要IDL也可以。因為github下載的頭文件中除了dlldata.c,其它生成DCOM代理/存根所需的文件都已經有了,而dlldata.c與我們前面的DCOM樣例生成的dlldata.c幾乎完全一樣,我們可以直接把dlldata.c復制過來,稍微改動就可以直接使用。
下載完畢后,在VS解決方案的根目錄下建立一個“opc”目錄,把這些文件復制到該目錄下:
然后調整LightOPC的工程屬性:
最后還是增加“后期生成事件”,把生成的兩個文件分別復制到我們先前建立的LIB和BIN文件夾下:
copy $(SolutionDir)$(Configuration)\lightopc.dll $(SolutionDir)bin\lightopc.dll copy $(SolutionDir)$(Configuration)\lightopc.lib $(SolutionDir)lib\lightopc.lib以上配置的前提是“unilog”和“opc”兩個文件夾均與“LightOPC”工程在同一個解決方案根目錄下,否則你必須根據你自己的路徑設置調整工程屬性的“包含目錄”和“庫目錄”。以上配置完成后,直接編譯即可生成相應的LIB和DLL文件。
以上完成的是VS2010下的32位DEBUG版本LightOPC庫的編譯,如果我們想使用VS2015編譯成64位的LightOPC庫,除了以上工作外還需要多做一些額外的工作。首先鼠標右鍵點選LightOPC的工程屬性,然后選擇“配置管理器”:
選擇“新建”,按照下圖所示進行設置,指定VS建立64位的OPC庫:
由于我們指定建立64位的庫,所以VS的生成輸出目錄中間多了一個平臺目錄,因此上面所有涉及目錄的配置都需要在“$(SolutionDir)”和“$(Configuration)”之間增加“$(Platform)\”。比如DLL文件的輸出目錄改為:
“后期生成事件”的兩條復制指令則改為:
copy $(SolutionDir)$(Platform)\$(Configuration)\lightopc.dll $(SolutionDir)bin\lightopc.dll copy $(SolutionDir)$(Platform)\$(Configuration)\lightopc.lib $(SolutionDir)lib\lightopc.lib所有與此有關的設置都按如上方式調整。最后,打開“loserv.h”文件,找到第147行如下一句:
int (*wstrncmp)(const loWchar *, const loWchar *, unsigned);將其改為:
int (*wstrncmp)(const loWchar *, const loWchar *, size_t);更改這一句的目的是解決編譯器報“類型轉換”錯誤的問題。至此,64位庫的調整也已完畢,直接編譯就能得到64位OPC庫。
接下來的工作是編譯LightOPC自帶的OPC服務器樣例,按照同樣的方法我們把“samp_exe”工程添加到我們的解決方案中:
然后移除掉工程原先引入的“unilog.lib”和“lightopc.lib”兩個庫文件。接著按照圖示配置工程屬性:
1、“包含目錄”和“庫目錄”分別為:“..\unilog;..\opc;$(IncludePath)”、“..\lib;$(LibraryPath)”;
2、“鏈接器”的“附加依賴項”增加“unilog.lib”和“lightopc.lib”兩個庫文件;
3、“鏈接器”的“輸出文件”改為:
$(SolutionDir)$(Configuration)\lopcsamp.exe4、“后期生成事件”增加:
copy $(SolutionDir)$(Configuration)\lopcsamp.exe $(SolutionDir)bin\lopcsamp.exe如此即可成功編譯生成“lopcsamp.exe”文件,這個文件就是OPC服務器。如果使用VS2015編譯該工程的話,需要調整的工程屬性與LightOPC工程一致,另外代碼部分還需要修改“sample.cpp”的第42行:
unilog *log; /* logging entry */這里聲明的這個變量產生了重定義沖突,需要將其名稱改為任意其它名稱而不是現在的“log”,比如“logg”,然后我們把所有使用該變量的語句都調整為改后的名稱再編譯即可成功。
到這里我們已經完成了EXE組件的開發,接下來就是得到OPC的代理/存根文件了。繼續在剛才的解決方案下,新建一個“Win32項目”,項目名稱為“OPCDASrvProxyStupGenerator”,隨便選擇一個項目類型,建立一個“空項目”,然后我們把“opc”目錄下的“opcda.idl”文件添加到工程中,鼠標右鍵選擇該文件,右鍵菜單選擇“編譯”,即可生成建立OPC代理/存根所需的源文件:
接下來就是繼續在VS的解決方案中建立OPC代理/存根動態庫工程了,依然是“Win32項目”,項目名稱為“OPCDASrvProxyStub”,項目類型為“DLL”、“空項目”。工程建立完畢,我們接著把剛才生成的那幾個文件添加到工程中:
opcda_h.h
dlldata.c
opcda_i.c
opcda_p.c
同時,我們把先前編寫iDCOMTestSrv時VS幫我們生成的“iDCOMTestSrvps.def”復制到當前工程目錄下,并將其重命名為“OPCDASrvProxyStub.def”,然后將其添加到工程中:
接著配置工程屬性:
1、“C/C++”->“預處理器定義”改為“WIN32;_DEBUG;REGISTER_PROXY_DLL;%(PreprocessorDefinitions)”,在這里重點是“REGISTER_PROXY_DLL”宏,它顯式地告訴VS要生成代理/存根DLL文件,而不是普通的DLL;
2、“鏈接器”的“附加依賴項”改為“rpcns4.lib;rpcrt4.lib;%(AdditionalDependencies)”,添加的兩個庫文件為RPC支持庫,這是遠程訪問DCOM組件必須要用到的庫;
3、“鏈接器”的“模塊定義文件”錄入“OPCDASrvProxyStub.def”;
4、“后期生成事件”增加:
copy $(SolutionDir)$(Configuration)\$(ProjectName).dll $(SolutionDir)bin\$(ProjectName).dll配置完成即可編譯成功,順利得到OPC的代理/存根文件。至此我們已經成功得到OPC樣例服務器相關的所有文件,接下來就是將LightOPC庫提供的測試服務器部署到遠程機器上了。首先把OPC服務器相關的程序文件復制到遠程機器上:
lopcsamp.exe、OPCDASrvProxyStub.dll、lightopc.dll、unilog.dll
然后注冊OPC服務和代理/存根文件。在OPC服務器文件所在的目錄下以管理員身份打開控制臺,先后輸入如下兩條指令進行注冊:
lopcsamp.exe/r
c:\Windows\system32\regsvr32.exe OPCDASrvProxyStub.dll
在這里還要再次提醒,依然是32位庫和64位庫的問題,如果你生成的是32位的OPC服務器組件,那么注冊代理/存根文件時,要把上面的“c:\Windows\system32”換成“c:\Windows\SysWOW64”才可。我注冊的是VS2015版本的64位Release庫,所以使用的是64位版本的“regsvr32.exe”。“lopcsamp.exe”文件的注冊不需要區分這個問題。
兩條指令的執行結果如下圖所示:
至此LightOPC提供的樣例服務器注冊成功。該服務器的名字我們可以在源碼中找到,在VS的“解決方案資源管理器”一欄,打開“exe_sample”工程的源文件“sample.cpp”,在“WinMain()”函數之上定義了該樣例服務器的名稱:
1 …… …… 2 3 /*************************************************************************** 4 EXE-specefic stuff 5 ***************************************************************************/ 6 7 const char eClsidName[] = "LightOPC Sample server (exe)"; //* OPC服務器名稱 8 const char eProgID[] = "OPC.LightOPC-exe"; //* OPC在注冊表中的注冊ID 9 10 HMODULE server_module = 0; 11 static int mymine(HINSTANCE hInstance, int argc, char *argv[]); 12 13 extern "C" 14 int APIENTRY WinMain(HINSTANCE hInstance, 15 HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 16 { 17 static char *argv[3] = { "fake.exe", NULL, NULL }; 18 argv[1] = lpCmdLine; 19 return mymine(hInstance, 2, argv); 20 } 21 22 extern "C" int main(int argc, char *argv[]) 23 { 24 return mymine(GetModuleHandle(NULL), argc, argv); 25 } 26 27 …… ……?
知道了名稱,我們就可以打開“組件服務”管理器配置該組件的相關屬性了。控制臺輸入:mmc comexp.msc(32位庫則是“mmc comexp.msc /32”),打開“組件服務”管理器,找到“LightOPC Sample server (exe)”,鼠標右鍵打開組件“屬性”窗口,按如下圖示進行配置:
其它未涉及的全部采用“默認”即可。關于組件服務的其它設置選項以及如何分配用戶,請參考該指南第一篇《基于第三方開源庫的OPC服務器開發指南(1)——OPC與DCOM》。至此,服務器部署完畢。接下來就需要我們編寫一個OPC客戶端測試一下這個服務器了,詳細說明請閱《基于第三方開源庫的OPC服務器開發指南(3)——OPC客戶端》。
?
本文源碼下載地址:
https://github.com/Neo-T/OPCDASrvBasedOnLightOPC
posted on 2019-05-08 13:57 Neo-T 閱讀(...) 評論(...) 編輯 收藏轉載于:https://www.cnblogs.com/neo-T/p/OPCSrvExample-2.html
總結
以上是生活随笔為你收集整理的基于第三方开源库的OPC服务器开发指南(2)——LightOPC的编译及部署的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 波兰StareOborzyska是哪个城
- 下一篇: 想知道:成都市文昌路看守所在哪?