Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e80e347
chore(common): add trailing newline to DeviceValidationLevel.cs
tukusejssirs Aug 18, 2026
1b6f85f
fix(common): mirror InputValidationLevel onto DeviceValidationLevel
tukusejssirs Aug 18, 2026
bad923f
test(common): pin DeviceValidationLevel enum arms for NormalizeDevice
tukusejssirs Aug 18, 2026
1e4e055
fix(devices): recurse RemoveComposition + cover top-level RemoveDataItem
ottobolyos Aug 18, 2026
94c28d8
test(common): pin DeviceValidationLevel enum-arm × site coverage FLOOR
ottobolyos Aug 18, 2026
3308f17
test(devices): invert OBSERVED asserts on DVL.Remove nested paths
ottobolyos Aug 18, 2026
3c748ba
test(devices,docs): pin Remove depth + ConfigRenderer type-mapping
ottobolyos Aug 18, 2026
edbd8c4
docs(reference): regenerate configuration.md after XML trailing dots
ottobolyos Aug 20, 2026
80fd3e8
docs(tests): resolve XML doc errors on new DVL fixtures
ottobolyos Aug 20, 2026
088946f
docs(devices): clear InvalidCref warning on RemoveDataItem summary
ottobolyos Aug 20, 2026
19b1def
test(common): pin DVL setter, Normalize, and Remove-sibling coverage
ottobolyos Aug 20, 2026
0c72beb
fix(devices): cycle-guard Remove tree walks — dime H1 + M2
ottobolyos Aug 20, 2026
4e9d133
fix(config): trace-and-rethrow config loader — dime H2 + M4
ottobolyos Aug 20, 2026
680777c
refactor(config): collapse DVL explicit flag into nullable — dime M1
ottobolyos Aug 20, 2026
883e923
refactor(common,docs): quick wins + doc fix — dime L1 L2 L3 L4 M3
ottobolyos Aug 20, 2026
72a8b73
docs(reference): regenerate configuration.md after IAgentConfiguration
ottobolyos Aug 20, 2026
b91ae0d
test(common,devices): pin depth-bound guards (Unwrap, Remove walk)
ottobolyos Aug 20, 2026
5a44612
fix(devices): guard RemoveComponent recursion — dime H1-C2
ottobolyos Aug 20, 2026
d1e5b3d
refactor(config): extract LoadWithTriage — dime M2-C2 subsumes M1-C2
ottobolyos Aug 20, 2026
26780d0
refactor(config): self-mirror getter — dime M3-C2
ottobolyos Aug 20, 2026
6eb7449
docs(agent-validation-events): correct AddDevice example — dime M4-C2
ottobolyos Aug 20, 2026
2422596
chore(devices,config): visitedIds self-seed + cap-hit traces + dead code
ottobolyos Aug 20, 2026
cd2e730
test(config): align Normalize precondition with self-mirror getter
ottobolyos Aug 20, 2026
8b13a4e
test(common,devices): capture cap-hit traces + Type-overload triage
ottobolyos Aug 20, 2026
e9fee53
docs(cli): DVL row default clarifies mirror semantics
ottobolyos Aug 21, 2026
c030d5f
chore(devices): tag cap-hit trace warnings with device Id
ottobolyos Aug 21, 2026
8c19101
refactor(config): map IVL to DVL through exhaustive switch
ottobolyos Aug 21, 2026
3411480
docs(config): DVL remarks call out save-latches-mirror — dime F-SEC-001
ottobolyos Aug 21, 2026
30380d4
refactor(config): tighten LoadWithTriage + hoist Path/Normalize
ottobolyos Aug 21, 2026
16383ab
test(config): align LoadWithTriage constraint pin with tightened bound
ottobolyos Aug 21, 2026
c42d415
test(common,config): pin cycle-4 coverage-FLOOR gaps (3 findings)
ottobolyos Aug 21, 2026
90b5bea
fix(common): rewrite AgentConfiguration.cs C#8 features to C#7.3 for …
ottobolyos Aug 21, 2026
111e577
chore: dotnet format drift baseline compliance
ottobolyos Aug 22, 2026
5ab4a39
fix(docs-gen): register DeviceValidationLevel + InputValidationLevel …
ottobolyos Aug 22, 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
26 changes: 25 additions & 1 deletion build/MTConnect.NET-DocsGen/Renderers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,36 @@ public static string Render(IReadOnlyList<ConfigClassInfo> classes)
sb.AppendLine("| --- | --- | --- | --- |");
foreach (var p in c.Properties)
{
sb.AppendLine($"| `{p.SerialisedKey}` | `{p.Name}` | `{Escape(p.Type)}` | {Escape(p.Summary)} |");
sb.AppendLine($"| `{p.SerialisedKey}` | `{p.Name}` | {RenderType(p.Type)} | {Escape(p.Summary)} |");
}
sb.AppendLine();
}
return sb.ToString();
}

/// <summary>
/// Maps a config property's type name to its authored docfx API page,
/// keyed on exact type-name equality (no prefix/substring matching).
/// </summary>
private static readonly IReadOnlyDictionary<string, string> TypeApiLinks = new Dictionary<string, string>
{
["DeviceValidationLevel"] = "/api/MTConnect.Agents.DeviceValidationLevel",
["InputValidationLevel"] = "/api/MTConnect.Agents.InputValidationLevel",
};

/// <summary>
/// Renders the Type column for a config property: a markdown link into
/// the docfx API namespace for types with an authored API page (see
/// <see cref="TypeApiLinks"/>), or a plain backtick-fenced type name
/// otherwise. The type name is escaped before either shape is emitted.
/// </summary>
private static string RenderType(string type)
{
var escaped = Escape(type);
return TypeApiLinks.TryGetValue(type, out var href)
? $"[`{escaped}`]({href})"
: $"`{escaped}`";
}

private static string Escape(string s) => s.Replace("|", "\\|").Replace("\n", " ").Replace("\r", " ");
}
2 changes: 1 addition & 1 deletion docs/cli/agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Backed by `AgentApplicationConfiguration` and the inherited `AgentConfiguration`
| `ignoreObservationCase` | bool | `false` | Case-insensitive comparison of incoming observation values to the data item's value-space (CONDITION severities, enum members, etc.). |
| `enableValidation` | bool | `false` | Emit per-observation validation diagnostics on the `agent-validation` logger. |
| `inputValidationLevel` | enum: `Ignore` (`0`), `Warning` (`1`), `Remove` (`2`), `Strict` (`3`) | `Warning` | What the agent does when an observation or asset arrives that fails per-DataItem validation. `Ignore` accepts everything; `Warning` accepts and logs; `Remove` rejects but does not log; `Strict` rejects and logs. Governs `InvalidObservationAdded` and `InvalidAssetAdded`. |
| `deviceValidationLevel` | enum: `Ignore` (`0`), `Warning` (`1`), `Remove` (`2`), `Strict` (`3`) | `Warning` | What the agent does when device-shape validation fails on a Component, Composition, or DataItem while a Device is being added or normalised. Independent from `inputValidationLevel` — a common integrator profile is `inputValidationLevel: Strict` alongside `deviceValidationLevel: Warning` (reject bad observations, tolerate minor device-model drift). Governs `InvalidComponentAdded`, `InvalidCompositionAdded`, `InvalidDataItemAdded`, and `InvalidDeviceAdded`. |
| `deviceValidationLevel` | enum: `Ignore` (`0`), `Warning` (`1`), `Remove` (`2`), `Strict` (`3`) | mirrors `inputValidationLevel` when omitted (`Warning` when both are omitted) | What the agent does when device-shape validation fails on a Component, Composition, or DataItem while a Device is being added or normalised. Independent from `inputValidationLevel` — a common integrator profile is `inputValidationLevel: Strict` alongside `deviceValidationLevel: Warning` (reject bad observations, tolerate minor device-model drift). Governs `InvalidComponentAdded`, `InvalidCompositionAdded`, `InvalidDataItemAdded`, and `InvalidDeviceAdded`. When the key is omitted, the effective value mirrors `inputValidationLevel` — see `AgentConfiguration.Normalize`. |
| `allowEmptyResultForEnumEvents` | bool | `false` | When `true`, preserves an empty Result verbatim on VALUE-representation EVENT DataItems whose Type has a controlled vocabulary (`EXECUTION`, `CONTROLLER_MODE`, `AVAILABILITY`, and every other Event whose value is defined by an `MTConnect.Observations.Events.<Type>` enum). Default coerces the empty Result to `UNAVAILABLE`. Free-form String and Numeric-typed Events are unaffected — free-form String preserves the empty Result unconditionally; Numeric-typed always coerces. |
| `enableAgentDevice` | bool | `true` | Whether the agent emits its own meta-device (`Agent`) on `/probe`, exposing availability and the `mtconnect:ChangeToken` data item. |
| `enableMetrics` | bool | `true` | Emit per-minute observation-rate and asset-update-rate metrics on the `agent-metrics` logger. |
Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/agent-validation-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ The handler runs first; what the agent does next depends on the applicable knob
- **`Ignore`** — the event does not fire, and the input is kept. Useful only for debugging.
- **`Warning`** — the event fires; the input is kept.
- **`Remove`** — the event fires; the offending node is pruned from its parent (e.g. `device.RemoveDataItem(id)`), or the input is dropped for the observation / asset case.
- **`Strict`** — the event fires; the entire Device is rejected (the `AddDevice` call returns `false` and no part of the tree is added), or the observation / asset input is rejected.
- **`Strict`** — the event fires; the entire Device is rejected (the `AddDevice` call returns `null` and no part of the tree is added), or the observation / asset input call returns `false`.

## Contributor POV

Expand Down Expand Up @@ -196,10 +196,10 @@ The event family is designed to grow. When a new element class becomes validatab
var fired = false;
agent.InvalidDeviceModelAdded += (_, _, _) => fired = true;

var ok = agent.AddDevice(BrokenDeviceModelFixture());
var added = agent.AddDevice(BrokenDeviceModelFixture());

Assert.That(added, Is.Null);
Assert.That(fired, Is.True);
Assert.That(ok, Is.False);
Assert.That(agent.GetDevices(), Is.Empty);
}
```
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ Configuration for an MTConnect Agent
| `changeToken` | `ChangeToken` | `string` | An opaque token regenerated each time the configuration is saved, allowing consumers to detect that the configuration has changed. |
| `convertUnits` | `ConvertUnits` | `bool` | Gets or Sets the default for Converting Units when adding Observations |
| `defaultVersion` | `DefaultVersionValue` | `string` | The string form of <see cref="DefaultVersion"/> used for serialization; assigning a parseable version string updates <see cref="DefaultVersion"/>. |
| `deviceValidationLevel` | `DeviceValidationLevel` | `DeviceValidationLevel` | Gets or Sets the default Device (MTConnectDevices) validation level. 0 = Ignore, 1 = Warning, 2 = Remove, 3 = Strict |
| `deviceValidationLevel` | `DeviceValidationLevel` | `DeviceValidationLevel` | Gets or Sets the default Device (MTConnectDevices) validation level. 0 = Ignore, 1 = Warning, 2 = Remove, 3 = Strict. |
| `enableAgentDevice` | `EnableAgentDevice` | `bool` | Gets or Sets whether the Agent Device is output |
| `enableMetrics` | `EnableMetrics` | `bool` | Gets or Sets whether Metrics are captured (ex. ObserationUpdateRate, AssetUpdateRate) |
| `enableValidation` | `EnableValidation` | `bool` | Gets or Sets whether validation information is output |
| `ignoreObservationCase` | `IgnoreObservationCase` | `bool` | Gets or Sets the default for Ignoring the case of Observation values |
| `ignoreTimestamps` | `IgnoreTimestamps` | `bool` | Overwrite timestamps with the agent time. This will correct clock drift but will not give as accurate relative time since it will not take into consideration network latencies. This can be overridden on a per adapter basis. |
| `inputValidationLevel` | `InputValidationLevel` | `InputValidationLevel` | Gets or Sets the default Input (Observation or Asset) validation level. 0 = Ignore, 1 = Warning, 2 = Remove, 3 = Strict |
| `inputValidationLevel` | `InputValidationLevel` | `InputValidationLevel` | Gets or Sets the default Input (Observation or Asset) validation level. 0 = Ignore, 1 = Warning, 2 = Remove, 3 = Strict. |
| `observationBufferSize` | `ObservationBufferSize` | `uint` | The maximum number of Observations the agent can hold in its buffer |
| `timezoneOutput` | `TimeZoneOutput` | `string` | Sets the TimeZone to use when timestamps are output from the Agent |

Expand Down Expand Up @@ -197,7 +197,7 @@ Configuration for an MTConnect Agent
| `changeToken` | `ChangeToken` | `string` | An opaque token that changes whenever the underlying configuration source is reloaded, allowing consumers to detect that the configuration has been replaced. |
| `convertUnits` | `ConvertUnits` | `bool` | Gets the default for Converting Units when adding Observations |
| `defaultVersion` | `DefaultVersion` | `Version` | Gets the default MTConnect version to output response documents for. |
| `deviceValidationLevel` | `DeviceValidationLevel` | `DeviceValidationLevel` | Gets or Sets the default Device (MTConnectDevices) validation level. 0 = Ignore, 1 = Warning, 2 = Remove, 3 = Strict |
| `deviceValidationLevel` | `DeviceValidationLevel` | `DeviceValidationLevel` | Gets the default Device (MTConnectDevices) validation level. 0 = Ignore, 1 = Warning, 2 = Remove, 3 = Strict |
| `enableAgentDevice` | `EnableAgentDevice` | `bool` | Gets or Sets whether the Agent Device is output |
| `enableMetrics` | `EnableMetrics` | `bool` | Gets whether Metrics are captured (ex. ObserationUpdateRate, AssetUpdateRate) |
| `enableValidation` | `EnableValidation` | `bool` | Gets or Sets whether validation information is output |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ public enum DeviceValidationLevel
/// </summary>
Strict
}
}
}
14 changes: 6 additions & 8 deletions libraries/MTConnect.NET-Common/Agents/MTConnectAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2342,17 +2342,15 @@ private static bool IsEmptyResult(IObservationInput input)
/// Rewrites the observation's Result value to <see cref="Observation.Unavailable"/> and flags the input as unavailable.
/// </summary>
/// <remarks>
/// Removes any prior Result entry (so the Values collection does not carry a duplicate ValueKey),
/// adds the UNAVAILABLE sentinel, and sets <see cref="IObservationInput.IsUnavailable"/> so downstream
/// consumers that branch on the flag observe the coerced state. Spec authority: MTConnect Part 2
/// Devices Information Model - Observation Information Model - Representation - Observation Values.
/// Delegates the Values-collection housekeeping to
/// <see cref="ObservationInput.AddValue(string, object)"/>, which already
/// replaces any prior entry with the same ValueKey — the earlier hand-written
/// pre-filter (Where + ToList + assign) was redundant work. Spec authority:
/// MTConnect Part 2 Devices Information Model - Observation Information Model -
/// Representation - Observation Values.
/// </remarks>
private static void CoerceEmptyResultToUnavailable(IObservationInput input)
{
var preserved = (input.Values ?? Enumerable.Empty<ObservationValue>())
.Where(v => v.Key != ValueKeys.Result)
.ToList();
input.Values = preserved;
input.AddValue(ValueKeys.Result, Observation.Unavailable);
input.IsUnavailable = true;
}
Expand Down
Loading