Skip to content

Fix SourceTimestamp for variables without explicit timestamps in BaseVariableState#3451

Merged
marcschier merged 8 commits intomasterfrom
copilot/fix-source-timestamp-update
Jan 19, 2026
Merged

Fix SourceTimestamp for variables without explicit timestamps in BaseVariableState#3451
marcschier merged 8 commits intomasterfrom
copilot/fix-source-timestamp-update

Conversation

Copy link
Contributor

Copilot AI commented Jan 11, 2026

Proposed changes

Variables without explicit timestamps (such as static variables in ReferenceNodeManager) were returning stale SourceTimestamp values instead of current time on reads. This was caused by BaseVariableState.ReadValueAttribute setting the internal m_timestamp once when it was DateTime.MinValue, then using that stale value for all subsequent reads. This caused CTT test failures in Attribute Services / Attribute Read / Test Case 006.js.

Fix: Modified BaseVariableState.ReadValueAttribute to return DateTime.UtcNow for sourceTimestamp when m_timestamp == DateTime.MinValue, without modifying the internal state.

Root Cause

The issue was in Stack/Opc.Ua.Types/State/BaseVariableState.cs where the timestamp was set once and then reused:

// ensure a value timestamp exists.
if (m_timestamp == DateTime.MinValue)
{
    m_timestamp = DateTime.UtcNow;
}
// ... later
sourceTimestamp = m_timestamp;

After the first read, m_timestamp would have a value set to that first read time, causing all subsequent reads to return the same stale timestamp.

Changes

  • Stack/Opc.Ua.Types/State/BaseVariableState.cs: Modified ReadValueAttribute to return current time for sourceTimestamp without modifying internal state
  • Tests/Opc.Ua.Server.Tests/ReferenceServerTest.cs: Added two tests to verify timestamp behavior:
    • ReferenceNodeManagerVariablesUpdateTimestampOnReadAsync - verifies scalar variables
    • ReferenceNodeManagerArrayVariablesUpdateTimestampOnReadAsync - verifies array variables

Solution

// ensure a value timestamp exists.
if (m_timestamp == DateTime.MinValue)
{
    sourceTimestamp = DateTime.UtcNow;
}
else
{
    sourceTimestamp = m_timestamp;
}

This approach:

  • Returns current time (DateTime.UtcNow) for variables without explicit timestamps (static variables)
  • Preserves explicitly set timestamps for variables that manage their own timestamps (dynamic variables)
  • Does not modify internal state during reads, maintaining consistency with OPC UA semantics

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Test enhancement (non-breaking change to increase test coverage)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected, requires version increase of Nuget packages)
  • Documentation Update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc.
  • I have signed the CLA.
  • I ran tests locally with my changes, all passed.
  • I fixed all failing tests in the CI pipelines.
  • I fixed all introduced issues with CodeQL and LGTM.
  • I have added tests that prove my fix is effective or that my feature works and increased code coverage.
  • I have added necessary documentation (if appropriate).
  • Any dependent changes have been merged and published in downstream modules.

Further comments

Test output shows timestamps correctly returning current time for both scalars and arrays on each read.

All existing server tests pass (169 passed, 5 skipped).

The fix is implemented at the BaseVariableState level, ensuring all variables throughout the system benefit from proper timestamp handling. Variables with explicit timestamp management (like dynamic variables updated via DoSimulation) continue to use their managed timestamps, while variables without explicit timestamps (like static variables in ReferenceNodeManager) now return current time on each read instead of a stale timestamp from the first read.

Original prompt

This section details on the original issue you should resolve

<issue_title>[CTT] Attribute Services / Attribute Read / Test Case 006.js: SourceTimestamp too old</issue_title>
<issue_description>Read Scalar Test set from ReferenceNodeManager.

ServerTimestamp too old

-> SourceTimestamp stays at the Startup time of the Server, as the SourceTimestamp is set to StartupTime the logic to update it never triggers.

CustomNodeManager.Read , logic to update the Timestamp is present:

CustomNodeManager

// Set timestamps after ReadAttribute to ensure consistency
                    // For Value attributes, match ServerTimestamp to SourceTimestamp
                    // For other attributes, just ensure ServerTimestamp is set
                    if (nodeToRead.AttributeId == Attributes.Value)
                    {
                        if (value.SourceTimestamp == DateTime.MinValue)
                        {
                            value.SourceTimestamp = DateTime.UtcNow;
                        }
                        value.ServerTimestamp = value.SourceTimestamp;
                    }
                    else
                    {
                        // For non-value attributes, only ServerTimestamp is relevant
                        if (value.ServerTimestamp == DateTime.MinValue)
                        {
                            value.ServerTimestamp = DateTime.UtcNow;
                        }
                    }
Image

ReferenceNodeManager needs to be updated so all methods that create a Variable add a callback OnReadValue that updates the Timestamp to the current time.</issue_description>

<agent_instructions>ReferenceNodeManager needs to be updated so all methods that create a Variable add a callback OnReadValue that updates the Timestamp to the current time.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@CLAassistant
Copy link

CLAassistant commented Jan 11, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ romanett
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI and others added 2 commits January 11, 2026 17:20
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix SourceTimestamp update logic in ReadAttribute Fix ReferenceNodeManager variables not updating SourceTimestamp on reads Jan 11, 2026
Copilot AI requested a review from romanett January 11, 2026 17:38
@romanett romanett marked this pull request as ready for review January 14, 2026 09:31
@romanett romanett marked this pull request as draft January 14, 2026 09:36
set loglevel in session publish when no subscriptions

ignore more keepalive errors in Client Fixture
@romanett
Copy link
Contributor

@copilot please investigate why for arrays the timestamp is still set to server startup time instead of current time. even with the current fix.

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 14, 2026

@copilot please investigate why for arrays the timestamp is still set to server startup time instead of current time. even with the current fix.

Fixed in commit df0e8df. The issue was in BaseVariableState.ReadValueAttribute which only set the timestamp once when it was DateTime.MinValue, then never updated it on subsequent reads. Changed to always update timestamp on every read. Both scalar and array variables now correctly update timestamps (verified with new array test).

Copilot AI changed the title Fix ReferenceNodeManager variables not updating SourceTimestamp on reads Fix SourceTimestamp not updating on variable reads in BaseVariableState Jan 14, 2026
@romanett romanett marked this pull request as ready for review January 17, 2026 17:13
@romanett romanett requested a review from marcschier January 17, 2026 17:13
@romanett
Copy link
Contributor

@copilot please update the PR description to reflect the actual changes done in the code

Copy link
Contributor Author

Copilot AI commented Jan 17, 2026

@copilot please update the PR description to reflect the actual changes done in the code

The PR description should be updated to reflect the actual fix implemented in commit 810d656. The current description states that m_timestamp is always updated to DateTime.UtcNow on every read, but the actual implementation is more conservative:

Actual Fix:

if (m_timestamp == DateTime.MinValue)
{
    sourceTimestamp = DateTime.UtcNow;
}
else
{
    sourceTimestamp = m_timestamp;
}

Instead of modifying internal state on every read, the fix returns DateTime.UtcNow for sourceTimestamp only when m_timestamp == DateTime.MinValue. This ensures variables without an explicit timestamp (like static variables in ReferenceNodeManager) return the current time, while variables with timestamps (like dynamic variables updated in DoSimulation) return their stored timestamp. This approach is more conservative and doesn't modify internal state during reads.

Copilot AI changed the title Fix SourceTimestamp not updating on variable reads in BaseVariableState Fix SourceTimestamp for variables without explicit timestamps in BaseVariableState Jan 17, 2026
Copilot AI requested a review from romanett January 17, 2026 17:17
@marcschier marcschier merged commit e314cf5 into master Jan 19, 2026
112 of 113 checks passed
@romanett romanett deleted the copilot/fix-source-timestamp-update branch January 19, 2026 17:11
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.

[CTT] Attribute Services / Attribute Read / Test Case 006.js: SourceTimestamp too old

4 participants