1 #include "opcuabridgeclient.h" 2 #include "opcuabridgediscoveryclient.h" 4 #include "logging/logging.h" 6 #include "uptane/secondaryconfig.h" 13 #include <unordered_set> 15 #include <boost/filesystem.hpp> 16 #include <boost/functional/hash.hpp> 22 SelectEndPoint::DiscoveredEndPointCacheEntryHash::result_type SelectEndPoint::DiscoveredEndPointCacheEntryHash::
23 operator()(SelectEndPoint::DiscoveredEndPointCacheEntryHash::argument_type
const& e)
const {
30 bool SelectEndPoint::DiscoveredEndPointCacheEntryEqual::operator()(
31 const SelectEndPoint::DiscoveredEndPointCacheEntryEqual::argument_type& lhs,
32 const SelectEndPoint::DiscoveredEndPointCacheEntryEqual::argument_type& rhs)
const {
33 return (lhs.serial == rhs.serial && lhs.hwid == rhs.hwid);
36 thread_local SelectEndPoint::DiscoveredEndPointCache SelectEndPoint::discovered_end_points_cache_;
39 if (discovered_end_points_cache_.empty()) {
40 if (!sconfig.opcua_lds_url.empty()) {
41 considerLdsRegisteredEndPoints(sconfig.opcua_lds_url);
43 discovery::Client discovery_client(OPCUA_DISCOVERY_SERVICE_PORT);
44 for (
const discovery::Client::discovered_endpoint& ep : discovery_client.getDiscoveredEndPoints()) {
45 if (ep.type == discovery::kLDS) {
46 considerLdsRegisteredEndPoints(makeOpcuaServerUri(ep.address));
47 }
else if (ep.type == discovery::kNoLDS) {
48 std::string opcua_server_url = makeOpcuaServerUri(ep.address);
49 if (endPointConfirmed(opcua_server_url, sconfig)) {
50 discovered_end_points_cache_.insert(DiscoveredEndPointCacheEntry{
54 LOG_INFO <<
"OPC-UA discover secondary: unsupported response from " << ep.address;
59 auto ep_it = discovered_end_points_cache_.find(DiscoveredEndPointCacheEntry{
61 if (ep_it != discovered_end_points_cache_.end()) {
62 url_ = ep_it->opcua_server_url;
66 bool SelectEndPoint::endPointConfirmed(
const std::string& opcua_server_url,
68 auto probe_opcua_server_config = Client(opcua_server_url).recvConfiguration();
69 return (probe_opcua_server_config.getSerial() ==
Uptane::EcuSerial(sconfig.ecu_serial) &&
73 std::string SelectEndPoint::makeOpcuaServerUri(
const std::string& address)
const {
74 return "opc.tcp://[" + address +
"]:" + std::to_string(OPCUABRIDGE_PORT);
77 void SelectEndPoint::considerLdsRegisteredEndPoints(
const std::string& opcua_lds_url) {
78 UA_ApplicationDescription* applicationDescriptionArray =
nullptr;
79 size_t applicationDescriptionArraySize = 0;
83 UA_ClientConfig config = UA_ClientConfig_default;
84 config.timeout = OPCUABRIDGE_CLIENT_SYNC_RESPONSE_TIMEOUT_MS;
85 config.logger = &opcuabridge::BoostLogOpcua;
87 UA_Client* client = UA_Client_new(config);
88 retval = UA_Client_findServers(client, opcua_lds_url.c_str(), 0,
nullptr, 0,
nullptr,
89 &applicationDescriptionArraySize, &applicationDescriptionArray);
90 UA_Client_delete(client);
93 if (retval != UA_STATUSCODE_GOOD) {
94 LOG_ERROR <<
"OPC-UA LDS dicovery request: unable to get registered servers on " << opcua_lds_url;
98 for (
size_t i = 0; i < applicationDescriptionArraySize; i++) {
100 UA_ApplicationDescription* description = &applicationDescriptionArray[i];
101 if (description->applicationType != UA_APPLICATIONTYPE_SERVER) {
104 if (description->discoveryUrlsSize == 0) {
105 LOG_INFO <<
"OPC-UA server " << std::string(reinterpret_cast<char*>(description->applicationUri.data),
106 description->applicationUri.length)
107 <<
" does not provide any discovery urls";
111 UA_ClientConfig config = UA_ClientConfig_default;
112 config.timeout = OPCUABRIDGE_CLIENT_SYNC_RESPONSE_TIMEOUT_MS;
113 config.logger = &opcuabridge::BoostLogOpcua;
114 UA_Client* client = UA_Client_new(config);
116 std::string discovery_url(reinterpret_cast<char*>(description->discoveryUrls[0].data),
118 description->discoveryUrls[0].length);
120 UA_EndpointDescription* endpointArray =
nullptr;
121 size_t endpointArraySize = 0;
122 retval = UA_Client_getEndpoints(client, discovery_url.c_str(), &endpointArraySize, &endpointArray);
124 if (retval != UA_STATUSCODE_GOOD) {
125 UA_Client_disconnect(client);
126 UA_Client_delete(client);
130 for (
size_t j = 0; j < endpointArraySize; j++) {
132 UA_EndpointDescription* endpoint = &endpointArray[j];
133 std::string opcua_server_url =
134 std::string(reinterpret_cast<char*>(endpoint->endpointUrl.data), endpoint->endpointUrl.length);
135 auto opcua_server_config = Client(opcua_server_url).recvConfiguration();
136 discovered_end_points_cache_.insert(DiscoveredEndPointCacheEntry{
137 opcua_server_config.getSerial(), opcua_server_config.getHwId(), opcua_server_url});
139 UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
140 UA_Client_delete(client);
142 UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize,
143 &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
146 Client::Client(
const SelectEndPoint& selector) noexcept : Client(selector.getUrl()) {}
148 Client::Client(
const std::string& url) noexcept {
149 UA_ClientConfig config = UA_ClientConfig_default;
150 config.timeout = OPCUABRIDGE_CLIENT_SYNC_RESPONSE_TIMEOUT_MS;
151 config.logger = &opcuabridge::BoostLogOpcua;
152 client_ = UA_Client_new(config);
153 UA_Client_connect(client_, url.c_str());
157 UA_Client_disconnect(client_);
158 UA_Client_delete(client_);
161 Client::operator bool()
const {
return (UA_Client_getState(client_) != UA_CLIENTSTATE_DISCONNECTED); }
163 Configuration Client::recvConfiguration()
const {
164 Configuration configuration;
165 if (UA_Client_getState(client_) != UA_CLIENTSTATE_DISCONNECTED) {
166 configuration.ClientRead(client_);
168 return configuration;
171 VersionReport Client::recvVersionReport()
const {
172 VersionReport version_report;
173 if (UA_Client_getState(client_) != UA_CLIENTSTATE_DISCONNECTED) {
174 version_report.ClientRead(client_);
176 return version_report;
179 OriginalManifest Client::recvOriginalManifest()
const {
180 OriginalManifest original_manifest;
181 if (UA_Client_getState(client_) != UA_CLIENTSTATE_DISCONNECTED) {
182 original_manifest.ClientRead(client_);
184 return original_manifest;
187 bool Client::sendMetadataFiles(std::vector<MetadataFile>& files)
const {
188 MetadataFiles metadatafiles;
190 if (UA_Client_getState(client_) != UA_CLIENTSTATE_DISCONNECTED) {
191 std::random_device rd;
192 std::mt19937 gen(rd());
193 std::uniform_int_distribution<> dis(1, INT_MAX);
194 metadatafiles.setGUID(dis(gen));
195 metadatafiles.setNumberOfMetadataFiles(files.size());
196 metadatafiles.ClientWrite(client_);
198 for (
auto f_it = files.begin(); f_it != files.end(); ++f_it) {
199 f_it->setGUID(metadatafiles.getGUID());
200 if (UA_STATUSCODE_GOOD != f_it->ClientWrite(client_)) {
206 if (!retval && UA_Client_getState(client_) != UA_CLIENTSTATE_DISCONNECTED) {
207 metadatafiles.setNumberOfMetadataFiles(0);
208 metadatafiles.ClientWrite(client_);
213 bool Client::syncDirectoryFiles(
const fs::path& repo_dir)
const {
214 if (UA_Client_getState(client_) == UA_CLIENTSTATE_DISCONNECTED) {
221 if (UA_STATUSCODE_GOOD != file_list.ClientRead(client_)) {
225 opcuabridge::FileUnorderedSet file_unordered_set;
226 opcuabridge::UpdateFileUnorderedSet(&file_unordered_set, file_list);
229 fs::recursive_directory_iterator repo_dir_it_end, repo_dir_it(repo_dir);
230 for (; repo_dir_it != repo_dir_it_end; ++repo_dir_it) {
231 const fs::path& ent_path = repo_dir_it->path();
232 if (fs::is_regular_file(ent_path)) {
233 fs::path rel_path = fs::relative(ent_path, repo_dir);
234 if (file_unordered_set.count(reinterpret_cast<opcuabridge::FileSetEntry>(rel_path.c_str())) == 0) {
235 file_data.setFilePath(rel_path);
236 if (UA_STATUSCODE_GOOD != file_data.ClientWriteFile(client_, ent_path)) {
243 file_list.getBlock().resize(1);
244 file_list.getBlock()[0] =
'\0';
245 return ((UA_STATUSCODE_GOOD == file_list.ClientWrite(client_)) && retval);