Skip to content

Commit 8493e73

Browse files
Copilotromanett
andauthored
Fix KeyFrame not sent if no changed values (#3367)
* Initial plan * Fix KeyFrame not sent if no changed values (Issue #2622) The MessageCount was only incremented in OnMessagePublished() which is only called when a message is actually published. When there are no data changes, no message is published, so MessageCount stayed the same and KeyFrames were never sent after the initial one. The fix moves the MessageCount increment to IsDeltaFrame() so it tracks publishing intervals instead of actual messages published. This ensures that after KeyFrameCount intervals, a key frame is sent regardless of whether data changed. Co-authored-by: romanett <[email protected]> * Address code review: Use direct cast instead of Convert.ToUInt32 Co-authored-by: romanett <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: romanett <[email protected]>
1 parent 2fc1ad1 commit 8493e73

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

Libraries/Opc.Ua.PubSub/WriterGroupPublishState.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public WriterGroupPublishState()
6565

6666
/// <summary>
6767
/// Returns TRUE if the next DataSetMessage is a delta frame.
68+
/// Also increments the message count for each publishing interval.
6869
/// </summary>
6970
public bool IsDeltaFrame(DataSetWriterDataType writer, out uint sequenceNumber)
7071
{
@@ -73,7 +74,16 @@ public bool IsDeltaFrame(DataSetWriterDataType writer, out uint sequenceNumber)
7374
DataSetState state = GetState(writer);
7475
sequenceNumber = state.MessageCount + 1;
7576

76-
if (state.MessageCount % writer.KeyFrameCount != 0)
77+
// Check if this is a key frame interval before incrementing
78+
// This ensures the first message (MessageCount=0) is always a key frame
79+
// and subsequent key frames occur every KeyFrameCount intervals
80+
bool isDeltaFrame = state.MessageCount % writer.KeyFrameCount != 0;
81+
82+
// Increment the message count to track publishing intervals
83+
// This ensures KeyFrameCount is based on intervals, not actual messages published
84+
state.MessageCount++;
85+
86+
if (isDeltaFrame)
7787
{
7888
return true;
7989
}
@@ -176,14 +186,13 @@ public DataSet ExcludeUnchangedFields(DataSetWriterDataType writer, DataSet data
176186
}
177187

178188
/// <summary>
179-
/// Increments the message counter.
189+
/// Updates the state after a message is published.
180190
/// </summary>
181191
public void OnMessagePublished(DataSetWriterDataType writer, DataSet dataset)
182192
{
183193
lock (m_dataSetStates)
184194
{
185195
DataSetState state = GetState(writer);
186-
state.MessageCount++;
187196

188197
if (writer.KeyFrameCount > 1)
189198
{

Tests/Opc.Ua.PubSub.Tests/PublishedData/WriterGroupPublishedStateTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,5 +404,54 @@ private static void ValidateDataSetMessageData(
404404
}
405405
}
406406
}
407+
408+
/// <summary>
409+
/// Tests that key frames are sent after KeyFrameCount intervals even when there are no data changes.
410+
/// This verifies the fix for issue #2622: KeyFrame is not sent if no changed values
411+
/// </summary>
412+
[Test(Description = "Verify KeyFrame is sent after KeyFrameCount intervals without data changes")]
413+
public void KeyFrameSentWithoutDataChanges([Values(3, 5)] int keyFrameCount)
414+
{
415+
// Arrange - create a simple DataSetWriter with specified KeyFrameCount
416+
var writer = new DataSetWriterDataType
417+
{
418+
DataSetWriterId = 1,
419+
KeyFrameCount = (uint)keyFrameCount
420+
};
421+
422+
var writerGroupPublishState = new WriterGroupPublishState();
423+
424+
// Act & Assert
425+
426+
// First call should be a key frame (interval 0)
427+
bool isDelta = writerGroupPublishState.IsDeltaFrame(writer, out uint seq1);
428+
Assert.That(isDelta, Is.False, "First message should be a key frame");
429+
Assert.That(seq1, Is.EqualTo(1), "First sequence number should be 1");
430+
431+
// Subsequent calls before KeyFrameCount should be delta frames
432+
for (int i = 1; i < keyFrameCount; i++)
433+
{
434+
isDelta = writerGroupPublishState.IsDeltaFrame(writer, out uint seqDelta);
435+
Assert.That(isDelta, Is.True, $"Message {i + 1} should be a delta frame");
436+
Assert.That(seqDelta, Is.EqualTo(i + 1), $"Sequence number should be {i + 1}");
437+
}
438+
439+
// After KeyFrameCount intervals, we should get another key frame
440+
isDelta = writerGroupPublishState.IsDeltaFrame(writer, out uint seqKeyFrame);
441+
Assert.That(isDelta, Is.False, $"Message {keyFrameCount + 1} should be a key frame");
442+
Assert.That(seqKeyFrame, Is.EqualTo(keyFrameCount + 1), $"Sequence number should be {keyFrameCount + 1}");
443+
444+
// Verify the cycle continues correctly
445+
for (int i = 1; i < keyFrameCount; i++)
446+
{
447+
isDelta = writerGroupPublishState.IsDeltaFrame(writer, out _);
448+
Assert.That(isDelta, Is.True, $"Message {keyFrameCount + i + 1} should be a delta frame");
449+
}
450+
451+
// And another key frame
452+
isDelta = writerGroupPublishState.IsDeltaFrame(writer, out uint seqKeyFrame2);
453+
Assert.That(isDelta, Is.False, $"Message {2 * keyFrameCount + 1} should be a key frame");
454+
Assert.That(seqKeyFrame2, Is.EqualTo(2 * keyFrameCount + 1), $"Sequence number should be {2 * keyFrameCount + 1}");
455+
}
407456
}
408457
}

0 commit comments

Comments
 (0)