using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using MxGateway.Contracts.Proto; namespace MxGateway.Client; /// /// Creates and projects gateway MXAccess values without hiding the raw /// protobuf value carried by command replies and events. /// public static class MxValueExtensions { /// /// Converts a boolean value to an MxValue with MxDataType.Boolean. /// /// Scalar boolean value to wrap. public static MxValue ToMxValue(this bool value) { return new MxValue { DataType = MxDataType.Boolean, VariantType = "VT_BOOL", BoolValue = value, }; } /// /// Converts a 32-bit integer value to an MxValue with MxDataType.Integer. /// /// 32-bit integer value to wrap. public static MxValue ToMxValue(this int value) { return new MxValue { DataType = MxDataType.Integer, VariantType = "VT_I4", Int32Value = value, }; } /// /// Converts a 64-bit integer value to an MxValue with MxDataType.Integer. /// /// 64-bit integer value to wrap. public static MxValue ToMxValue(this long value) { return new MxValue { DataType = MxDataType.Integer, VariantType = "VT_I8", Int64Value = value, }; } /// /// Converts a single-precision floating-point value to an MxValue with MxDataType.Float. /// /// Single-precision floating-point value to wrap. public static MxValue ToMxValue(this float value) { return new MxValue { DataType = MxDataType.Float, VariantType = "VT_R4", FloatValue = value, }; } /// /// Converts a double-precision floating-point value to an MxValue with MxDataType.Double. /// /// Double-precision floating-point value to wrap. public static MxValue ToMxValue(this double value) { return new MxValue { DataType = MxDataType.Double, VariantType = "VT_R8", DoubleValue = value, }; } /// /// Converts a string value to an MxValue with MxDataType.String. /// /// String value to wrap. public static MxValue ToMxValue(this string value) { ArgumentNullException.ThrowIfNull(value); return new MxValue { DataType = MxDataType.String, VariantType = "VT_BSTR", StringValue = value, }; } /// /// Converts a DateTimeOffset value to an MxValue with MxDataType.Time. /// /// DateTimeOffset value to wrap. public static MxValue ToMxValue(this DateTimeOffset value) { return new MxValue { DataType = MxDataType.Time, VariantType = "VT_DATE", TimestampValue = Timestamp.FromDateTimeOffset(value), }; } /// /// Converts a DateTime value to an MxValue with MxDataType.Time. /// /// DateTime value to wrap. public static MxValue ToMxValue(this DateTime value) { return new DateTimeOffset( value.Kind == DateTimeKind.Unspecified ? DateTime.SpecifyKind(value, DateTimeKind.Utc) : value.ToUniversalTime()) .ToMxValue(); } /// /// Converts a boolean array to an MxValue with MxDataType.Boolean. /// /// Array of boolean values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new BoolArray(); array.Values.Add(values); return CreateArrayValue(MxDataType.Boolean, "VT_ARRAY|VT_BOOL", values.Count, new MxArray { ElementDataType = MxDataType.Boolean, VariantType = "VT_ARRAY|VT_BOOL", BoolValues = array, }); } /// /// Converts a 32-bit integer array to an MxValue with MxDataType.Integer. /// /// Array of 32-bit integer values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new Int32Array(); array.Values.Add(values); return CreateArrayValue(MxDataType.Integer, "VT_ARRAY|VT_I4", values.Count, new MxArray { ElementDataType = MxDataType.Integer, VariantType = "VT_ARRAY|VT_I4", Int32Values = array, }); } /// /// Converts a 64-bit integer array to an MxValue with MxDataType.Integer. /// /// Array of 64-bit integer values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new Int64Array(); array.Values.Add(values); return CreateArrayValue(MxDataType.Integer, "VT_ARRAY|VT_I8", values.Count, new MxArray { ElementDataType = MxDataType.Integer, VariantType = "VT_ARRAY|VT_I8", Int64Values = array, }); } /// /// Converts a single-precision floating-point array to an MxValue with MxDataType.Float. /// /// Array of single-precision floating-point values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new FloatArray(); array.Values.Add(values); return CreateArrayValue(MxDataType.Float, "VT_ARRAY|VT_R4", values.Count, new MxArray { ElementDataType = MxDataType.Float, VariantType = "VT_ARRAY|VT_R4", FloatValues = array, }); } /// /// Converts a double-precision floating-point array to an MxValue with MxDataType.Double. /// /// Array of double-precision floating-point values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new DoubleArray(); array.Values.Add(values); return CreateArrayValue(MxDataType.Double, "VT_ARRAY|VT_R8", values.Count, new MxArray { ElementDataType = MxDataType.Double, VariantType = "VT_ARRAY|VT_R8", DoubleValues = array, }); } /// /// Converts a string array to an MxValue with MxDataType.String. /// /// Array of string values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new StringArray(); array.Values.Add(values); return CreateArrayValue(MxDataType.String, "VT_ARRAY|VT_BSTR", values.Count, new MxArray { ElementDataType = MxDataType.String, VariantType = "VT_ARRAY|VT_BSTR", StringValues = array, }); } /// /// Converts a DateTimeOffset array to an MxValue with MxDataType.Time. /// /// Array of DateTimeOffset values to wrap. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); var array = new TimestampArray(); array.Values.Add(values.Select(Timestamp.FromDateTimeOffset)); return CreateArrayValue(MxDataType.Time, "VT_ARRAY|VT_DATE", values.Count, new MxArray { ElementDataType = MxDataType.Time, VariantType = "VT_ARRAY|VT_DATE", TimestampValues = array, }); } /// /// Gets the projection kind (field name) of the given MxValue's current oneof value. /// /// The MxValue whose oneof projection kind is returned. public static string GetProjectionKind(this MxValue value) { ArgumentNullException.ThrowIfNull(value); return value.KindCase switch { MxValue.KindOneofCase.BoolValue => "boolValue", MxValue.KindOneofCase.Int32Value => "int32Value", MxValue.KindOneofCase.Int64Value => "int64Value", MxValue.KindOneofCase.FloatValue => "floatValue", MxValue.KindOneofCase.DoubleValue => "doubleValue", MxValue.KindOneofCase.StringValue => "stringValue", MxValue.KindOneofCase.TimestampValue => "timestampValue", MxValue.KindOneofCase.ArrayValue => "arrayValue", MxValue.KindOneofCase.RawValue => "rawValue", _ => value.IsNull ? "nullValue" : "unspecified", }; } /// /// Converts an MxValue to a CLR object; returns the boxed value or null for null MxValues. /// /// The MxValue to convert. public static object? ToClrValue(this MxValue value) { ArgumentNullException.ThrowIfNull(value); return value.KindCase switch { MxValue.KindOneofCase.BoolValue => value.BoolValue, MxValue.KindOneofCase.Int32Value => value.Int32Value, MxValue.KindOneofCase.Int64Value => value.Int64Value, MxValue.KindOneofCase.FloatValue => value.FloatValue, MxValue.KindOneofCase.DoubleValue => value.DoubleValue, MxValue.KindOneofCase.StringValue => value.StringValue, MxValue.KindOneofCase.TimestampValue => value.TimestampValue.ToDateTimeOffset(), MxValue.KindOneofCase.ArrayValue => value.ArrayValue.ToClrArrayValue(), MxValue.KindOneofCase.RawValue => value.RawValue.ToByteArray(), _ => value.IsNull ? null : value, }; } /// /// Converts an MxArray to a CLR array; returns null if the array does not have a known element type. /// /// The MxArray to convert. public static object? ToClrArrayValue(this MxArray array) { ArgumentNullException.ThrowIfNull(array); return array.ValuesCase switch { MxArray.ValuesOneofCase.BoolValues => array.BoolValues.Values.ToArray(), MxArray.ValuesOneofCase.Int32Values => array.Int32Values.Values.ToArray(), MxArray.ValuesOneofCase.Int64Values => array.Int64Values.Values.ToArray(), MxArray.ValuesOneofCase.FloatValues => array.FloatValues.Values.ToArray(), MxArray.ValuesOneofCase.DoubleValues => array.DoubleValues.Values.ToArray(), MxArray.ValuesOneofCase.StringValues => array.StringValues.Values.ToArray(), MxArray.ValuesOneofCase.TimestampValues => array.TimestampValues.Values .Select(timestamp => timestamp.ToDateTimeOffset()) .ToArray(), MxArray.ValuesOneofCase.RawValues => array.RawValues.Values .Select(value => value.ToByteArray()) .ToArray(), _ => null, }; } /// /// Creates an MxValue with MxDataType.Unknown from raw byte data, variant type, and diagnostic info. /// /// Raw byte data representing the value. /// Variant type string (e.g., "VT_BSTR"). /// Diagnostic string describing the raw value. /// Optional MXAccess data type override. public static MxValue ToRawMxValue( byte[] value, string variantType, string rawDiagnostic, int rawDataType = 0) { ArgumentNullException.ThrowIfNull(value); return new MxValue { DataType = MxDataType.Unknown, VariantType = variantType, RawDiagnostic = rawDiagnostic, RawDataType = rawDataType, RawValue = ByteString.CopyFrom(value), }; } private static MxValue CreateArrayValue( MxDataType dataType, string variantType, int length, MxArray array) { array.Dimensions.Add((uint)length); return new MxValue { DataType = dataType, VariantType = variantType, ArrayValue = array, }; } }