緣起
將open62541作為中介軟體使用代替自定義資料的RPC,client通過訂閱valueChange來接收資料。使用時發現有一些問題:
- 前後兩次產生的資料相同時,不會觸發valueChange
- 如果資料更新較快,client會漏掉其中一些資料
探索
問題1:
其實不是問題,這是opcua的屬性,valueChange是根據取樣(sample)來生成的,並不是server執行了writeValue就會觸發事件。只能說這種模式不適合這種每次server產生結果都需要client響應的需求。(有一個小問題,是如何快速判斷數值變化的?基本型別可以判斷是否和原始資料不相等,對於複雜資料,如圖片,逐位元組比對?)
問題2:
還是和取樣模式相關,如果在兩次取樣之間資料多次變化是沒辦法感知的。那就修改取樣頻率:取樣頻率同時受server和client的影響,server決定範圍,client在範圍內決定最終結果。如果client設定為0,則採用server能實現的最快頻率。
心生一計
改進方式是將結果通過event傳送,設定為report模式。資料內容自己填充,相同的資料也不會被過濾掉;主動report,脫離取樣的限制(但響應時間還是受publish間隔的約束)。
event一般是簡單訊息的通知,承載傳遞資料的用法不算主流,或許能說明opcua的靈活性吧,不知道這是否存在潛在缺陷。這種模式和pub-sub很類似,不同點在於pub-sub利用的是udp的多播,event還是在server-client的形態下。pub-sub就是為實時性的需求設計的(類似dds),如果只是傳送資料,pub-sub會更適合。
使用event基本能滿足需求,但是響應的速度還是不夠快。網路上查詢說是opcua實時性只能到100ms左右,對於目前應用來說,時間上達不到要求。實際測試發現,publish間隔是關鍵因素,和sample一樣,server決定範圍,client決定最終結果。將publish間隔設定到1ms,從server傳送event到client接收的延遲終於能達標。官方建議不能低於5ms,但測試設定到0也沒什麼副作用,一開始考慮可能會cpu空轉高佔用,但並沒有。
小插曲
在測試過程中發現在windows平臺下,當publish間隔設定為1ms時,本地迴環網路還是會有16ms左右的延遲,相同程式碼在ubuntu下延遲可以穩定在1ms以內。一度為此糾結了很久,一度想換pub-sub甚至dds。後來用asio進行單純的tcp資料傳輸測試發現也會有相同的延遲,才確認這是平臺的原因(電腦個體差異,待驗證?)。Windows下的網路效率不如linux有耳聞,但差距有這麼大嗎?
補充
在使用qtopcua作為client的過程中,設定相同的引數(publish間隔通過QOpcUaMonitoringParameters設定),還是會有50-60ms的延遲。網上遍尋無果,官方文件不夠詳細(qtopcua只包含在收費嵌入式的開發套件中,不知道交錢的文件有沒有詳細些)。最後無奈啃起qtopcua的原始碼,收穫如下:opcua client執行迭代(UA_Client_run_iterate)才會響應event,qt是一個非同步的庫,qtopcua通過定時器來執行迭代,預設的超時時間就是50ms。通過QOpcUaProvider建立QOpcUaClient時,將超時時間設定放在backendProperties。正確的設定方式如下:
auto client = QOpcUaProvider::createClient("open62541", QVariantMap({"clientIterateIntervalMs", 1}));
server設定sample和publish的間隔範圍方式如下:
auto config = UA_Server_getConfig(server);
config->publishingIntervalLimits = UA_DurationRange{1.0, 24 * 60 * 60 * 1000};
config->samplingIntervalLimits = UA_DurationRange{1.0, 24 * 60 * 60 * 1000};
client設定publish間隔的方式如下:
auto request = UA_CreateSubscriptionRequest_default();
request.requestedPublishingInterval = 1.0;