chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)

Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.

- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
  the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
  mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
  integration, install).

Build green (0 errors); unit tests pass. Docs left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-17 01:55:28 -04:00
parent 69f02fed7f
commit a25593a9c6
1044 changed files with 365 additions and 343 deletions
@@ -0,0 +1,85 @@
using Google.Protobuf.WellKnownTypes;
using MxGateway.Contracts.Proto;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
/// <summary>
/// Translates boxed CLR values from <c>WriteRequest.Value</c> into gateway-side
/// <see cref="MxValue"/> instances. Inverse of <see cref="MxValueDecoder"/>.
/// Handles the seven Galaxy data types — Boolean, Int32, Int64, Float32, Float64,
/// String, DateTime — and their array variants. Null + unsupported types throw
/// <see cref="ArgumentException"/> so the IWritable caller can fail the write with a
/// clear status code rather than silently mis-typing the wire payload.
/// </summary>
internal static class MxValueEncoder
{
public static MxValue Encode(object? value)
{
if (value is null) return new MxValue { IsNull = true };
switch (value)
{
case bool b: return new MxValue { BoolValue = b };
case sbyte i8: return new MxValue { Int32Value = i8 };
case short i16: return new MxValue { Int32Value = i16 };
case int i32: return new MxValue { Int32Value = i32 };
case byte u8: return new MxValue { Int32Value = u8 };
case ushort u16: return new MxValue { Int32Value = u16 };
case uint u32 when u32 <= int.MaxValue: return new MxValue { Int32Value = (int)u32 };
case long i64: return new MxValue { Int64Value = i64 };
case ulong u64 when u64 <= long.MaxValue: return new MxValue { Int64Value = (long)u64 };
case float f32: return new MxValue { FloatValue = f32 };
case double f64: return new MxValue { DoubleValue = f64 };
case string s: return new MxValue { StringValue = s };
case DateTime dt: return new MxValue { TimestampValue = Timestamp.FromDateTime(EnsureUtc(dt)) };
case DateTimeOffset dto: return new MxValue { TimestampValue = Timestamp.FromDateTimeOffset(dto) };
case bool[] arr: return EncodeArray(arr, (mx, vs) => mx.BoolValues = ToBoolArray(vs));
case int[] arr: return EncodeArray(arr, (mx, vs) => mx.Int32Values = ToInt32Array(vs));
case long[] arr: return EncodeArray(arr, (mx, vs) => mx.Int64Values = ToInt64Array(vs));
case float[] arr: return EncodeArray(arr, (mx, vs) => mx.FloatValues = ToFloatArray(vs));
case double[] arr: return EncodeArray(arr, (mx, vs) => mx.DoubleValues = ToDoubleArray(vs));
case string[] arr: return EncodeArray(arr, (mx, vs) => mx.StringValues = ToStringArray(vs));
case DateTime[] arr: return EncodeArray(arr, (mx, vs) => mx.TimestampValues = ToTimestampArray(vs));
default:
throw new ArgumentException(
$"Cannot encode value of type {value.GetType()} as MxValue. Supported: " +
"bool, int / long (and their unsigned variants), float, double, string, DateTime, " +
"and their 1-D array variants.",
nameof(value));
}
}
private static MxValue EncodeArray<T>(T[] values, Action<MxArray, T[]> populate)
{
var array = new MxArray();
populate(array, values);
array.Dimensions.Add((uint)values.Length);
return new MxValue { ArrayValue = array };
}
private static BoolArray ToBoolArray(bool[] vs) { var a = new BoolArray(); a.Values.AddRange(vs); return a; }
private static Int32Array ToInt32Array(int[] vs) { var a = new Int32Array(); a.Values.AddRange(vs); return a; }
private static Int64Array ToInt64Array(long[] vs) { var a = new Int64Array(); a.Values.AddRange(vs); return a; }
private static FloatArray ToFloatArray(float[] vs) { var a = new FloatArray(); a.Values.AddRange(vs); return a; }
private static DoubleArray ToDoubleArray(double[] vs) { var a = new DoubleArray(); a.Values.AddRange(vs); return a; }
private static StringArray ToStringArray(string[] vs) { var a = new StringArray(); a.Values.AddRange(vs); return a; }
private static TimestampArray ToTimestampArray(DateTime[] vs)
{
var a = new TimestampArray();
foreach (var dt in vs) a.Values.Add(Timestamp.FromDateTime(EnsureUtc(dt)));
return a;
}
/// <summary>
/// <see cref="Timestamp.FromDateTime"/> requires UTC. Convert non-UTC inputs
/// explicitly so a caller passing local time gets predictable wire bytes.
/// </summary>
private static DateTime EnsureUtc(DateTime dt) => dt.Kind switch
{
DateTimeKind.Utc => dt,
DateTimeKind.Local => dt.ToUniversalTime(),
_ => DateTime.SpecifyKind(dt, DateTimeKind.Utc),
};
}