Skip to content

Generic client: single sampling scheduler and monitored item fixes - #36

Open
viacheslauK wants to merge 13 commits into
mainfrom
improvements-for-generic-client
Open

viacheslauK wants to merge 13 commits into
mainfrom
improvements-for-generic-client

Conversation

@viacheslauK

@viacheslauK viacheslauK commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Replaces the reader thread that each OpcUaMonitoredItemFbImpl used to own with one SamplingScheduler per device, and fixes several monitored-item defects found along the way.

Changes

  • SamplingScheduler (new) drives every monitored item of a device from one thread, each item keeping its own deadline.
  • DateTime: added OpcUaDataValue::isDateTime(); DateTime values now build a packet and are read as UA_DateTime instead of readScalar<UA_Int64>, including the UtcTime subtype.
  • Validation: SamplingInterval rejects negative and above-uint32_t values; reads reject a null value, report a failed packet build, and catch std::exception / ... beyond OpcUaException.
  • Tests and docs: new test_sampling_scheduler.cpp; device and monitored-item suites extended; test server publishes .dt and .utc nodes; USAGE.md adds a property reference and walkthrough.

@viacheslauK viacheslauK self-assigned this Aug 28, 2026
@viacheslauK viacheslauK changed the title Improvements for generic client Generic client: single sampling scheduler and monitored item fixes Aug 28, 2026
@viacheslauK
viacheslauK force-pushed the improvements-for-generic-client branch from 8e76917 to c3752de Compare August 28, 2026 19:10
@viacheslauK
viacheslauK marked this pull request as ready for review August 31, 2026 05:56
Comment thread modules/opcua_generic_client_module/USAGE.md Outdated
Comment thread modules/opcua_generic_client_module/USAGE.md Outdated
@JakaMohorko
JakaMohorko removed the request for review from NikolaiShipilov September 10, 2026 07:36
Comment thread modules/opcua_generic_client_module/USAGE.md Outdated
…#41)

The device now holds only defaults for new blocks: DefaultTimestampMode and DefaultSamplingInterval. Each MonitoredItem has its own TimestampMode and SamplingInterval properties, which start from those defaults. None of these properties is part of a config. They exist only as properties after the object is created.

@JakaMohorko JakaMohorko left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ltgm, just a few smaller comments.

sampler.stop();
}

PropertyObjectPtr OpcuaGenericClientDeviceImpl::createDefaultConfig()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of any property (even in config) should be explicitly stated in the PR description, as it's a breaking change.

Comment on lines +242 to +243
server's own sampling and publishing settings do not apply. Every successful read publishes a sample,
even when the value has not changed — there is no deadband or change filter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this apply even if the timestamps are equal? We should probably avoid sending packets with repeated timestamps.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, even if the timestamps are equal


const auto samplingInterval =
readProperty<Int, IInteger>(objPtr, PROPERTY_NAME_OPCUA_DEFAULT_SAMPLING_INTERVAL, DEFAULT_OPCUA_MIFB_SAMPLING_INTERVAL);
if (samplingInterval <= 0 || samplingInterval > static_cast<Int>(std::numeric_limits<uint32_t>::max()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be added as a min/max attribute of the SamplingInterval property.

{
std::unique_lock lock(mutex);
items.erase(std::remove_if(items.begin(), items.end(), [item](const Entry& e) { return e.item == item; }), items.end());
inFlightCv.wait(lock, [this, item] { return inFlight != item; });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can deadlock during removed() if reconnection happens at the same time as destruction.

.setUnit(Unit("s", -1, "seconds", "time"))
.setTickResolution(Ratio(1, 1'000'000))
.setOrigin("1970-01-01T00:00:00Z")
.setName("Time")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not set the name of data descriptors that are not struct types.

@@ -418,7 +465,7 @@ void OpcUaMonitoredItemFbImpl::createSignal()

void OpcUaMonitoredItemFbImpl::reconfigureSignal(const FbConfig& prevConfig)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like prevConfig is now an usused parameter.

Comment on lines +37 to +40
| `DeviceNodeIDType` | Selection | `1` — `String` |
| `DeviceNodeIDString` | String | `""` |
| `DeviceNodeIDNumeric` | Int | `0` |
| `DeviceNamespaceIndex` | Int | `0` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, these should be a fallback in case the DeviceSet is not found / has no child objects of DeviceType.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants