364 lines
13 KiB
C#
364 lines
13 KiB
C#
using Google.Protobuf;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using MxGateway.Contracts.Proto;
|
|
|
|
namespace MxGateway.Client;
|
|
|
|
/// <summary>
|
|
/// Creates and projects gateway MXAccess values without hiding the raw
|
|
/// protobuf value carried by command replies and events.
|
|
/// </summary>
|
|
public static class MxValueExtensions
|
|
{
|
|
/// <summary>
|
|
/// Converts a boolean value to an MxValue with MxDataType.Boolean.
|
|
/// </summary>
|
|
/// <param name="value">Scalar boolean value to wrap.</param>
|
|
public static MxValue ToMxValue(this bool value)
|
|
{
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.Boolean,
|
|
VariantType = "VT_BOOL",
|
|
BoolValue = value,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a 32-bit integer value to an MxValue with MxDataType.Integer.
|
|
/// </summary>
|
|
/// <param name="value">32-bit integer value to wrap.</param>
|
|
public static MxValue ToMxValue(this int value)
|
|
{
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.Integer,
|
|
VariantType = "VT_I4",
|
|
Int32Value = value,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a 64-bit integer value to an MxValue with MxDataType.Integer.
|
|
/// </summary>
|
|
/// <param name="value">64-bit integer value to wrap.</param>
|
|
public static MxValue ToMxValue(this long value)
|
|
{
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.Integer,
|
|
VariantType = "VT_I8",
|
|
Int64Value = value,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a single-precision floating-point value to an MxValue with MxDataType.Float.
|
|
/// </summary>
|
|
/// <param name="value">Single-precision floating-point value to wrap.</param>
|
|
public static MxValue ToMxValue(this float value)
|
|
{
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.Float,
|
|
VariantType = "VT_R4",
|
|
FloatValue = value,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a double-precision floating-point value to an MxValue with MxDataType.Double.
|
|
/// </summary>
|
|
/// <param name="value">Double-precision floating-point value to wrap.</param>
|
|
public static MxValue ToMxValue(this double value)
|
|
{
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.Double,
|
|
VariantType = "VT_R8",
|
|
DoubleValue = value,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a string value to an MxValue with MxDataType.String.
|
|
/// </summary>
|
|
/// <param name="value">String value to wrap.</param>
|
|
public static MxValue ToMxValue(this string value)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(value);
|
|
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.String,
|
|
VariantType = "VT_BSTR",
|
|
StringValue = value,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a DateTimeOffset value to an MxValue with MxDataType.Time.
|
|
/// </summary>
|
|
/// <param name="value">DateTimeOffset value to wrap.</param>
|
|
public static MxValue ToMxValue(this DateTimeOffset value)
|
|
{
|
|
return new MxValue
|
|
{
|
|
DataType = MxDataType.Time,
|
|
VariantType = "VT_DATE",
|
|
TimestampValue = Timestamp.FromDateTimeOffset(value),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a DateTime value to an MxValue with MxDataType.Time.
|
|
/// </summary>
|
|
/// <param name="value">DateTime value to wrap.</param>
|
|
public static MxValue ToMxValue(this DateTime value)
|
|
{
|
|
return new DateTimeOffset(
|
|
value.Kind == DateTimeKind.Unspecified
|
|
? DateTime.SpecifyKind(value, DateTimeKind.Utc)
|
|
: value.ToUniversalTime())
|
|
.ToMxValue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a boolean array to an MxValue with MxDataType.Boolean.
|
|
/// </summary>
|
|
/// <param name="values">Array of boolean values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<bool> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a 32-bit integer array to an MxValue with MxDataType.Integer.
|
|
/// </summary>
|
|
/// <param name="values">Array of 32-bit integer values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<int> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a 64-bit integer array to an MxValue with MxDataType.Integer.
|
|
/// </summary>
|
|
/// <param name="values">Array of 64-bit integer values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<long> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a single-precision floating-point array to an MxValue with MxDataType.Float.
|
|
/// </summary>
|
|
/// <param name="values">Array of single-precision floating-point values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<float> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a double-precision floating-point array to an MxValue with MxDataType.Double.
|
|
/// </summary>
|
|
/// <param name="values">Array of double-precision floating-point values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<double> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a string array to an MxValue with MxDataType.String.
|
|
/// </summary>
|
|
/// <param name="values">Array of string values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<string> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a DateTimeOffset array to an MxValue with MxDataType.Time.
|
|
/// </summary>
|
|
/// <param name="values">Array of DateTimeOffset values to wrap.</param>
|
|
public static MxValue ToMxValue(this IReadOnlyList<DateTimeOffset> 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,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the projection kind (field name) of the given MxValue's current oneof value.
|
|
/// </summary>
|
|
/// <param name="value">The MxValue whose oneof projection kind is returned.</param>
|
|
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",
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts an MxValue to a CLR object; returns the boxed value or null for null MxValues.
|
|
/// </summary>
|
|
/// <param name="value">The MxValue to convert.</param>
|
|
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,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts an MxArray to a CLR array; returns null if the array does not have a known element type.
|
|
/// </summary>
|
|
/// <param name="array">The MxArray to convert.</param>
|
|
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,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an MxValue with MxDataType.Unknown from raw byte data, variant type, and diagnostic info.
|
|
/// </summary>
|
|
/// <param name="value">Raw byte data representing the value.</param>
|
|
/// <param name="variantType">Variant type string (e.g., "VT_BSTR").</param>
|
|
/// <param name="rawDiagnostic">Diagnostic string describing the raw value.</param>
|
|
/// <param name="rawDataType">Optional MXAccess data type override.</param>
|
|
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,
|
|
};
|
|
}
|
|
}
|