953 lines
46 KiB
C#
953 lines
46 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using XYLEM.Communication;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
using XYLEM.Base;
|
|
using XYLEM.Base.Files;
|
|
using XYLEM.Device.Value;
|
|
using System.Numerics;
|
|
using XYLEM.Communication.ValueEncoders;
|
|
using XYLEM.Base.XML;
|
|
|
|
namespace XYLEM.Communication.ValueEncoders
|
|
{
|
|
/// <summary>
|
|
/// Return used character set used for email or empty if not Loaded from setup
|
|
/// </summary>
|
|
/// <returns>ex. "iso-8859-1"</returns>
|
|
public delegate string GetEmailCharacterSetToUse();
|
|
|
|
public static class DeviceValueEncoder
|
|
{
|
|
public static object Default(this ValueDataType dataType)
|
|
{
|
|
try
|
|
{
|
|
switch (dataType)
|
|
{
|
|
case ValueDataType.U8:
|
|
case ValueDataType.B8: return (byte)0;
|
|
case ValueDataType.S8: return (sbyte)0;
|
|
|
|
case ValueDataType.B16:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
return (UInt16)0;
|
|
case ValueDataType.U32TIME_Y1970:
|
|
case ValueDataType.U32TIME_Y1970_UTC: return ((UInt32)0).Convert1970SecToTime(TimeType.UTC);
|
|
case ValueDataType.U32TIME_Y2000: return ((UInt32)0).Convert2000SecToTime();
|
|
case ValueDataType.U32MJK_SN: return ((UInt32)0);
|
|
//case DeviceValueDataType.U24DateHH_MM_SS:
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: return ((UInt32)0);
|
|
//case DeviceValueDataType.U24:
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC:
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32: return (UInt32)0;
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64: return (UInt64)0;
|
|
case ValueDataType.F64: return (double)0;
|
|
case ValueDataType.U128: return (BigInteger)0;
|
|
case ValueDataType.S16: return (Int16)0;
|
|
case ValueDataType.F32: return (Single)0;
|
|
//case DeviceValueDataType.U32IPV4: return "0.0.0.0"; // handled as a string
|
|
default:
|
|
if (dataType.IsText())
|
|
{ // Do this as the last check to save CPU power
|
|
return "";
|
|
}
|
|
throw new Exception(dataType.ToString() + "has no default value)");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Default()", "dataType = " + dataType.ToString()));
|
|
}
|
|
}
|
|
|
|
public static string EncodeXML(this object value, ValueDataType type)
|
|
{
|
|
try
|
|
{
|
|
string valueAsTxt = value.ToString();
|
|
switch (type)
|
|
{
|
|
case ValueDataType.U32TIME_Y1970:
|
|
case ValueDataType.U32TIME_Y1970_UTC:
|
|
if (value is DateTime)
|
|
{
|
|
var value32bit = ((DateTime)value).ConvertTimeTo1970Sec(TimeType.Local);
|
|
return XmlConvert.ToString(value32bit);
|
|
}
|
|
return XmlConvert.ToString((Double)value);
|
|
case ValueDataType.U32TIME_Y2000:
|
|
if (value is DateTime)
|
|
{
|
|
var value32bit = ((DateTime)value).ConvertTimeTo2000Sec(TimeType.Local);
|
|
return XmlConvert.ToString(value32bit);
|
|
}
|
|
return XmlConvert.ToString((Double)value);
|
|
case ValueDataType.U8:
|
|
case ValueDataType.B8:
|
|
case ValueDataType.S8:
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC:
|
|
case ValueDataType.B16:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS:
|
|
//case DeviceValueDataType.U24DateHH_MM_SS:
|
|
//case DeviceValueDataType.U24:
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32:
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64:
|
|
case ValueDataType.S16:
|
|
case ValueDataType.S32:
|
|
case ValueDataType.S64:
|
|
case ValueDataType.F32:
|
|
case ValueDataType.F64:
|
|
case ValueDataType.U32MJK_SN:
|
|
{
|
|
if (typeof(Double) == value.GetType())
|
|
{
|
|
valueAsTxt = XmlConvert.ToString((Double)value);
|
|
}
|
|
else if (typeof(Single) == value.GetType())
|
|
{
|
|
valueAsTxt = XmlConvert.ToString((Single)value);
|
|
}
|
|
return valueAsTxt;
|
|
}
|
|
case ValueDataType.U128:
|
|
if (typeof(BigInteger) == value.GetType())
|
|
{
|
|
valueAsTxt = ((BigInteger)value).ToString("X"); // enclode as hex
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"{value.GetType()} is not supported for U128 xml convert");
|
|
}
|
|
return valueAsTxt;
|
|
|
|
//case DeviceValueDataType.U32IPV4: return valueAsTxt.Trim( ).StripIllegalXMLChars( );// handled as a string
|
|
default:
|
|
if (type.IsText())
|
|
{ // Do this as the last check to save CPU power
|
|
return valueAsTxt.Trim().StripIllegalXMLChars();
|
|
}
|
|
throw new NotImplementedException("DataType '" + type + "' not recognized.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "EncodeXML()", "dataType = " + type.ToString() + " Value is = " + value.ToString()));
|
|
}
|
|
}
|
|
|
|
public static Object DecodeXML(this string valueInXml, ValueDataType type)
|
|
{
|
|
try
|
|
{
|
|
switch (type)
|
|
{
|
|
case ValueDataType.U32TIME_Y1970:
|
|
case ValueDataType.U32TIME_Y1970_UTC:
|
|
return XmlConvert.ToUInt32(valueInXml).Convert1970SecToTime(TimeType.Local);
|
|
case ValueDataType.U32TIME_Y2000:
|
|
return XmlConvert.ToUInt32(valueInXml).Convert2000SecToTime();
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC: return XmlConvert.ToUInt32( valueInXml );
|
|
case ValueDataType.B8:
|
|
case ValueDataType.U8:
|
|
return XmlConvert.ToByte(valueInXml);
|
|
case ValueDataType.S8:
|
|
return XmlConvert.ToSByte(valueInXml);
|
|
case ValueDataType.B16:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
return XmlConvert.ToUInt16(valueInXml);
|
|
//case DeviceValueDataType.U24DateHH_MM_SS:
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS:
|
|
//case DeviceValueDataType.U24:
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32:
|
|
case ValueDataType.U32MJK_SN:
|
|
return XmlConvert.ToUInt32(valueInXml);
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64: return XmlConvert.ToUInt64(valueInXml);
|
|
case ValueDataType.S16: return XmlConvert.ToInt16(valueInXml);
|
|
case ValueDataType.S32: return XmlConvert.ToInt32(valueInXml);
|
|
case ValueDataType.S64: return XmlConvert.ToInt64(valueInXml);
|
|
case ValueDataType.F32: return XmlConvert.ToSingle(valueInXml);
|
|
case ValueDataType.F64: return XmlConvert.ToDouble(valueInXml);
|
|
case ValueDataType.U128: return BigInteger.Parse(valueInXml, System.Globalization.NumberStyles.HexNumber);
|
|
//case DeviceValueDataType.U32IPV4: return valueInXml.ReinsetIllegalXMLChars( );// handled as a string
|
|
default:
|
|
if (type.IsText())
|
|
{ // Do this as the last check to save CPU power
|
|
return valueInXml.ReinsetIllegalXMLChars();
|
|
}
|
|
throw new NotImplementedException("DataType '" + type + "' not recognized.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "DecodeXML()", "dataType = " + type.ToString() + " Value is = " + valueInXml));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// do layout changes on byte stream
|
|
/// </summary>
|
|
/// <param name="bytes"></param>
|
|
/// <param name="layout"></param>
|
|
/// <returns></returns>
|
|
static byte[] DoLayout(this byte[] bytes, LayoutType layout = LayoutType.Normal)
|
|
{
|
|
try
|
|
{
|
|
switch (layout)
|
|
{
|
|
case LayoutType.Normal: return bytes; ; // Do nothing
|
|
case LayoutType.SwapAll: return bytes.Reverse();
|
|
default:
|
|
throw new NotSupportedException(layout + " is not supported");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "DoLayout", "layout = " + layout));
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Encode data with normal data layout
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <param name="dataType"></param>
|
|
/// <param name="EmailCharacterSet">Null if not used</param>
|
|
/// <returns></returns>
|
|
public static byte[] Encode(this object value, ValueDataType dataType, GetEmailCharacterSetToUse emailCharacterSet = null)
|
|
{
|
|
try
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new Exception("Value is null");
|
|
}
|
|
switch (dataType)
|
|
{
|
|
case ValueDataType.B8:
|
|
case ValueDataType.U8:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(byte));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((byte)value);
|
|
}
|
|
case ValueDataType.S8:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(sbyte));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((sbyte)value);
|
|
}
|
|
case ValueDataType.B16:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
return BitConverter.GetBytes((UInt16)value);
|
|
case ValueDataType.U32TIME_Y1970:
|
|
if (value is DateTime)
|
|
{
|
|
return BitConverter.GetBytes(((DateTime)value).ConvertTimeTo1970Sec(TimeType.Local));
|
|
}
|
|
return BitConverter.GetBytes((UInt32)value); // Work around on olde types
|
|
case ValueDataType.U32TIME_Y1970_UTC:
|
|
if (value is DateTime)
|
|
{
|
|
return BitConverter.GetBytes(((DateTime)value).ConvertTimeTo1970Sec(TimeType.UTC));
|
|
}
|
|
return BitConverter.GetBytes((UInt32)value); // Work around on olde types
|
|
case ValueDataType.U32TIME_Y2000:
|
|
if (value is DateTime)
|
|
{
|
|
return BitConverter.GetBytes(((DateTime)value).ConvertTimeTo2000Sec(TimeType.Local));
|
|
}
|
|
return BitConverter.GetBytes((UInt32)value);
|
|
// Work around on olde types
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC:
|
|
// return BitConverter.GetBytes( TimeConvertClass.ToMidnightSecOffsetUTC( (UInt32)value ) );
|
|
//case DeviceValueDataType.U24DateHH_MM_SS: {
|
|
// var timeSpan = TimeConvertClass.ConvertTimeSpan( (UInt32)value );
|
|
// return new byte[] { (byte)timeSpan.Seconds, (byte)timeSpan.Minutes, (byte)timeSpan.Hours };
|
|
// }
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: {
|
|
// var date = ((UInt32)value).Convert1970SecToTime( TimeType.Local );
|
|
// return new byte[] {
|
|
// (byte)date.Second,
|
|
// (byte)date.Minute,
|
|
// (byte)date.Hour,
|
|
// (byte)date.Day,
|
|
// (byte)date.Month,
|
|
// (byte)(date.Year - 2000)
|
|
// };
|
|
// }
|
|
//case DeviceValueDataType.U24: {
|
|
// value = CorrectTypeIfNotTheSame( value, dataType, typeof( UInt32 ) );// Try set type if not the correct one
|
|
// return BitConverter.GetBytes( (UInt32)value ).Remove( 3 );
|
|
// }
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32:
|
|
case ValueDataType.U32MJK_SN:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(UInt32));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((UInt32)value);
|
|
}
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(UInt64));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((UInt64)value);
|
|
}
|
|
case ValueDataType.S16:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(Int16));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((Int16)value);
|
|
}
|
|
case ValueDataType.S32:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(Int32));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((Int32)value);
|
|
}
|
|
case ValueDataType.F32:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(Single));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((Single)value);
|
|
}
|
|
case ValueDataType.F64:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(double));// Try set type if not the correct one
|
|
return BitConverter.GetBytes((double)value);
|
|
}
|
|
case ValueDataType.U128:
|
|
{
|
|
value = CorrectTypeIfNotTheSame(value, dataType, typeof(BigInteger));// Try set type if not the correct one
|
|
return ((BigInteger)value).ToByteArray(); // TODO: Is this okay or need always to be the 16 byte in size?
|
|
}
|
|
//case DeviceValueDataType.U32IPV4: return U32_IP_Encoder.Encode( value.ToString( ) );
|
|
default:
|
|
if (dataType.IsText())
|
|
{ // Do this as the last check to save CPU power
|
|
var typeAsText = dataType.ToString();
|
|
// is Email
|
|
if (dataType.IsEmailText())
|
|
{
|
|
return StringEncoderEmail.Encode(value.ToString(), Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet()));
|
|
}
|
|
// is SMS
|
|
if (dataType.IsSMSText())
|
|
{
|
|
return StringEncoderUnicode.Encode(value.ToString(), Length(dataType));
|
|
}
|
|
// is Windows 1252
|
|
if (dataType.IsWindows1252Text())
|
|
{
|
|
return StringEncoder_W1252.Encode(value.ToString(), Length(dataType));
|
|
}
|
|
// is Windows 1252, but needs to have "<>" in frond and end
|
|
// Ex. end result is "<tmo@xyleminc.com>,<tmo@mjk.dk>" or "<tmo@xyleminc.com>"
|
|
if (dataType.IsWindows1252TextRecipient())
|
|
{
|
|
var valueAsText = value.ToString().Trim();
|
|
if (!String.IsNullOrEmpty(valueAsText))
|
|
{
|
|
// Remove manual added brackets or " if user has done it in the past
|
|
if (valueAsText.Contains("\""))
|
|
{
|
|
valueAsText = valueAsText.Replace("\"", "");
|
|
}
|
|
//// work arround if user has already used "," as separation
|
|
//if (valueAsText.Contains( "," )) {
|
|
// valueAsText = valueAsText.Replace( ",", ">,<" ); //old modem firmware V1.003 and TDC NO
|
|
//}
|
|
// Replace ";" with correct "," as separation and add bracked ">" "<" to support RCF821
|
|
if (valueAsText.Contains(";"))
|
|
{
|
|
//valueAsText = valueAsText.Replace( ";", ">,<" ); old modem firmware V1.003 and TDC NO
|
|
valueAsText = valueAsText.Replace(";", ",");//new modem firmware V1.3 using mail.mjk-link.dk
|
|
}
|
|
// Trim
|
|
valueAsText = valueAsText.Trim();
|
|
// Add brackets "<>" around address / addresses and end with " to tell modem where it starts and end in MC55I at command
|
|
//Ex. end result is "<tmo@xyleminc.com>,<tmo@mjk.dk>" or "<tmo@xyleminc.com>"
|
|
if (!String.IsNullOrEmpty(valueAsText))
|
|
{
|
|
//valueAsText = "\"<" + valueAsText + "\">"; old modem firmware V1.003 and TDC NO
|
|
valueAsText = "\"" + valueAsText + "\""; //new modem firmware V1.3 using mail.mjk-link.dk
|
|
}
|
|
}
|
|
return StringEncoder_W1252.Encode(valueAsText, Length(dataType));
|
|
}
|
|
// is Quoted Printable Text
|
|
if (dataType.IsEmailQuotedPrintableText())
|
|
{
|
|
return StringEncoderEmail_Q_Types.Encode(value.ToString(), Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet()));
|
|
}
|
|
// Is is normal utf8
|
|
return StringEncoder.Encode(value.ToString(), Length(dataType));
|
|
}
|
|
throw new NotImplementedException("DataType '" + dataType + "' not recognized.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Encode", "dataType = " + dataType.ToString()));
|
|
}
|
|
}
|
|
|
|
private static object CorrectTypeIfNotTheSame(object value, ValueDataType correctTypeTag, Type typeUsedForCast)
|
|
{
|
|
try
|
|
{
|
|
if (value.GetType() == typeUsedForCast)
|
|
{
|
|
return value; // Nothing to change
|
|
}
|
|
{
|
|
AppConst.Logger.AddFuncLog(
|
|
#if DEBUG //
|
|
true // log as error
|
|
#else
|
|
false // Log silent in log
|
|
#endif //#if DEBUG
|
|
, typeof(DeviceValueEncoder).ToString(), "CorrectTypeIfNotTheSame()",
|
|
"Warning! correction was needed. Is this a older setting else not okay? " + MakeCorrectTypeValueInfoText(value, correctTypeTag, typeUsedForCast)
|
|
);
|
|
}
|
|
return ToValue(value.ToString(), correctTypeTag);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var valueInfo = MakeCorrectTypeValueInfoText(value, correctTypeTag, typeUsedForCast);
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "CorrectTypeIfNotTheSame", valueInfo));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate the value info possible to get for a value to convert
|
|
/// </summary>
|
|
/// <param name="value">Value to correct</param>
|
|
/// <param name="correctTypeTag">correct value type tag</param>
|
|
/// <param name="typeUsedForCast">type used for cast</param>
|
|
/// <returns>Text to show in log</returns>
|
|
private static string MakeCorrectTypeValueInfoText(object value, ValueDataType correctTypeTag, Type typeUsedForCast)
|
|
{
|
|
var ValueInfo = "";
|
|
try
|
|
{
|
|
if (ValueInfo == null)
|
|
{
|
|
ValueInfo += " Value= IS NULL and not possible to correct!";
|
|
}
|
|
else
|
|
{
|
|
ValueInfo += " Value=\"" + value.ToString() + "\"";
|
|
ValueInfo += " From Type=\"" + value.GetType() + "\"";
|
|
}
|
|
ValueInfo += " To Type=\"" + correctTypeTag.ToString() + "\"";
|
|
}
|
|
catch { ValueInfo += " [Fail to get info ?] "; }
|
|
return ValueInfo;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encode data with data layout setting
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <param name="dataType"></param>
|
|
/// <param name="layout"></param>
|
|
/// <param name="emailCharacterSet">Null if not used</param>
|
|
/// <returns></returns>
|
|
public static byte[] Encode(this object value, ValueDataType dataType, LayoutType layout, GetEmailCharacterSetToUse emailCharacterSet = null)
|
|
{
|
|
try
|
|
{
|
|
var bytes = Encode(value, dataType, emailCharacterSet);
|
|
if (bytes == null)
|
|
{
|
|
return bytes;
|
|
}
|
|
return bytes.DoLayout(layout);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Encode", "dataType = " + dataType.ToString()));
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Decode data with normal data layout
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <param name="dataType"></param>
|
|
/// <param name="emailCharacterSet">Null if not used</param>
|
|
/// <returns></returns>
|
|
public static object Decode(this byte[] value, ValueDataType dataType, GetEmailCharacterSetToUse emailCharacterSet = null)
|
|
{
|
|
try
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new Exception("Value is null");
|
|
}
|
|
if (value.Count() == 0)
|
|
{
|
|
throw new Exception("Value Has no values in array");
|
|
}
|
|
switch (dataType)
|
|
{
|
|
case ValueDataType.B8:
|
|
case ValueDataType.U8:
|
|
return (byte)value.First();
|
|
case ValueDataType.S8:
|
|
return (sbyte)value.First();
|
|
case ValueDataType.B16:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
return BitConverter.ToUInt16(value, 0);
|
|
case ValueDataType.U32TIME_Y1970:
|
|
return BitConverter.ToUInt32(value, 0).Convert1970SecToTime(TimeType.Local);
|
|
case ValueDataType.U32TIME_Y1970_UTC:
|
|
return BitConverter.ToUInt32(value, 0).Convert1970SecToTime(TimeType.UTC).ToLocalTime();
|
|
case ValueDataType.U32TIME_Y2000:
|
|
return BitConverter.ToUInt32(value, 0).Convert2000SecToTime();
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC:
|
|
// return TimeConvertClass.FromMidnightSecOffsetUTC( BitConverter.ToUInt32( value, 0 ) );
|
|
//case DeviceValueDataType.U24DateHH_MM_SS: {
|
|
// return (UInt32)((UInt32)value[ 2 ] * 3600) + ((UInt32)value[ 1 ] * 60 + (UInt32)value[ 0 ]);
|
|
// }
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: {
|
|
// var date = new DateTime( (int)value[ 5 ] + 2000, value[ 4 ], value[ 3 ], value[ 2 ], value[ 1 ], value[ 0 ] );
|
|
// return date.ConvertTimeTo1970Sec( TimeType.Local );
|
|
// }
|
|
//case DeviceValueDataType.U24: {
|
|
// var as32bitValue = new byte[] { value[ 0 ], value[ 1 ], value[ 2 ], 0 };
|
|
// return BitConverter.ToUInt32( as32bitValue, 0 );
|
|
// }
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32:
|
|
case ValueDataType.U32MJK_SN:
|
|
return BitConverter.ToUInt32(value, 0);
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64: return BitConverter.ToUInt64(value, 0);
|
|
case ValueDataType.S16: return BitConverter.ToInt16(value, 0);
|
|
case ValueDataType.S32: return BitConverter.ToInt32(value, 0);
|
|
case ValueDataType.F32: return BitConverter.ToSingle(value, 0);
|
|
case ValueDataType.F64: return BitConverter.ToDouble(value, 0);
|
|
case ValueDataType.U128:
|
|
{
|
|
// Need a zero in msb to avoid value is handled as signed
|
|
var valueConverted = new BigInteger((value).Concat(new byte[1]).ToArray());
|
|
return valueConverted;
|
|
}
|
|
|
|
case ValueDataType.Array6B:
|
|
case ValueDataType.Array10B:
|
|
case ValueDataType.Array12B:
|
|
case ValueDataType.Array64B:
|
|
case ValueDataType.Array128B:
|
|
case ValueDataType.Array136B:
|
|
case ValueDataType.Array250B: return value;
|
|
//case DeviceValueDataType.U32IPV4: return U32_IP_Encoder.Decode( value );
|
|
default:
|
|
if (dataType.IsText())
|
|
{ // Do this as the last check to save CPU power
|
|
var typeAsText = dataType.ToString();
|
|
if (dataType.IsEmailText())
|
|
{ // is Email
|
|
return StringEncoderEmail.Decode(value, Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet()));
|
|
}
|
|
if (dataType.IsSMSText())
|
|
{ // is SMS
|
|
return StringEncoderUnicode.Decode(value, Length(dataType));
|
|
}
|
|
if (dataType.IsWindows1252Text())
|
|
{ // is Windows 1252
|
|
return StringEncoder_W1252.Decode(value, Length(dataType));
|
|
}
|
|
if (dataType.IsWindows1252TextRecipient())
|
|
{ // is Windows 1252, but needs to have "<>" in frond and end
|
|
var valueAsText = StringEncoder_W1252.Decode(value, Length(dataType));
|
|
if (!String.IsNullOrEmpty(valueAsText))
|
|
{
|
|
// Remove added brackets or " nessesarry that is only to be
|
|
// Ex. end result is "<tmo@xyleminc.com>,<tmo@mjk.dk>" or "<tmo@xyleminc.com>"
|
|
if (valueAsText.Contains("\""))
|
|
{
|
|
valueAsText = valueAsText.Replace("\"", "");
|
|
}
|
|
if (valueAsText.Contains("<"))
|
|
{
|
|
valueAsText = valueAsText.Replace("<", "");
|
|
}
|
|
if (valueAsText.Contains(">"))
|
|
{
|
|
valueAsText = valueAsText.Replace(">", "");
|
|
}
|
|
// Replace email seperation and replace it with correct the standard ";"
|
|
if (valueAsText.Contains(","))
|
|
{
|
|
valueAsText = valueAsText.Replace(",", ";");
|
|
}
|
|
}
|
|
return valueAsText;
|
|
}
|
|
if (dataType.IsEmailQuotedPrintableText())
|
|
{
|
|
return StringEncoderEmail_Q_Types.Decode(value, Length(dataType), ((emailCharacterSet == null) ? "" : emailCharacterSet()));
|
|
}
|
|
// Is allowed to have a variable length on a string / text
|
|
var maxLng = dataType.Length();
|
|
if (value.Length > maxLng)
|
|
{
|
|
throw new Exception(String.Format("Size of text is {0} and more than max {1}", value.Length, maxLng));
|
|
}
|
|
// Is is normal utf8
|
|
return StringEncoder.Decode(value, value.Length);
|
|
}
|
|
throw new NotImplementedException("DataType '" + dataType + "' not recognized.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Decode", "dataType = " + dataType.ToString()));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decode data with data layout setting
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <param name="dataType"></param>
|
|
/// <param name="emailCharacterSet">Null if not used</param>
|
|
/// <param name="layout"></param>
|
|
/// <returns></returns>
|
|
public static object Decode(this byte[] value, ValueDataType dataType, LayoutType layout, GetEmailCharacterSetToUse emailCharacterSet = null)
|
|
{
|
|
try
|
|
{
|
|
var bytes = value.DoLayout(layout);
|
|
return Decode(value, dataType, emailCharacterSet);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Encode", "dataType = " + dataType.ToString()));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a local defined string value to the correct value object according to dataType ("DeviceValueType")
|
|
/// </summary>
|
|
/// <param name="value">Value as string (floating point must use local setting for "," or ".") and DateTime as local time </param>
|
|
/// <param name="dataType"></param>
|
|
/// <returns></returns>
|
|
public static object ToValue(this string value, ValueDataType dataType)
|
|
{
|
|
try
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new Exception("Value is null");
|
|
}
|
|
switch (dataType)
|
|
{
|
|
case ValueDataType.B8:
|
|
case ValueDataType.U8:
|
|
return (byte)Convert.ToUInt16(value);
|
|
case ValueDataType.S8:
|
|
return (sbyte)Convert.ToInt16(value);
|
|
case ValueDataType.B16:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
return Convert.ToUInt16(value);
|
|
case ValueDataType.U32TIME_Y1970:
|
|
case ValueDataType.U32TIME_Y1970_UTC:
|
|
case ValueDataType.U32TIME_Y2000:
|
|
// Always expect this as local time
|
|
return new DateTime(Convert.ToDateTime(value).Ticks, DateTimeKind.Local);
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC:
|
|
//case DeviceValueDataType.U24DateHH_MM_SS:
|
|
//case DeviceValueDataType.U24:
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32:
|
|
case ValueDataType.U32MJK_SN:
|
|
return Convert.ToUInt32(value);
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64: return Convert.ToUInt64(value);
|
|
case ValueDataType.S16: return Convert.ToInt16(value);
|
|
case ValueDataType.S32: return Convert.ToInt32(value);
|
|
case ValueDataType.F32: return Convert.ToSingle(value);
|
|
case ValueDataType.F64: return Convert.ToDouble(value);
|
|
case ValueDataType.U128:
|
|
// Need a zero in msb to avoid value is handled as signed
|
|
return BigInteger.Parse("0"+value, System.Globalization.NumberStyles.HexNumber);
|
|
//case DeviceValueDataType.U32IPV4: {
|
|
// if (dataType.IsText( )) {
|
|
// return value.ToString( );
|
|
// }
|
|
// throw new NotImplementedException( "DataType '" + dataType + "' not recognized." );
|
|
// }
|
|
default:
|
|
if (dataType.IsText())
|
|
{ // Do this as the last check to save CPU power
|
|
var typeAsText = dataType.ToString();
|
|
if (dataType.IsEmailText())
|
|
{ // is Email
|
|
return value;
|
|
}
|
|
if (dataType.IsSMSText())
|
|
{ // is SMS
|
|
return value;
|
|
}
|
|
if (dataType.IsWindows1252Text())
|
|
{ // is Windows 1252
|
|
return value;
|
|
}
|
|
if (dataType.IsWindows1252TextRecipient())
|
|
{ // is Windows 1252, but needs to have "<>" in frond and end
|
|
return value;
|
|
}
|
|
if (dataType.IsEmailQuotedPrintableText())
|
|
{ // is Special Email Quoted Printable
|
|
return value;
|
|
}
|
|
// Is is normal utf8
|
|
return value; //TODO: No converting is done. may have to be converted to utf8?
|
|
}
|
|
throw new NotImplementedException("DataType '" + dataType + "' not recognized.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "ToValue()", "dataType = " + dataType.ToString()));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return data Byte Length (1x8bit)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static int Length(this ValueDataType DataType)
|
|
{
|
|
try
|
|
{
|
|
switch (DataType)
|
|
{
|
|
case ValueDataType._Empty: return 0;
|
|
#region ---- 8 Bit ----
|
|
case ValueDataType.Bool:
|
|
case ValueDataType.B8:
|
|
case ValueDataType.S8:
|
|
case ValueDataType.U8: return 1;
|
|
#endregion // #region ---- 8 Bit ----
|
|
//case DeviceValueDataType.U24DateHH_MM_SS:
|
|
//case DeviceValueDataType.U24: return 3;
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: return 6;
|
|
default:
|
|
return WLength(DataType) << 1;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "Length"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return data Word Length (2x8bit)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static int WLength(this ValueDataType DataType)
|
|
{
|
|
try
|
|
{
|
|
switch (DataType)
|
|
{
|
|
case ValueDataType._Empty: return 0;
|
|
#region ---- 8 / 16 Bit ----
|
|
case ValueDataType.Bool:
|
|
case ValueDataType.B8:
|
|
case ValueDataType.S8:
|
|
case ValueDataType.U8:
|
|
case ValueDataType.U16:
|
|
case ValueDataType.U16MJK_SN:
|
|
case ValueDataType.B16:
|
|
case ValueDataType.S16:
|
|
case ValueDataType.U16TEXTID:
|
|
case ValueDataType.U16UNIT:
|
|
case ValueDataType.U16UNITGROUP: return 1;
|
|
#endregion
|
|
//case DeviceValueDataType.U48DateYY_MM_DD_HH_MM_SS: return 3;
|
|
//case DeviceValueDataType.U24DateHH_MM_SS:
|
|
//case DeviceValueDataType.U24: return 2;
|
|
#region ---- 32 Bit ----
|
|
case ValueDataType.B32:
|
|
case ValueDataType.U32:
|
|
case ValueDataType.U32MJK_SN:
|
|
case ValueDataType.S32:
|
|
case ValueDataType.F32:
|
|
//case DeviceValueDataType.U32TIMESPAN_MIDNIGHT_UTC:
|
|
case ValueDataType.U32TIME_Y2000:
|
|
case ValueDataType.U32TIME_Y1970:
|
|
case ValueDataType.U32TIME_Y1970_UTC:
|
|
//case DeviceValueDataType.U32IPV4:
|
|
return 2;
|
|
#endregion
|
|
#region ---- 12 x 8 Bit Time Types ----
|
|
case ValueDataType.TIME_ASCII_12B:
|
|
case ValueDataType.TIME_ymdhms:
|
|
return 6;
|
|
#endregion
|
|
#region ---- 64 Bit ----
|
|
case ValueDataType.B64:
|
|
case ValueDataType.U64:
|
|
case ValueDataType.S64:
|
|
case ValueDataType.F64:
|
|
return 4;
|
|
#endregion
|
|
#region ---- 128 Bit ----
|
|
case ValueDataType.U128:
|
|
return 8;
|
|
#endregion
|
|
#region ---- Bit Arrays ----
|
|
case ValueDataType.Array6B: return (6 / 2);
|
|
case ValueDataType.Array10B: return (10 / 2);
|
|
case ValueDataType.Array12B: return (12 / 2);
|
|
case ValueDataType.Array64B: return (64 / 2);
|
|
case ValueDataType.Array128B: return (128 / 2);
|
|
case ValueDataType.Array136B: return (136 / 2);
|
|
case ValueDataType.Array250B: return (250 / 2);
|
|
#endregion
|
|
#region ---- Strings / Text ----
|
|
//6bytes
|
|
case ValueDataType.STR6E:
|
|
case ValueDataType.STR6S:
|
|
//case DeviceValueDataType.STR6Q:
|
|
//case DeviceValueDataType.STR6W1252:
|
|
case ValueDataType.STR6: return 3;
|
|
//8bytes
|
|
case ValueDataType.STR8E:
|
|
case ValueDataType.STR8S:
|
|
//case DeviceValueDataType.STR8Q:
|
|
//case DeviceValueDataType.STR8W1252:
|
|
case ValueDataType.STR8: return 4;
|
|
//10bytes
|
|
case ValueDataType.STR10E:
|
|
case ValueDataType.STR10S:
|
|
//case DeviceValueDataType.STR10Q:
|
|
//case DeviceValueDataType.STR10W1252:
|
|
case ValueDataType.STR10: return 5;
|
|
//16bytes
|
|
case ValueDataType.STR16E:
|
|
case ValueDataType.STR16S:
|
|
//case DeviceValueDataType.STR16Q:
|
|
//case DeviceValueDataType.STR16W1252:
|
|
case ValueDataType.STR16: return 8;
|
|
//18bytes
|
|
case ValueDataType.STR18E:
|
|
case ValueDataType.STR18S:
|
|
//case DeviceValueDataType.STR18Q:
|
|
//case DeviceValueDataType.STR18W1252:
|
|
case ValueDataType.STR18: return 9;
|
|
//20bytes
|
|
case ValueDataType.STR20E:
|
|
case ValueDataType.STR20S:
|
|
//case DeviceValueDataType.STR20Q:
|
|
//case DeviceValueDataType.STR20W1252:
|
|
case ValueDataType.STR20: return 10;
|
|
//22bytes
|
|
case ValueDataType.STR22E:
|
|
case ValueDataType.STR22S:
|
|
//case DeviceValueDataType.STR22Q:
|
|
//case DeviceValueDataType.STR22W1252:
|
|
case ValueDataType.STR22: return 11;
|
|
//30bytes
|
|
case ValueDataType.STR30E:
|
|
case ValueDataType.STR30S:
|
|
//case DeviceValueDataType.STR30Q:
|
|
//case DeviceValueDataType.STR30W1252:
|
|
case ValueDataType.STR30: return 15;
|
|
//32bytes
|
|
case ValueDataType.STR32E:
|
|
case ValueDataType.STR32S:
|
|
//case DeviceValueDataType.STR32Q:
|
|
//case DeviceValueDataType.STR32W1252:
|
|
case ValueDataType.STR32: return 16;
|
|
//64bytes
|
|
case ValueDataType.STR64E:
|
|
case ValueDataType.STR64S:
|
|
//case DeviceValueDataType.STR64Q:
|
|
//case DeviceValueDataType.STR64W1252:
|
|
case ValueDataType.STR64: return 32;
|
|
//100bytes
|
|
case ValueDataType.STR100E:
|
|
case ValueDataType.STR100S:
|
|
//case DeviceValueDataType.STR100Q:
|
|
//case DeviceValueDataType.STR100W1252:
|
|
case ValueDataType.STR100: return 50;
|
|
//128bytes
|
|
case ValueDataType.STR128E:
|
|
case ValueDataType.STR128S:
|
|
//case DeviceValueDataType.STR128Q:
|
|
//case DeviceValueDataType.STR128W1252:
|
|
//case DeviceValueDataType.STR128W1252rec:
|
|
case ValueDataType.STR128: return 64;
|
|
//160bytes
|
|
//case DeviceValueDataType.STR160E:
|
|
//case DeviceValueDataType.STR160S:
|
|
//case DeviceValueDataType.STR160Q:
|
|
//case DeviceValueDataType.STR160W1252:
|
|
//case DeviceValueDataType.STR160: return 80;
|
|
//240bytes
|
|
case ValueDataType.STR240E:
|
|
case ValueDataType.STR240S:
|
|
//case DeviceValueDataType.STR240Q:
|
|
//case DeviceValueDataType.STR240W1252:
|
|
case ValueDataType.STR240: return 240;
|
|
#endregion
|
|
default:
|
|
throw (new Exception("No known length for tag = " + DataType.ToString()));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "WLength"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the text length for value type if it is a text
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static int StringLength(this ValueDataType DataType)
|
|
{
|
|
try
|
|
{
|
|
switch (DataType)
|
|
{
|
|
//case DeviceValueDataType.U32IPV4: return (4 * 3 + 3);
|
|
default:
|
|
if (DataType.IsSMSText())
|
|
{ // is SMS
|
|
return DataType.Length() / 2; // is always 16bit
|
|
}
|
|
return Length(DataType);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.ToFuncErrorText(typeof(DeviceValueEncoder).ToString(), "StringLength"));
|
|
}
|
|
}
|
|
}
|
|
}
|