437 lines
19 KiB
Plaintext
437 lines
19 KiB
Plaintext
|
|
/*
|
||
|
|
Test.Module.AnalogInputChannel.cs
|
||
|
|
|
||
|
|
Copyright © 2008
|
||
|
|
Diversified Technical Systems, Inc.
|
||
|
|
All Rights Reserved
|
||
|
|
*/
|
||
|
|
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Text;
|
||
|
|
using System.Xml;
|
||
|
|
using DTS.Common.Utilities;
|
||
|
|
using DTS.Common.Utilities.DotNetProgrammingConstructs;
|
||
|
|
using System.ComponentModel;
|
||
|
|
using DTS.Common.Utilities.Logging;
|
||
|
|
using DTS.Common.Utilities.Xml;
|
||
|
|
|
||
|
|
namespace DTS.Serialization
|
||
|
|
{
|
||
|
|
// *** see Test.cs ***
|
||
|
|
public partial class Test
|
||
|
|
{
|
||
|
|
// *** see Test.Module.cs ***
|
||
|
|
public partial class Module
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Representation of a calculated channel.
|
||
|
|
/// </summary>
|
||
|
|
[XmlSerializationTag("CalculatedChannel")]
|
||
|
|
public class CalculatedChannel : AnalogInputChannel
|
||
|
|
{
|
||
|
|
public CalculatedChannel(Module parentModule)
|
||
|
|
: base(parentModule)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get/set the Source Channel for this channel.
|
||
|
|
/// </summary>
|
||
|
|
[XmlSerializationTag("SourceChannelNumber")]
|
||
|
|
public int[] SourceChannelNumber
|
||
|
|
{
|
||
|
|
get => _SourceChannelNumber.Value;
|
||
|
|
set => _SourceChannelNumber.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<int[]> _SourceChannelNumber
|
||
|
|
= new Property<int[]>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.SourceChannelNumber",
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get/set the Source Channel for this channel.
|
||
|
|
/// </summary>
|
||
|
|
[XmlSerializationTag("SourceModuleNumber")]
|
||
|
|
public int[] SourceModuleNumber
|
||
|
|
{
|
||
|
|
get => _SourceModuleNumber.Value;
|
||
|
|
set => _SourceModuleNumber.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<int[]> _SourceModuleNumber
|
||
|
|
= new Property<int[]>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.SourceModuleNumber",
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get/set the Source Channel for this channel.
|
||
|
|
/// </summary>
|
||
|
|
[XmlSerializationTag("SourceModuleSerialNumber")]
|
||
|
|
public string[] SourceModuleSerialNumber
|
||
|
|
{
|
||
|
|
get => _SourceModuleSerialNumber.Value;
|
||
|
|
set => _SourceModuleSerialNumber.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<string[]> _SourceModuleSerialNumber
|
||
|
|
= new Property<string[]>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.SourceModuleSerialNumber",
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
/// <summary>
|
||
|
|
/// the calculation for the channel(s) involved
|
||
|
|
/// </summary>
|
||
|
|
[XmlSerializationTag("Calculation")]
|
||
|
|
public string Calculation
|
||
|
|
{
|
||
|
|
get => _Calculation.Value;
|
||
|
|
set => _Calculation.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<string> _Calculation
|
||
|
|
= new Property<string>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.Calculation",
|
||
|
|
"NONE",
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
[XmlSerializationTag("T1")]
|
||
|
|
public ulong T1
|
||
|
|
{
|
||
|
|
get => _T1.Value;
|
||
|
|
set => _T1.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<ulong> _T1
|
||
|
|
= new Property<ulong>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.T1",
|
||
|
|
0,
|
||
|
|
false);
|
||
|
|
|
||
|
|
[XmlSerializationTag("T2")]
|
||
|
|
public ulong T2
|
||
|
|
{
|
||
|
|
get => _T2.Value;
|
||
|
|
set => _T2.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<ulong> _T2
|
||
|
|
= new Property<ulong>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.T2",
|
||
|
|
0,
|
||
|
|
false);
|
||
|
|
|
||
|
|
[XmlSerializationTag("HIC")]
|
||
|
|
public double HIC
|
||
|
|
{
|
||
|
|
get => _HIC.Value;
|
||
|
|
set => _HIC.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<double> _HIC
|
||
|
|
= new Property<double>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.HIC",
|
||
|
|
0D,
|
||
|
|
false);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get/set the sample rate for this test module.
|
||
|
|
/// </summary>
|
||
|
|
[XmlSerializationTag("SampleRateHz")]
|
||
|
|
public double SampleRateHz
|
||
|
|
{
|
||
|
|
get => _SampleRateHz.Value;
|
||
|
|
set => _SampleRateHz.Value = value;
|
||
|
|
}
|
||
|
|
private readonly Property<double> _SampleRateHz
|
||
|
|
= new Property<double>(
|
||
|
|
typeof(CalculatedChannel).Namespace + ".Test.Module.CalculatedChannel.SampleRateHz",
|
||
|
|
0,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
private const string BeginTagModifier = "Begin";
|
||
|
|
private const string EndTagModifier = "End";
|
||
|
|
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Write XML serialization for this object to the specified writer.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="writer">
|
||
|
|
/// The <see cref="XmlWriter"/> to which this object's XML serialization
|
||
|
|
/// will be written.
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
public override void WriteXml(XmlWriter writer)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
APILogger.Log("writing CalculatedChannel::WriteXML");
|
||
|
|
var cult = new System.Globalization.CultureInfo("");
|
||
|
|
var attributeExtractor = new AttributeExtractor<XmlSerializationTagAttribute>();
|
||
|
|
|
||
|
|
writer.WriteStartElement(attributeExtractor.ExtractAttachedAttributeFromObject(this).Value);
|
||
|
|
|
||
|
|
writeXmlAttributes(writer);
|
||
|
|
|
||
|
|
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"SourceChannelNumber").Value, IntArrayToString(SourceChannelNumber));
|
||
|
|
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"SourceModuleNumber").Value, IntArrayToString(SourceModuleNumber));
|
||
|
|
writer.WriteAttributeString(
|
||
|
|
attributeExtractor.ExtractAttachedAttributeFromProperty(this, "SourceModuleSerialNumber")
|
||
|
|
.Value, StringArrayToString(SourceModuleSerialNumber));
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"Calculation").Value, Calculation.ToString(cult));
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"SampleRateHz").Value, SampleRateHz.ToString(cult));
|
||
|
|
|
||
|
|
|
||
|
|
if (_HIC.IsValueInitialized)
|
||
|
|
{
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"HIC").Value, HIC.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
|
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"T1").Value, T1.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
|
|
||
|
|
writer.WriteAttributeString(attributeExtractor.ExtractAttachedAttributeFromProperty(this,
|
||
|
|
"T2").Value, T2.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
|
}
|
||
|
|
|
||
|
|
writer.WriteEndElement();
|
||
|
|
}
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem converting " + GetType().FullName + " object to XML: ", ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Read XML serialization for this object from the specified reader.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="reader">
|
||
|
|
/// The <see cref="XmlReader"/> from which this object's XML serialization
|
||
|
|
/// will be read.
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
public override void ReadXml(XmlReader reader)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var cult = new System.Globalization.CultureInfo("");
|
||
|
|
var attributeExtractor = new AttributeExtractor<XmlSerializationTagAttribute>();
|
||
|
|
|
||
|
|
var xmlAttributeDecoder
|
||
|
|
= new PropertyAttributeDecoder<CalculatedChannel>(this);
|
||
|
|
|
||
|
|
base.ReadXml(reader);
|
||
|
|
|
||
|
|
SourceChannelNumber =
|
||
|
|
StringToIntArray(xmlAttributeDecoder.ExtractStringProperty("SourceChannelNumber", reader));
|
||
|
|
|
||
|
|
SourceModuleNumber =
|
||
|
|
StringToIntArray(xmlAttributeDecoder.ExtractStringProperty("SourceModuleNumber", reader));
|
||
|
|
|
||
|
|
SourceModuleSerialNumber =
|
||
|
|
StringToStringArray(xmlAttributeDecoder.ExtractStringProperty("SourceModuleSerialNumber",
|
||
|
|
reader));
|
||
|
|
|
||
|
|
Calculation = xmlAttributeDecoder.ExtractStringProperty("Calculation", reader);
|
||
|
|
SampleRateHz = xmlAttributeDecoder.ExtractDoubleProperty("SampleRateHz", reader);
|
||
|
|
|
||
|
|
var s = reader.GetAttribute("HIC");
|
||
|
|
if (!string.IsNullOrWhiteSpace(s))
|
||
|
|
{
|
||
|
|
HIC = Convert.ToDouble(s);
|
||
|
|
T1 = Convert.ToUInt64(reader.GetAttribute("T1"));
|
||
|
|
T2 = Convert.ToUInt64(reader.GetAttribute("T2"));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem converting XML to " + GetType().FullName + " object", ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private int[] StringToIntArray(string s)
|
||
|
|
{
|
||
|
|
var ints = new List<int>();
|
||
|
|
var tokens = s.Split(new char[] { ',' });
|
||
|
|
foreach (var token in tokens)
|
||
|
|
{
|
||
|
|
if (int.TryParse(token, System.Globalization.NumberStyles.Any,
|
||
|
|
System.Globalization.CultureInfo.InvariantCulture, out int i))
|
||
|
|
{
|
||
|
|
ints.Add(i);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return ints.ToArray();
|
||
|
|
}
|
||
|
|
|
||
|
|
private string[] StringToStringArray(string s)
|
||
|
|
{
|
||
|
|
var tokens = s.Split(new string[] { "_.-._" }, StringSplitOptions.None);
|
||
|
|
return tokens;
|
||
|
|
}
|
||
|
|
|
||
|
|
private string IntArrayToString(int[] array)
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
foreach (var i in array)
|
||
|
|
{
|
||
|
|
if (sb.Length > 0)
|
||
|
|
{
|
||
|
|
sb.Append(",");
|
||
|
|
}
|
||
|
|
sb.Append(i.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
|
|
}
|
||
|
|
return sb.ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
private string StringArrayToString(string[] array)
|
||
|
|
{
|
||
|
|
return string.Join("_.-._", array);
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// Test the specified object for equality with this object.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="obj">
|
||
|
|
/// The <see cref="object"/> to be tested for equality.
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// <see cref="bool"/> true if the specified object has memeberwise equality with
|
||
|
|
/// this object; false otherwise.
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
public override bool Equals(object obj)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var that = obj as CalculatedChannel;
|
||
|
|
return (null != obj)
|
||
|
|
|
||
|
|
// Must-be-initialized properties.
|
||
|
|
&& ChannelId.Equals(that.ChannelId)
|
||
|
|
&& SourceChannelNumber.Equals(that.SourceChannelNumber)
|
||
|
|
&& ChannelDescriptionString.Equals(that.ChannelDescriptionString)
|
||
|
|
&& EngineeringUnits.Equals(that.EngineeringUnits, StringComparison.OrdinalIgnoreCase)
|
||
|
|
&& Calculation.Equals(that.Calculation)
|
||
|
|
&& IsoCode.Equals(that.IsoCode);
|
||
|
|
}
|
||
|
|
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem equality-testing the object " + (null != obj ? "\"" + obj.ToString() + "\"" : "<<NULL>>"), ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Return the hash code for this object.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// The <see cref="int"/> hash code for this object.
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
public override int GetHashCode()
|
||
|
|
{
|
||
|
|
return base.GetHashCode();
|
||
|
|
}
|
||
|
|
|
||
|
|
public override string ToString()
|
||
|
|
{
|
||
|
|
return ChannelDescriptionString;
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// creates a new calculated channel, with the modulenumbers, channelnumbers, and moduleserialnumbers composed of all the inputs
|
||
|
|
///
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="channels"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
public static CalculatedChannel CreateInstance(Channel[] channels)
|
||
|
|
{
|
||
|
|
var cc = new CalculatedChannel(channels.First().ParentModule);
|
||
|
|
|
||
|
|
var maxSampleRAte = channels.Select(ch => ch.ParentModule.SampleRateHz).Max();
|
||
|
|
//CC only values
|
||
|
|
var channelNumbers = new List<int>();
|
||
|
|
var moduleNumbers = new List<int>();
|
||
|
|
var moduleSerialNumbers = new List<string>();
|
||
|
|
var sps = channels.First().ParentModule.SampleRateHz;
|
||
|
|
foreach (var ch in channels)
|
||
|
|
{
|
||
|
|
channelNumbers.Add(ch.Number);
|
||
|
|
moduleSerialNumbers.Add(ch.ParentModule.SerialNumber);
|
||
|
|
if (0 != maxSampleRAte % ch.ParentModule.SampleRateHz)
|
||
|
|
{
|
||
|
|
throw new InvalidOperationException($"sample rate: {maxSampleRAte} is not a multiple of sample rate: {ch.ParentModule.SampleRateHz}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
cc.SourceChannelNumber = channelNumbers.ToArray();
|
||
|
|
cc.SourceModuleNumber = moduleNumbers.ToArray();
|
||
|
|
cc.SourceModuleSerialNumber = moduleSerialNumbers.ToArray();
|
||
|
|
cc.SampleRateHz = sps;
|
||
|
|
|
||
|
|
//Rip a copy. Can't use cloning
|
||
|
|
foreach (
|
||
|
|
PropertyDescriptor item in
|
||
|
|
TypeDescriptor.GetProperties(
|
||
|
|
channels.First() as AnalogInputChannel))
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
item.SetValue(cc, item.GetValue(channels.First()));
|
||
|
|
}
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log(ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//need a copy not the original for linearization formula...
|
||
|
|
cc.LinearizationFormula =
|
||
|
|
new DTS.Common.Classes.Sensors.LinearizationFormula(
|
||
|
|
(channels.First() as AnalogInputChannel).LinearizationFormula);
|
||
|
|
return cc;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// creates a calculated channel using a single source channel
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="sourceChannel"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
public static CalculatedChannel CreateInstance(Channel sourceChannel)
|
||
|
|
{
|
||
|
|
//Dammit, need to manually clone properties from souce channel
|
||
|
|
var cc = new CalculatedChannel(sourceChannel.ParentModule);
|
||
|
|
|
||
|
|
//CC only values
|
||
|
|
cc.SourceChannelNumber = new int[] { sourceChannel.Number };
|
||
|
|
cc.SourceModuleNumber = new int[] { sourceChannel.ParentModule.Number };
|
||
|
|
cc.SourceModuleSerialNumber = new string[] { sourceChannel.ParentModule.SerialNumber };
|
||
|
|
cc.SampleRateHz = sourceChannel.ParentModule.SampleRateHz;
|
||
|
|
|
||
|
|
//Rip a copy. Can't use cloning
|
||
|
|
foreach (PropertyDescriptor item in TypeDescriptor.GetProperties(sourceChannel as AnalogInputChannel))
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
item.SetValue(cc, item.GetValue(sourceChannel));
|
||
|
|
}
|
||
|
|
catch { }
|
||
|
|
}
|
||
|
|
|
||
|
|
return cc;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|