feat(mtconnect): parse the write.enabled config block
This commit is contained in:
@@ -109,6 +109,12 @@ public sealed class MTConnectDriverOptions
|
||||
/// <c>/sample</c> streaming connection.
|
||||
/// </summary>
|
||||
public MTConnectReconnectOptions Reconnect { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Write-back settings. Off by default — the driver shipped read-only, and enabling this is
|
||||
/// only half the gate: the Agent's own <c>AllowPut</c> must be on too.
|
||||
/// </summary>
|
||||
public MTConnectWriteOptions Write { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>Background connectivity-probe knobs, mirroring <c>ModbusProbeOptions</c>.</summary>
|
||||
@@ -132,3 +138,16 @@ public sealed class MTConnectReconnectOptions
|
||||
/// <summary>Multiplier applied each retry. Default <c>2.0</c> doubles each step.</summary>
|
||||
public double BackoffMultiplier { get; init; } = 2.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write-back knobs. A write is <b>observation injection</b> via the Agent's <c>AllowPut</c>
|
||||
/// surface — it sets what the Agent reports for a DataItem, not the machine itself (design §2.2).
|
||||
/// </summary>
|
||||
public sealed class MTConnectWriteOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether write-back is enabled. Default <c>false</c> — an existing
|
||||
/// deployment that never authors this block keeps today's read-only behaviour exactly.
|
||||
/// </summary>
|
||||
public bool Enabled { get; init; }
|
||||
}
|
||||
|
||||
@@ -2276,6 +2276,13 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis
|
||||
MaxBackoffMs = dto.Reconnect?.MaxBackoffMs ?? 30_000,
|
||||
BackoffMultiplier = dto.Reconnect?.BackoffMultiplier ?? 2.0,
|
||||
},
|
||||
|
||||
// Absent block == disabled: write-back is opt-in, and the agent's own AllowPut has to be
|
||||
// on as well, so neither layer can arm the other by omission.
|
||||
Write = new MTConnectWriteOptions
|
||||
{
|
||||
Enabled = dto.Write?.Enabled ?? false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3272,6 +3279,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis
|
||||
public List<RawTagEntry>? RawTags { get; init; }
|
||||
public MTConnectProbeDto? Probe { get; init; }
|
||||
public MTConnectReconnectDto? Reconnect { get; init; }
|
||||
public MTConnectWriteDto? Write { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -3347,4 +3355,10 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis
|
||||
public int? MaxBackoffMs { get; init; }
|
||||
public double? BackoffMultiplier { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Write-back knobs — the driver-side half of the two-layer opt-in on observation injection.</summary>
|
||||
private sealed class MTConnectWriteDto
|
||||
{
|
||||
public bool? Enabled { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,20 @@ public sealed class MTConnectDriverOptionsTests
|
||||
reconnect.BackoffMultiplier.ShouldBeGreaterThan(1.0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write-back is <b>off unless authored</b>: the driver shipped read-only, and the agent's
|
||||
/// PUT surface is itself off by default, so a deployment that never mentions <c>write</c>
|
||||
/// must keep exactly today's behaviour.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Write_defaults_to_disabled()
|
||||
{
|
||||
var write = new MTConnectDriverOptions { AgentUri = "http://agent:5000" }.Write;
|
||||
|
||||
write.ShouldNotBeNull();
|
||||
write.Enabled.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="MTConnectDriverOptions.Tags"/> defaults to an <i>empty</i> collection, never
|
||||
/// <c>null</c> — a null collection here is an operator-authorable NullReferenceException at
|
||||
|
||||
@@ -349,4 +349,51 @@ public sealed class MTConnectFactoryTests
|
||||
// above) hold for both entry points.
|
||||
MTConnectDriverFactoryExtensions.CreateInstance("mt1", json).DriverInstanceId.ShouldBe("mt1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <c>write</c> block reaches the options — the opt-in half of the two-layer gate on
|
||||
/// write-back (the agent's own <c>AllowPut</c> is the other). Authored <c>true</c> arms it;
|
||||
/// an authored <c>false</c> and an omitted block are the same thing, and both leave the
|
||||
/// driver read-only.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Write_section_round_trips_and_defaults_to_disabled_when_absent()
|
||||
{
|
||||
MTConnectDriver.ParseOptions(
|
||||
"mt1", """{ "agentUri": "http://x:5000", "write": { "enabled": true } }""")
|
||||
.Write.Enabled.ShouldBeTrue();
|
||||
|
||||
MTConnectDriver.ParseOptions(
|
||||
"mt1", """{ "agentUri": "http://x:5000", "write": { "enabled": false } }""")
|
||||
.Write.Enabled.ShouldBeFalse();
|
||||
|
||||
MTConnectDriver.ParseOptions("mt1", """{ "agentUri": "http://x:5000" }""")
|
||||
.Write.Enabled.ShouldBeFalse();
|
||||
|
||||
// The factory shares that parse, so an armed instance is authorable through it too.
|
||||
MTConnectDriverFactoryExtensions.CreateInstance(
|
||||
"mt1", """{ "agentUri": "http://x:5000", "write": { "enabled": true } }""")
|
||||
.DriverInstanceId.ShouldBe("mt1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A malformed <c>write</c> block fails <b>exactly as its siblings do</b> — the whole
|
||||
/// document is one <c>JsonSerializer.Deserialize</c>, so a non-boolean <c>enabled</c> faults
|
||||
/// the parse rather than silently defaulting to off. The <c>probe</c> leg is the control: it
|
||||
/// pins that this is the surrounding convention and not stricter behaviour invented here.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Malformed_write_block_faults_exactly_like_the_sibling_probe_block()
|
||||
{
|
||||
Should.Throw<InvalidOperationException>(() => MTConnectDriver.ParseOptions(
|
||||
"mt1", """{ "agentUri": "http://x:5000", "probe": { "enabled": "yes" } }"""));
|
||||
|
||||
Should.Throw<InvalidOperationException>(() => MTConnectDriver.ParseOptions(
|
||||
"mt1", """{ "agentUri": "http://x:5000", "write": { "enabled": "yes" } }"""));
|
||||
|
||||
// A null block is not malformed — the same "omitted means default" reading the sibling
|
||||
// blocks get, since every DTO property is nullable.
|
||||
MTConnectDriver.ParseOptions("mt1", """{ "agentUri": "http://x:5000", "write": null }""")
|
||||
.Write.Enabled.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user