Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6b23fb6
docs(testing): seed issue-130-131 writeup skeleton
ottobolyos Apr 25, 2026
508e495
docs(testing): scope cppagent header schemaVersion defect
ottobolyos Apr 25, 2026
a779c41
test(json-cppagent-tests): add red tests for header schemaVersion
ottobolyos Apr 25, 2026
d922b11
docs(testing): document red-test matrix for issue-130-131
ottobolyos Apr 25, 2026
482ba8d
enh(common): add SchemaVersion to header DTOs
ottobolyos Apr 25, 2026
4dcc6a7
fix(json-cppagent): emit schemaVersion on streams header
ottobolyos Apr 25, 2026
609fa1c
fix(json-cppagent): emit schemaVersion on devices header
ottobolyos Apr 25, 2026
6feff7d
fix(json-cppagent): emit schemaVersion on assets header
ottobolyos Apr 25, 2026
9667377
docs(testing): document issue-130-131 library fix
ottobolyos Apr 25, 2026
01323eb
test(json-cppagent): guard schemaVersion+testIndicator on every header
ottobolyos Apr 25, 2026
1ca3e60
docs(testing): document issue-130-131 regression pins
ottobolyos Apr 25, 2026
3e6f3d3
test(json-cppagent): pin schema-version wire shape across v2.0/v2.3/v2.5
ottobolyos Apr 25, 2026
127b7ab
docs(testing): document issue-130-131 e2e validation
ottobolyos Apr 25, 2026
4b13c46
docs(testing): author issue-130-131 campaign summary
ottobolyos Apr 25, 2026
0f73e16
chore(docs): remove internal planning leak from committed tree
ottobolyos Apr 27, 2026
2259cf4
test(json-cppagent): pin Validation on every cppagent header DTO
ottobolyos Apr 27, 2026
ad0cf27
fix(json-cppagent): emit Validation on every cppagent header DTO
ottobolyos Apr 27, 2026
37a1a33
test(json-cppagent): pin shared envelope/version compliance matrix
ottobolyos Apr 27, 2026
82d22b1
test(json-cppagent): extract shared envelope/version wire-shape matrix
ottobolyos Apr 27, 2026
6a1c945
test(common): pin AddDevice required-DataItem backfill contract
ottobolyos Apr 27, 2026
b724e41
fix(agent): cache DataItems and type set during NormalizeDevice backfill
ottobolyos Apr 27, 2026
c8acb39
test(common): pin Header xmldoc example off stale 2.5 snapshot
ottobolyos Apr 27, 2026
6bb3c98
docs(common-headers): refresh SchemaVersion xmldoc example to 2.7
ottobolyos Apr 27, 2026
167b47c
test(json-cppagent): pin SchemaVersion xmldoc on cppagent headers
ottobolyos Apr 27, 2026
85c7e8c
docs(json-cppagent): document SchemaVersion on cppagent headers
ottobolyos Apr 27, 2026
9b08778
fix(common): preserve AssetCount DATA_SET representation override
ottobolyos Apr 27, 2026
2cd95ee
fix(agent): guard HashSet capacity ctor for legacy TFMs
ottobolyos Apr 28, 2026
e68d24d
style(prose): rewrite NormalizeDevice test summary inline, drop F-code
ottobolyos Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions libraries/MTConnect.NET-Common/Agents/MTConnectAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,54 +1112,76 @@ private Device NormalizeDevice(IDevice device)
obj.Compositions = NormalizeCompositions(device.Compositions, obj, obj);
obj.Components = NormalizeComponents(device.Components, obj, obj);

// Required DataItem backfill: enumerate `obj.DataItems` once
// into a List<IDataItem> and project the type set into a HashSet
// so each required-type lookup is O(1) instead of O(n) and we
// avoid the per-required-type ToList() allocation. Preserves the
// existing behavior pinned by NormalizeDeviceRequiredDataItemsTests.
var dataItemList = obj.DataItems != null
? new List<IDataItem>(obj.DataItems)
: new List<IDataItem>();
#if NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_0_OR_GREATER
var dataItemTypes = new HashSet<string>(dataItemList.Count);
#else
var dataItemTypes = new HashSet<string>();
#endif
for (var i = 0; i < dataItemList.Count; i++)
{
var t = dataItemList[i]?.Type;
if (t != null) dataItemTypes.Add(t);
}

// Add Required Availability DataItem
if (obj.DataItems.IsNullOrEmpty() || !obj.DataItems.Any(o => o.Type == AvailabilityDataItem.TypeId))
if (!dataItemTypes.Contains(AvailabilityDataItem.TypeId))
{
var availability = new AvailabilityDataItem(obj.Id);
availability.Device = obj;
availability.Container = obj;
availability.Name = AvailabilityDataItem.NameId;
var x = obj.DataItems.ToList();
x.Add(availability);
obj.DataItems = x;
dataItemList.Add(availability);
dataItemTypes.Add(AvailabilityDataItem.TypeId);
}

// Add Required AssetChanged DataItem
if (obj.DataItems.IsNullOrEmpty() || !obj.DataItems.Any(o => o.Type == AssetChangedDataItem.TypeId))
if (!dataItemTypes.Contains(AssetChangedDataItem.TypeId))
{
var assetChanged = new AssetChangedDataItem(obj.Id);
assetChanged.Device = obj;
assetChanged.Container = obj;
assetChanged.Name = AssetChangedDataItem.NameId;
var x = obj.DataItems.ToList();
x.Add(assetChanged);
obj.DataItems = x;
dataItemList.Add(assetChanged);
dataItemTypes.Add(AssetChangedDataItem.TypeId);
}

// Add Required AssetRemoved DataItem
if (obj.DataItems.IsNullOrEmpty() || !obj.DataItems.Any(o => o.Type == AssetRemovedDataItem.TypeId))
if (!dataItemTypes.Contains(AssetRemovedDataItem.TypeId))
{
var assetRemoved = new AssetRemovedDataItem(obj.Id);
assetRemoved.Device = obj;
assetRemoved.Container = obj;
assetRemoved.Name = AssetRemovedDataItem.NameId;
var x = obj.DataItems.ToList();
x.Add(assetRemoved);
obj.DataItems = x;
dataItemList.Add(assetRemoved);
dataItemTypes.Add(AssetRemovedDataItem.TypeId);
}

// Add Required AssetCount DataItem
if (obj.DataItems.IsNullOrEmpty() || !obj.DataItems.Any(o => o.Type == AssetCountDataItem.TypeId))
if (!dataItemTypes.Contains(AssetCountDataItem.TypeId))
{
var assetcount = new AssetCountDataItem(obj.Id);
assetcount.Device = obj;
assetcount.Container = obj;
assetcount.Name = AssetCountDataItem.NameId;
var x = obj.DataItems.ToList();
x.Add(assetcount);
obj.DataItems = x;
// ASSET_COUNT is a DATA_SET representation per MTConnect Part 2
// (UML _19_0_3_68e0225_1640602520420_217627_44). The generated
// AssetCountDataItem still defaults Representation to VALUE; override
// it here so the auto-injected DataItem matches the spec.
assetcount.Representation = DataItemRepresentation.DATA_SET;
dataItemList.Add(assetcount);
dataItemTypes.Add(AssetCountDataItem.TypeId);
}

obj.DataItems = dataItemList;


// Generic Components
var genericComponents = obj.GetComponents()?.Where(o => o.GetType() == typeof(Component));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public interface IMTConnectAssetsHeader
/// </summary>
string Version { get; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
string SchemaVersion { get; }

/// <summary>
/// An identification defining where the Agent that published the Response Document is installed or hosted.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public interface IMTConnectDevicesHeader
/// </summary>
string Version { get; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
string SchemaVersion { get; }

/// <summary>
/// An identification defining where the Agent that published the Response Document is installed or hosted.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public interface IMTConnectStreamsHeader
/// </summary>
string Version { get; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
string SchemaVersion { get; }

/// <summary>
/// An identification defining where the Agent that published the Response Document is installed or hosted.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public class MTConnectAssetsHeader : IMTConnectAssetsHeader
/// </summary>
public string Version { get; set; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
public string SchemaVersion { get; set; }

/// <summary>
/// An identification defining where the Agent that published the Response Document is installed or hosted.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public class MTConnectDevicesHeader : IMTConnectDevicesHeader
/// </summary>
public string Version { get; set; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
public string SchemaVersion { get; set; }

/// <summary>
/// An identification defining where the Agent that published the Response Document is installed or hosted.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public class MTConnectStreamsHeader : IMTConnectStreamsHeader
/// </summary>
public string Version { get; set; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
public string SchemaVersion { get; set; }

/// <summary>
/// An identification defining where the Agent that published the Response Document is installed or hosted.
/// </summary>
Expand Down
18 changes: 18 additions & 0 deletions libraries/MTConnect.NET-JSON-cppagent/Assets/JsonAssetsHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public class JsonAssetsHeader
[JsonPropertyName("version")]
public string Version { get; set; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
[JsonPropertyName("schemaVersion")]
public string SchemaVersion { get; set; }

[JsonPropertyName("sender")]
public string Sender { get; set; }

Expand All @@ -30,6 +37,13 @@ public class JsonAssetsHeader
[JsonPropertyName("testIndicator")]
public bool TestIndicator { get; set; }

/// <summary>
/// Indicates if the MTConnect Agent is validating against the normative model.
/// Mirrors the cppagent v2 wire shape that emits `validation` on every Header.
/// </summary>
[JsonPropertyName("validation")]
public bool Validation { get; set; }

[JsonPropertyName("creationTime")]
public DateTime CreationTime { get; set; }

Expand All @@ -42,11 +56,13 @@ public JsonAssetsHeader(IMTConnectAssetsHeader header)
{
InstanceId = header.InstanceId;
Version = header.Version;
SchemaVersion = header.SchemaVersion;
Sender = header.Sender;
AssetBufferSize = header.AssetBufferSize;
AssetCount = header.AssetCount;
DeviceModelChangeTime = header.DeviceModelChangeTime;
TestIndicator = header.TestIndicator;
Validation = header.Validation;
CreationTime = header.CreationTime;
}
}
Expand All @@ -57,11 +73,13 @@ public virtual IMTConnectAssetsHeader ToAssetsHeader()
var header = new MTConnectAssetsHeader();
header.InstanceId = InstanceId;
header.Version = Version;
header.SchemaVersion = SchemaVersion;
header.Sender = Sender;
header.AssetBufferSize = AssetBufferSize;
header.AssetCount = AssetCount;
header.DeviceModelChangeTime = DeviceModelChangeTime;
header.TestIndicator = TestIndicator;
header.Validation = Validation;
header.CreationTime = CreationTime;
return header;
}
Expand Down
11 changes: 11 additions & 0 deletions libraries/MTConnect.NET-JSON-cppagent/Devices/JsonDevicesHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ public class JsonDevicesHeader
[JsonPropertyName("testIndicator")]
public bool TestIndicator { get; set; }

/// <summary>
/// Indicates if the MTConnect Agent is validating against the normative model.
/// Mirrors the cppagent v2 wire shape that emits `validation` on every Header.
/// </summary>
[JsonPropertyName("validation")]
public bool Validation { get; set; }

[JsonPropertyName("creationTime")]
public DateTime CreationTime { get; set; }

Expand All @@ -48,12 +55,14 @@ public JsonDevicesHeader(IMTConnectDevicesHeader header)
{
InstanceId = header.InstanceId;
Version = header.Version;
SchemaVersion = header.SchemaVersion;
Sender = header.Sender;
BufferSize = header.BufferSize;
AssetBufferSize = header.AssetBufferSize;
AssetCount = header.AssetCount;
DeviceModelChangeTime = header.DeviceModelChangeTime;
TestIndicator = header.TestIndicator;
Validation = header.Validation;
CreationTime = header.CreationTime;
}
}
Expand All @@ -64,12 +73,14 @@ public IMTConnectDevicesHeader ToDevicesHeader()
var header = new MTConnectDevicesHeader();
header.InstanceId = InstanceId;
header.Version = Version;
header.SchemaVersion = SchemaVersion;
header.Sender = Sender;
header.BufferSize = BufferSize;
header.AssetBufferSize = AssetBufferSize;
header.AssetCount = AssetCount;
header.DeviceModelChangeTime = DeviceModelChangeTime;
header.TestIndicator = TestIndicator;
header.Validation = Validation;
header.CreationTime = CreationTime;
return header;
}
Expand Down
18 changes: 18 additions & 0 deletions libraries/MTConnect.NET-JSON-cppagent/Streams/JsonStreamsHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public class JsonStreamsHeader
[JsonPropertyName("version")]
public string Version { get; set; }

/// <summary>
/// The major and minor number of the MTConnect Standard schema the Response Document conforms to (for example "2.7").
/// Mirrors the cppagent v2 wire shape that emits `schemaVersion` on every Header.
/// </summary>
[JsonPropertyName("schemaVersion")]
public string SchemaVersion { get; set; }

[JsonPropertyName("sender")]
public string Sender { get; set; }

Expand All @@ -36,6 +43,13 @@ public class JsonStreamsHeader
[JsonPropertyName("testIndicator")]
public bool TestIndicator { get; set; }

/// <summary>
/// Indicates if the MTConnect Agent is validating against the normative model.
/// Mirrors the cppagent v2 wire shape that emits `validation` on every Header.
/// </summary>
[JsonPropertyName("validation")]
public bool Validation { get; set; }

[JsonPropertyName("creationTime")]
public DateTime CreationTime { get; set; }

Expand All @@ -48,13 +62,15 @@ public JsonStreamsHeader(IMTConnectStreamsHeader header)
{
InstanceId = header.InstanceId;
Version = header.Version;
SchemaVersion = header.SchemaVersion;
Sender = header.Sender;
BufferSize = header.BufferSize;
FirstSequence = header.FirstSequence;
LastSequence = header.LastSequence;
NextSequence = header.NextSequence;
DeviceModelChangeTime = header.DeviceModelChangeTime;
TestIndicator = header.TestIndicator;
Validation = header.Validation;
CreationTime = header.CreationTime;
}
}
Expand All @@ -65,13 +81,15 @@ public virtual IMTConnectStreamsHeader ToStreamsHeader()
var header = new MTConnectStreamsHeader();
header.InstanceId = InstanceId;
header.Version = Version;
header.SchemaVersion = SchemaVersion;
header.Sender = Sender;
header.BufferSize = BufferSize;
header.FirstSequence = FirstSequence;
header.LastSequence = LastSequence;
header.NextSequence = NextSequence;
header.DeviceModelChangeTime = DeviceModelChangeTime;
header.TestIndicator = TestIndicator;
header.Validation = Validation;
header.CreationTime = CreationTime;
return header;
}
Expand Down
Loading