緣起
將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;