Skip to content

Commit df8b93a

Browse files
author
Viacheslav Kalenikov
committed
generic opcua client: device local id is opcua server application URI; device name is opcua server application name;
1 parent b1249ba commit df8b93a

6 files changed

Lines changed: 152 additions & 26 deletions

File tree

modules/opcua_generic_client_module/src/opcua_generic_client_module_impl.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,39 @@ DevicePtr OpcUaGenericClientModule::onCreateDevice(const StringPtr& connectionSt
7676
std::string host;
7777
std::string hostType;
7878
int port;
79-
formConnectionString(connectionString, configPtr, host, port, hostType);
79+
const auto opcuaConnStr = formConnectionString(connectionString, configPtr, host, port, hostType);
8080

8181
std::scoped_lock lock(sync);
8282

83-
DevicePtr device(createWithImplementation<IDevice, OpcuaGenericClientDeviceImpl>(context, parent, configPtr));
83+
std::shared_ptr<OpcUaClient> client;
84+
std::string deviceName;
85+
std::string deviceLocalId;
86+
87+
try
88+
{
89+
client = std::make_shared<OpcUaClient>(OpcUaEndpoint(opcuaConnStr,
90+
configPtr.getPropertyValue(PROPERTY_NAME_OPCUA_USERNAME),
91+
configPtr.getPropertyValue(PROPERTY_NAME_OPCUA_PASSWORD)));
92+
const auto desc = client->readApplicationDescription();
93+
94+
deviceName = desc.name.empty() ? GENERIC_OPCUA_CLIENT_DEVICE_NAME : desc.name;
95+
deviceLocalId = desc.uri.empty() ? "" : desc.uri;
96+
std::replace(deviceLocalId.begin(), deviceLocalId.end(), '/', '-');
97+
}
98+
99+
catch (const OpcUaException& e)
100+
{
101+
switch (e.getStatusCode())
102+
{
103+
case UA_STATUSCODE_BADUSERACCESSDENIED:
104+
case UA_STATUSCODE_BADIDENTITYTOKENINVALID:
105+
DAQ_THROW_EXCEPTION(AuthenticationFailedException, e.what());
106+
default:
107+
DAQ_THROW_EXCEPTION(NotFoundException, e.what());
108+
}
109+
}
110+
111+
DevicePtr device(createWithImplementation<IDevice, OpcuaGenericClientDeviceImpl>(context, parent, configPtr, client, deviceLocalId, deviceName));
84112

85113
// Set the connection info for the device
86114
DeviceInfoPtr deviceInfo = device.getInfo();
@@ -162,11 +190,13 @@ DeviceInfoPtr OpcUaGenericClientModule::populateDiscoveredDevice(const MdnsDisco
162190

163191
cap.setConnectionType("TCP/IP");
164192
cap.setPrefix(DaqOpcUaGenericDevicePrefix);
165-
cap.setProtocolVersion(discoveredDevice.getPropertyOrDefault("protocolVersion", ""));
193+
cap.setProtocolVersion("");
166194
if (discoveredDevice.servicePort > 0)
167195
cap.setPort(discoveredDevice.servicePort);
168196

169-
return populateDiscoveredDeviceInfo(DiscoveryClient::populateDiscoveredInfoProperties, discoveredDevice, cap, createDeviceType());
197+
auto devInfo = populateDiscoveredDeviceInfo(DiscoveryClient::populateDiscoveredInfoProperties, discoveredDevice, cap, createDeviceType());
198+
devInfo.asPtr<IDeviceInfoConfig>().setName(discoveredDevice.serviceInstance);
199+
return devInfo;
170200
}
171201

172202
StringPtr OpcUaGenericClientModule::formConnectionString(const StringPtr& connectionString, const PropertyObjectPtr& config, std::string& host, int& port, std::string& hostType)

shared/libraries/opcua/opcuaclient/include/opcuaclient/opcuaclient.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,19 @@ class UaClientFactory
9595
class OpcUaClient
9696
{
9797
public:
98+
struct ApplicationDescription{
99+
std::string name;
100+
std::string uri;
101+
};
102+
98103
explicit OpcUaClient(const std::string& url);
99104
explicit OpcUaClient(const OpcUaEndpoint& endpoint);
100105
~OpcUaClient();
101106

102107
static constexpr size_t CONNECTION_TIMEOUT_SECONDS = 10;
103108

104109
void initialize();
110+
ApplicationDescription readApplicationDescription();
105111
void connect();
106112
void disconnect(bool doClear = true);
107113
void clear();

shared/libraries/opcua/opcuaclient/src/opcuaclient.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,42 @@ UA_Client* UaClientFactory::build()
195195
return client;
196196
}
197197

198+
OpcUaClient::ApplicationDescription OpcUaClient::readApplicationDescription()
199+
{
200+
std::lock_guard guard(getLock());
201+
ApplicationDescription desc;
202+
if (!uaclient)
203+
initialize();
204+
205+
UA_StatusCode status = UA_STATUSCODE_GOOD;
206+
207+
size_t endpointCount = 0;
208+
UA_EndpointDescription *endpointArray = NULL;
209+
210+
status = UA_Client_getEndpoints(
211+
uaclient,
212+
endpoint.getUrl().c_str(),
213+
&endpointCount,
214+
&endpointArray
215+
);
216+
const auto url = endpoint.getUrl();
217+
if (OPCUA_STATUSCODE_SUCCEEDED(status))
218+
{
219+
for (size_t i = 0; i < endpointCount; ++i) {
220+
const std::string_view endpointUrl(reinterpret_cast<char*>(endpointArray[i].endpointUrl.data), endpointArray[i].endpointUrl.length);
221+
if (url == endpointUrl)
222+
{
223+
const UA_ApplicationDescription& app = endpointArray[i].server;
224+
desc.uri = std::string(reinterpret_cast<char*>(app.applicationUri.data), (int)app.applicationUri.length);
225+
desc.name = utils::ToStdString(app.applicationName.text);
226+
}
227+
}
228+
}
229+
230+
UA_Array_delete(endpointArray, endpointCount, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
231+
return desc;
232+
}
233+
198234
void OpcUaClient::connect()
199235
{
200236
std::lock_guard guard(getLock());

shared/libraries/opcuageneric/opcuageneric_client/include/opcuageneric_client/generic_client_device_impl.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ BEGIN_NAMESPACE_OPENDAQ_OPCUA_GENERIC
2525
class OpcuaGenericClientDeviceImpl : public Device
2626
{
2727
public:
28-
explicit OpcuaGenericClientDeviceImpl(const ContextPtr& ctx, const ComponentPtr& parent, const PropertyObjectPtr& config);
28+
explicit OpcuaGenericClientDeviceImpl(const ContextPtr& ctx,
29+
const ComponentPtr& parent,
30+
const PropertyObjectPtr& config,
31+
std::shared_ptr<OpcUaClient> client,
32+
const std::string& localId,
33+
const std::string& name);
2934
static PropertyObjectPtr createDefaultConfig();
3035
protected:
3136
static std::atomic<int> localIndex;
@@ -42,10 +47,11 @@ class OpcuaGenericClientDeviceImpl : public Device
4247
FunctionBlockPtr onAddFunctionBlock(const StringPtr& typeId, const PropertyObjectPtr& config) override;
4348

4449
void initNestedFbTypes();
50+
void initProperties(const PropertyObjectPtr& config);
51+
std::string getConnectionString() const;
4552

4653
DictObjectPtr<IDict, IString, IFunctionBlockType> nestedFbTypes;
4754

48-
StringPtr connectionString;
4955
EnumerationPtr connectionStatus;
5056

5157
std::atomic<bool> connectedDone{false};

shared/libraries/opcuageneric/opcuageneric_client/src/generic_client_device_impl.cpp

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,45 @@ BEGIN_NAMESPACE_OPENDAQ_OPCUA_GENERIC
1010

1111
std::atomic<int> OpcuaGenericClientDeviceImpl::localIndex = 0;
1212

13-
OpcuaGenericClientDeviceImpl::OpcuaGenericClientDeviceImpl(const ContextPtr& ctx, const ComponentPtr& parent, const PropertyObjectPtr& config)
14-
: Device(ctx, parent, generateLocalId()),
15-
connectionStatus(Enumeration("ConnectionStatusType", "Connected", this->context.getTypeManager()))
13+
namespace
1614
{
17-
this->name = GENERIC_OPCUA_CLIENT_DEVICE_NAME;
18-
19-
const auto host = config.getPropertyValue(PROPERTY_NAME_OPCUA_HOST).asPtr<IString>().toStdString();
20-
const auto port = config.getPropertyValue(PROPERTY_NAME_OPCUA_PORT).asPtr<IInteger>().getValue(DEFAULT_OPCUA_PORT);
21-
const auto path = config.getPropertyValue(PROPERTY_NAME_OPCUA_PATH).asPtr<IString>().toStdString();
15+
PropertyObjectPtr populateDefaultConfig(const PropertyObjectPtr& defaultConfig, const PropertyObjectPtr& config)
16+
{
17+
auto newConfig = PropertyObject();
18+
for (const auto& prop : defaultConfig.getAllProperties())
19+
{
20+
newConfig.addProperty(prop.asPtr<IPropertyInternal>(true).clone());
21+
const auto propName = prop.getName();
22+
newConfig.setPropertyValue(propName, config.hasProperty(propName) ? config.getPropertyValue(propName) : prop.getValue());
23+
}
24+
return newConfig;
25+
}
26+
}
2227

23-
connectionString = std::string(OpcUaGenericScheme) + "://" + host + ":" + std::to_string(port) + path;
28+
OpcuaGenericClientDeviceImpl::OpcuaGenericClientDeviceImpl(const ContextPtr& ctx,
29+
const ComponentPtr& parent,
30+
const PropertyObjectPtr& config,
31+
std::shared_ptr<OpcUaClient> client,
32+
const std::string& localId,
33+
const std::string& name)
34+
: Device(ctx, parent, localId.empty() ? generateLocalId() : localId)
35+
, connectionStatus(Enumeration("ConnectionStatusType", "Connected", this->context.getTypeManager()))
36+
, client(client)
37+
{
38+
if (this->client == nullptr)
39+
DAQ_THROW_EXCEPTION(UninitializedException, "OpcUaClient is not initialized");
2440

25-
auto endpoint = OpcUaEndpoint(connectionString);
41+
this->name = name.empty() ? GENERIC_OPCUA_CLIENT_DEVICE_NAME : name;
2642

27-
endpoint.setUsername(config.getPropertyValue(PROPERTY_NAME_OPCUA_USERNAME));
28-
endpoint.setPassword(config.getPropertyValue(PROPERTY_NAME_OPCUA_PASSWORD));
43+
if (config.assigned())
44+
initProperties(populateDefaultConfig(createDefaultConfig(), config));
45+
else
46+
initProperties(createDefaultConfig());
2947

3048
try
3149
{
32-
client = std::make_shared<OpcUaClient>(endpoint);
33-
client->connect();
34-
client->runIterate();
50+
this->client->connect();
51+
this->client->runIterate();
3552
}
3653
catch (const OpcUaException& e)
3754
{
@@ -41,11 +58,10 @@ OpcuaGenericClientDeviceImpl::OpcuaGenericClientDeviceImpl(const ContextPtr& ctx
4158
case UA_STATUSCODE_BADIDENTITYTOKENINVALID:
4259
DAQ_THROW_EXCEPTION(AuthenticationFailedException, e.what());
4360
default:
44-
DAQ_THROW_EXCEPTION(NotFoundException, e.what());
61+
DAQ_THROW_EXCEPTION(GeneralErrorException, e.what());
4562
}
4663
}
4764

48-
4965
initComponentStatus();
5066
initNestedFbTypes();
5167
}
@@ -63,14 +79,38 @@ PropertyObjectPtr OpcuaGenericClientDeviceImpl::createDefaultConfig()
6379
return defaultConfig;
6480
}
6581

82+
void OpcuaGenericClientDeviceImpl::initProperties(const PropertyObjectPtr& config)
83+
{
84+
for (const auto& prop : config.getAllProperties())
85+
{
86+
const auto propName = prop.getName();
87+
if (!objPtr.hasProperty(propName))
88+
{
89+
auto propClone = PropertyBuilder(prop.getName())
90+
.setValueType(prop.getValueType())
91+
.setDescription(prop.getDescription())
92+
.setDefaultValue(prop.getValue())
93+
.setVisible(prop.getVisible())
94+
.setReadOnly(true)
95+
.build();
96+
objPtr.addProperty(propClone);
97+
}
98+
}
99+
}
100+
101+
std::string OpcuaGenericClientDeviceImpl::getConnectionString() const
102+
{
103+
return client->getEndpoint().getUrl();
104+
}
105+
66106
void OpcuaGenericClientDeviceImpl::removed()
67107
{
68108
Device::removed();
69109
}
70110

71111
DeviceInfoPtr OpcuaGenericClientDeviceImpl::onGetInfo()
72112
{
73-
return DeviceInfo(connectionString, GENERIC_OPCUA_CLIENT_DEVICE_NAME);
113+
return DeviceInfo(getConnectionString(), GENERIC_OPCUA_CLIENT_DEVICE_NAME);
74114
}
75115

76116

shared/libraries/opcuageneric/opcuageneric_client/tests/test_opcua_generic_client_device.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,18 @@ TEST_F(GenericOpcuaClientDeviceTest, DefaultDeviceConfig)
6767
TEST_F(GenericOpcuaClientDeviceTest, CreatingDeviceWithDefaultConfig)
6868
{
6969
const auto instance = Instance();
70+
const std::string deviceName("open62541-based OPC UA Application");
7071
daq::GenericDevicePtr<daq::IDevice> device;
7172
ASSERT_NO_THROW(device = instance.addDevice("daq.opcua.generic://127.0.0.1:4842"));
7273
ASSERT_EQ(device.getStatusContainer().getStatus("ComponentStatus"),
7374
Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager()));
74-
ASSERT_EQ(device.getInfo().getName(), GENERIC_OPCUA_CLIENT_DEVICE_NAME);
75+
ASSERT_EQ(device.getInfo().getName(), deviceName);
7576
auto devices = instance.getDevices();
7677
bool contain = false;
7778
daq::GenericDevicePtr<daq::IDevice> deviceFromList;
7879
for (const auto& d : devices)
7980
{
80-
contain = (d.getName() == GENERIC_OPCUA_CLIENT_DEVICE_NAME);
81+
contain = (d.getName() == deviceName);
8182
if (contain)
8283
{
8384
deviceFromList = d;
@@ -117,3 +118,10 @@ TEST_F(GenericOpcuaClientDeviceTest, CheckDeviceFunctionalBlocks)
117118
ASSERT_GE(fbTypes.getCount(), 1);
118119
ASSERT_TRUE(fbTypes.hasKey(GENERIC_OPCUA_MONITORED_ITEM_FB_NAME));
119120
}
121+
122+
TEST_F(GenericOpcuaClientDeviceTest, CheckDeviceNameLocalId)
123+
{
124+
StartUp();
125+
EXPECT_EQ(device.getLocalId().toStdString(), std::string("urn:open62541.server.application"));
126+
EXPECT_EQ(device.getName().toStdString(), std::string("open62541-based OPC UA Application"));
127+
}

0 commit comments

Comments
 (0)