fix(heidenhain): Tool/Number, Tool/Index, Tool/AxisCode publish their declared integer types (#867)
HeidenhainSession.Leaf's `case Lsv2Tool t` switch expression mixed long, int and double? arms, so C# unified the natural type to double? and Tool/Number (Int64), Tool/Index and Tool/AxisCode (Int32) were published as Double. Every arm in that switch (and in the alarm-list, axes and feature-acquire switches in the same file) is now boxed to object explicitly so no numeric arm can widen another. New HeidenhainLeafRuntimeTypeTests asserts value.GetType() for every catalog value leaf against its declared DataType (scalar + array), plus PLC/DATA binding coercion for Int16/Int32/Int64/Boolean. Verified red on the old code (exactly the three Tool leaves) and green with the fix. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
This commit is contained in:
@@ -256,14 +256,15 @@ internal sealed class HeidenhainSession : IAsyncDisposable
|
||||
await PrepareTelemetryAsync(wire, ct).ConfigureAwait(false);
|
||||
value = feature switch
|
||||
{
|
||||
"Execution" => Lsv2Data.U16(await _client.ReadRuntimeAsync(23, ct).ConfigureAwait(false)),
|
||||
"ProgramState" => Lsv2Data.U16(await _client.ReadRuntimeAsync(26, ct).ConfigureAwait(false)),
|
||||
// Each arm boxed explicitly so no numeric arm can widen another (lmxopcua#867).
|
||||
"Execution" => (object)Lsv2Data.U16(await _client.ReadRuntimeAsync(23, ct).ConfigureAwait(false)),
|
||||
"ProgramState" => (object)Lsv2Data.U16(await _client.ReadRuntimeAsync(26, ct).ConfigureAwait(false)),
|
||||
"Program" => Lsv2Data.Program(await _client.ReadRuntimeAsync(24, ct).ConfigureAwait(false)),
|
||||
"Overrides" => Lsv2Data.Overrides(await _client.ReadRuntimeAsync(25, ct).ConfigureAwait(false)),
|
||||
"Axes" => Lsv2Data.Axes(await _client.ReadRuntimeAsync(22, ct).ConfigureAwait(false)),
|
||||
"Tool" => Lsv2Data.Tool(await _client.ReadRuntimeAsync(51, ct).ConfigureAwait(false)),
|
||||
HeidenhainCatalog.AlarmsFeature => await AlarmsAsync(ct).ConfigureAwait(false),
|
||||
_ => await TimerAsync(feature, ct).ConfigureAwait(false),
|
||||
_ => (object)await TimerAsync(feature, ct).ConfigureAwait(false),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -444,24 +445,27 @@ internal sealed class HeidenhainSession : IAsyncDisposable
|
||||
case double[] o when address.StartsWith("Overrides/", StringComparison.Ordinal):
|
||||
return o[name switch { "Feed" => 0, "Spindle" => 1, _ => 2 }];
|
||||
case Lsv2Axes a:
|
||||
return name == "Names" ? a.Names : a.Positions;
|
||||
return name == "Names" ? (object)a.Names : (object)a.Positions;
|
||||
case Lsv2Tool t:
|
||||
// lmxopcua#867: every arm is cast to object. Without it C# unifies long/int/double? to double?, so
|
||||
// Tool/Number (Int64) and Tool/Index + Tool/AxisCode (Int32) were published as Double. A null
|
||||
// geometry still boxes to null (BadNotSupported upstream).
|
||||
return name switch
|
||||
{
|
||||
"Number" => t.Number,
|
||||
"Index" => t.Index,
|
||||
"AxisCode" => t.Axis,
|
||||
"Length" => t.Length,
|
||||
_ => t.Radius,
|
||||
"Number" => (object)t.Number,
|
||||
"Index" => (object)t.Index,
|
||||
"AxisCode" => (object)t.Axis,
|
||||
"Length" => (object?)t.Length,
|
||||
_ => (object?)t.Radius,
|
||||
};
|
||||
case List<Lsv2Alarm> list:
|
||||
return name switch
|
||||
{
|
||||
"Count" => list.Count,
|
||||
"Messages" => list.Select(x => x.Message).ToArray(),
|
||||
"Classes" => list.Select(x => x.ErrorClass).ToArray(),
|
||||
"Groups" => list.Select(x => x.Group).ToArray(),
|
||||
_ => list.Select(x => x.Number).ToArray(),
|
||||
"Count" => (object)list.Count,
|
||||
"Messages" => (object)list.Select(x => x.Message).ToArray(),
|
||||
"Classes" => (object)list.Select(x => x.ErrorClass).ToArray(),
|
||||
"Groups" => (object)list.Select(x => x.Group).ToArray(),
|
||||
_ => (object)list.Select(x => x.Number).ToArray(),
|
||||
};
|
||||
case int i:
|
||||
return i;
|
||||
|
||||
Reference in New Issue
Block a user