feat(s7): STRING/WSTRING/CHAR/WCHAR and DTL/DATE/TOD/TIME/S5TIME codecs, TSAP/connection-type override, negotiated PDU surfaced (#632 #288 #289 #294 #295)
New S7DataType members WString/Char/WChar/Dtl/Date/Tod/Time/S5Time mapped onto existing DriverDataType (String / DateTime / Int32 ms); writes refuse out-of-range with BadOutOfRange. Optional localTsap/remoteTsap/connectionType through options, factory JSON, probe and AdminUI form. NegotiatedPduSize snapshotted at connect and exposed via GetDiagnostics (DriverHealth has no bag). Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -87,9 +87,24 @@ public static class S7DriverFactoryExtensions
|
||||
$"S7 driver config for '{driverInstanceId}' has SpanReadMaxGapBytes {spanReadMaxGapBytes}; " +
|
||||
"must be >= -1 — -1 disables span grouping and 0 merges only strictly adjacent tags.");
|
||||
|
||||
// Optional TSAP / connection-type override. Validated here (the single choke point) so a typo'd
|
||||
// TSAP fails the deploy rather than silently connecting with the slot-derived default.
|
||||
var localTsap = NormalizeTsap(dto.LocalTsap, driverInstanceId, "LocalTsap");
|
||||
var remoteTsap = NormalizeTsap(dto.RemoteTsap, driverInstanceId, "RemoteTsap");
|
||||
S7ConnectionType? connectionType = string.IsNullOrWhiteSpace(dto.ConnectionType)
|
||||
? null
|
||||
: ParseEnum<S7ConnectionType>(dto.ConnectionType, driverInstanceId, "ConnectionType");
|
||||
if (remoteTsap is not null && connectionType is not null)
|
||||
throw new InvalidOperationException(
|
||||
$"S7 driver config for '{driverInstanceId}' sets both RemoteTsap and ConnectionType; " +
|
||||
"ConnectionType only fills the first byte of the slot-derived remote TSAP, so choose one.");
|
||||
|
||||
return new S7DriverOptions
|
||||
{
|
||||
Host = dto.Host!,
|
||||
LocalTsap = localTsap,
|
||||
RemoteTsap = remoteTsap,
|
||||
ConnectionType = connectionType,
|
||||
Port = dto.Port ?? 102,
|
||||
CpuType = ParseEnum<S7CpuType>(dto.CpuType, driverInstanceId, "CpuType",
|
||||
fallback: S7CpuType.S71500),
|
||||
@@ -115,6 +130,23 @@ public static class S7DriverFactoryExtensions
|
||||
};
|
||||
}
|
||||
|
||||
private static string? NormalizeTsap(JsonElement? raw, string driverInstanceId, string field)
|
||||
{
|
||||
if (raw is not { } el || el.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined) return null;
|
||||
string? text = el.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => el.GetString(),
|
||||
JsonValueKind.Number when el.TryGetUInt16(out var n) => S7Tsap.Format(n),
|
||||
_ => "?",
|
||||
};
|
||||
if (string.IsNullOrWhiteSpace(text)) return null;
|
||||
if (!S7Tsap.TryParse(text, out var tsap))
|
||||
throw new InvalidOperationException(
|
||||
$"S7 driver config for '{driverInstanceId}' has {field} '{text}'; expected two hex bytes " +
|
||||
"such as \"01.00\" or \"03.01\" (or a 16-bit number).");
|
||||
return S7Tsap.Format(tsap);
|
||||
}
|
||||
|
||||
private static T ParseEnum<T>(string? raw, string driverInstanceId, string field,
|
||||
string? tagName = null, T? fallback = null) where T : struct, Enum
|
||||
{
|
||||
@@ -151,6 +183,12 @@ public static class S7DriverFactoryExtensions
|
||||
public short? Rack { get; init; }
|
||||
/// <summary>Gets the slot number.</summary>
|
||||
public short? Slot { get; init; }
|
||||
/// <summary>Gets the local TSAP override — hex text (<c>"01.00"</c>) or a 16-bit number.</summary>
|
||||
public JsonElement? LocalTsap { get; init; }
|
||||
/// <summary>Gets the remote TSAP override — hex text (<c>"03.01"</c>) or a 16-bit number.</summary>
|
||||
public JsonElement? RemoteTsap { get; init; }
|
||||
/// <summary>Gets the connection-type name (PG / OP / S7Basic); a string so AdminUI-authored configs bind.</summary>
|
||||
public string? ConnectionType { get; init; }
|
||||
/// <summary>Gets the connection timeout in milliseconds.</summary>
|
||||
public int? TimeoutMs { get; init; }
|
||||
/// <summary>Gets the authored raw tags (RawPath + TagConfig blob + WriteIdempotent) the deploy
|
||||
|
||||
Reference in New Issue
Block a user