fix(dcl): write List attributes as real MXAccess arrays (not a stringified List)

The Ipsen MoveIn e2e (after the supervisory-advise fix landed scalar writes)
exposed a second blocker: writes to List-typed attributes
(MoveInWorkOrderNumbers / MoveInPartNumbers, List<string>) hung at the 30s
device-write timeout while scalar writes succeeded.

InstanceActor.HandleSetDataAttribute already decodes a List attribute's
canonical JSON into a typed List<T> before the write (so the DCL can push a
real array), but RealMxGatewayClient.ToMxValue only had scalar cases — a
List<T> fell through to Convert.ToString and wrote the garbage string
"System.Collections.Generic.List`1[System.String]" to the array Galaxy
node, which the gateway's COM write rejected/blocked.

Add IReadOnlyList<bool|int|long|float|double|string|DateTimeOffset|DateTime>
cases that call the client package's typed array encoders
(VT_ARRAY|VT_BSTR etc.); List<DateTime> is mapped to DateTimeOffset. Covers
every element type AttributeValueCodec produces.
This commit is contained in:
Joseph Doherty
2026-06-17 06:07:40 -04:00
parent b5333d0f15
commit 30d07b91f4
@@ -435,6 +435,21 @@ public sealed class RealMxGatewayClient : IMxGatewayClient
string s => s.ToMxValue(), string s => s.ToMxValue(),
DateTimeOffset dto => dto.ToMxValue(), DateTimeOffset dto => dto.ToMxValue(),
DateTime dt => dt.ToMxValue(), DateTime dt => dt.ToMxValue(),
// List/array attribute values. The InstanceActor decodes a canonical JSON
// List attribute into a typed List<T> (AttributeValueCodec produces
// List<string|int|float|double|bool|DateTime>) before the write so the DCL
// can push a real MXAccess array. Map each to the client's typed array
// encoder. Without these a List<T> would fall through to Convert.ToString
// and write a garbage scalar string to an array node — which the gateway's
// COM write then rejects/blocks (the array-write hang).
IReadOnlyList<bool> lb => lb.ToMxValue(),
IReadOnlyList<int> li => li.ToMxValue(),
IReadOnlyList<long> ll => ll.ToMxValue(),
IReadOnlyList<float> lf => lf.ToMxValue(),
IReadOnlyList<double> ld => ld.ToMxValue(),
IReadOnlyList<string> ls => ls.ToMxValue(),
IReadOnlyList<DateTimeOffset> ldto => ldto.ToMxValue(),
IReadOnlyList<DateTime> ldt => ldt.Select(d => (DateTimeOffset)d).ToList().ToMxValue(),
// Fall back to invariant string for any other CLR type. // Fall back to invariant string for any other CLR type.
_ => Convert.ToString(value, CultureInfo.InvariantCulture)!.ToMxValue(), _ => Convert.ToString(value, CultureInfo.InvariantCulture)!.ToMxValue(),
}; };