diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/DatastreamFrame.cs b/TBF/BenchControl/RegisterReaders/SerialStream/DatastreamFrame.cs
new file mode 100644
index 000000000..74bb9910c
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/DatastreamFrame.cs
@@ -0,0 +1,267 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.Globalization;
+using System.Text;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public class DatastreamFrame
+ {
+ ///
+ /// Strobed value
+ ///
+ public static decimal TestStartTimestampDec;
+
+ ///
+ /// Stored values
+ ///
+ public FrameFlags Flags;
+
+ public DateTime DateTime; /// From PC
+ public float RefFlow; /// [m3/h]
+ public int Counter;
+
+ const int MaxLength = 256;
+ public char[] Data = new char[MaxLength];
+ public int Length;
+
+ public byte CheckSum;
+
+ public UInt32 VolumeRaw;
+ public Int64 VolumeRawExt;
+
+ public UInt32 Timestamp;
+ public Int64 TimestampExt;
+
+ ///
+ /// Calculated values
+ ///
+ public double Volume(double scalingFactor) { return (double)VolumeRawExt / scalingFactor; }
+ public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
+
+ public double VolumeDelta(double scalingFactor, DatastreamFrame previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
+ public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
+
+ public string Label()
+ {
+ if (Flags == FrameFlags.OK_TestStart) return "#### start test ####";
+ else if (Flags == FrameFlags.OK_TestEnd) return "#### end of test ####";
+ else return string.Empty;
+ }
+
+
+ static DatastreamFrame()
+ {
+ }
+
+ public DatastreamFrame()
+ {
+ }
+
+ ///
+ /// Parses optical telegram and returns OptoTelegramRaw object
+ ///
+ ///
+ /// Create a configuration structure from a complete byte array
+ ///
+ /// Telegram description:
+ ///
+ /// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
+ ///
+ /// Data Comment Type Calculate to decimal
+ /// ----------------------------------------------------------------
+ /// AAAAAA EMF Int24 Value * 0.000000333
+ /// BBBB Magnetic field Int16 Value
+ /// CCCC Flow Int16 Value * 0.225 * Scalig factor
+ /// DDDDDD Volume Int24 Value / 16000 * Scaling factor
+ /// EEEE Impedance Int16 Value
+ /// FFFFFFFF Timestamp Uint32 Value / 8192
+ /// GG Checksum Byte
+ /// ----------------------------------------------------------------
+ ///
+ /// Example:
+ /// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
+ /// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
+ /// ...
+ ///
+ /// A complete byte array data
+ /// true = telegram OK, false = telegram NOK
+ public bool UpdateFromString(string frame, FrameFormat ff, int counter, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast)
+ {
+ DateTime = DateTime.Now;
+ Counter = counter;
+ RefFlow = (float)Sequences.ProcessData.RefFlow.Val;
+
+ if ((frame == null)
+ || (frame.Length < ff.FrameLength)
+ || (frame[ff.FrameLength - 2] != '\r')
+ || (frame[ff.FrameLength - 1] != '\n'))
+ {
+ Flags = FrameFlags.InvalidFrame;
+ return false;
+ }
+
+ Length = frame.Length;
+ for (int i = 0; i < Math.Min(MaxLength, frame.Length); i++) Data[i] = frame[i];
+
+ bool f1 = UInt32.TryParse(frame.Substring(ff.VolumeFieldStart, ff.VolumeFieldLength), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw);
+ bool f2 = UInt32.TryParse(frame.Substring(ff.TimeFieldStart, ff.TimeFieldLength), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp);
+ bool f3 = byte.TryParse(frame.Substring(ff.FrameLength - 4, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum);
+
+ bool allOk = f1 && f2 && f3;
+ Flags = allOk ? FrameFlags.OK : FrameFlags.InvalidFrame;
+
+ ///
+ /// Cope with 'VolumeRaw' overflow
+ ///
+ Int64 uncorrected = (Int64)(((UInt64)volumeRawExtLast & ff.VolumeWindowMask) | VolumeRaw);
+ if (Math.Abs(uncorrected - volumeRawExtLast) <= ff.VolumeWrapThreshold)
+ {
+ VolumeRawExt = volumeRawExtLast = uncorrected;
+ }
+ else if (Math.Abs(uncorrected + ff.VolumeWrapIncrement - volumeRawExtLast) <= ff.VolumeWrapThreshold)
+ {
+ VolumeRawExt = volumeRawExtLast = uncorrected + ff.VolumeWrapIncrement;
+ }
+ else if (Math.Abs(uncorrected - ff.VolumeWrapIncrement - volumeRawExtLast) <= ff.VolumeWrapThreshold)
+ {
+ VolumeRawExt = volumeRawExtLast = uncorrected - ff.VolumeWrapIncrement;
+ }
+ else
+ {
+ VolumeRawExt = volumeRawExtLast = uncorrected;
+ }
+
+ ///
+ /// Cope with 'Timestamp' overflow
+ ///
+ uncorrected = (Int64)(((UInt64)timestampExtLast & ff.TimeWindowMask) | Timestamp);
+ if (Math.Abs(uncorrected - timestampExtLast) <= ff.TimeWrapThreshold)
+ {
+ TimestampExt = timestampExtLast = uncorrected;
+ }
+ else if (Math.Abs(uncorrected + ff.TimeWrapIncrement - timestampExtLast) <= ff.TimeWrapThreshold)
+ {
+ TimestampExt = timestampExtLast = uncorrected + ff.TimeWrapIncrement;
+ }
+ else if (Math.Abs(uncorrected - ff.TimeWrapIncrement - timestampExtLast) <= ff.TimeWrapThreshold)
+ {
+ TimestampExt = timestampExtLast = uncorrected - ff.TimeWrapIncrement;
+ }
+ else
+ {
+ TimestampExt = timestampExtLast = uncorrected;
+ }
+
+ return allOk;
+ }
+
+ ///
+ /// Alternative to UpdateFromString(...) when data are flushed
+ ///
+ public bool UpdateFromStringDummy(string frame, FrameFormat ff)
+ {
+ DateTime = DateTime.Now;
+ RefFlow = (float)Sequences.ProcessData.RefFlow.Val;
+
+ if ((frame == null)
+ || (frame.Length < ff.FrameLength)
+ || (frame[ff.FrameLength - 2] != '\r')
+ || (frame[ff.FrameLength - 1] != '\n'))
+ {
+ Flags = FrameFlags.InvalidFrame;
+ return false;
+ }
+
+ return true;
+ }
+
+
+ public void SetFlags(FrameFlags flags)
+ {
+ this.Flags = flags;
+ }
+
+
+ public string ToString(double scalingFactor, DatastreamFrame previous)
+ {
+ if (Flags == FrameFlags.SyncError)
+ {
+ return "Sychronization error";
+ }
+ else if (Flags == FrameFlags.InvalidFrame)
+ {
+ return "Invalid telegram";
+ }
+ else /// if (flags == OptoTelegramFlags.OK / OptoTelegramFlags.OK_TestStart / OptoTelegramFlags.OK_TestEnd)
+ {
+ StringBuilder sb = new StringBuilder(MaxLength);
+ for (int i = 0; i < Math.Min(MaxLength, Length - 2); i++) sb.Append(Data[i]);
+
+ return string.Format("{0}:{1}:{2}.{3}\t{4} : {5} Volume={6} Time={7} {8}",
+ DateTime.Hour.ToString("D2"),
+ DateTime.Minute.ToString("D2"),
+ DateTime.Second.ToString("D2"),
+ DateTime.Millisecond.ToString("D4"),
+ Counter,
+ sb,
+ VolumeRaw.ToString("X6"),
+ Timestamp.ToString("X8"),
+ Label());
+ }
+ }
+
+ ///
+ /// Filter RefFlow data in an array of OptoTelegramRaw objects by a FIR filter:
+ ///
+ /// kSize = 5, kSize2 = 2
+ ///
+ /// i k
+ /// ---------------------------------------------------------------------------
+ /// 0 -5 filtered[0] = data[0]
+ /// 1 -4 filtered[1] = data[1]
+ /// 2 -3 filtered[2] = data[0]*k[0] + ... + data[4]*k[4]
+ /// 3 -2 filtered[3] = data[1]*k[0] + ... + data[5]*k[4]
+ /// 4 -1 filtered[4] = data[2]*k[0] + ... + data[6]*k[4]
+ /// 5 0 data[0] = filtered[0], filtered[0] = data[3]*k[0] + ... + data[7]*k[4]
+ /// 6 1 data[1] = filtered[1], filtered[1] = data[4]*k[0] + ... + data[8]*k[4]
+ /// 7 ...
+ ///
+ /// array of OptoTelegramRaw objects
+ /// number of objects to process
+ public static void FIRFilterFlow(DatastreamFrame[] optoData, int optoDataCount)
+ {
+ float[] kernel = new float[] { 0.1f, 0.2f, 0.4f, 0.2f, 0.1f };
+ int kSize = kernel.Length;
+ int kSize2 = kernel.Length / 2;
+
+ float[] filtered = new float[kSize];
+
+ for (int i = 0; i < optoDataCount; i++)
+ {
+ int k = i - kSize;
+ if (k >= 0) optoData[k].RefFlow = filtered[i % kSize];
+
+ if (i < kSize2 || i >= optoDataCount - kSize2)
+ {
+ filtered[i % kSize] = optoData[i].RefFlow;
+ }
+ else
+ {
+ float weoightedSum = 0;
+ for (int j = -kSize2; j <= kSize2; j++)
+ weoightedSum += optoData[i + j].RefFlow * kernel[j + kSize2];
+
+ filtered[i % kSize] = weoightedSum;
+ }
+ }
+
+ for (int k = optoDataCount - kSize; k < optoDataCount; k++)
+ {
+ if (k >= 0) optoData[k].RefFlow = filtered[k % kSize];
+ }
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/Enums.cs b/TBF/BenchControl/RegisterReaders/SerialStream/Enums.cs
new file mode 100644
index 000000000..55e3a5eac
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/Enums.cs
@@ -0,0 +1,42 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public enum StreamType
+ {
+ None = 0,
+ Iperl = 1,
+ Flexible = 2,
+ Count /// Number of stream types
+ }
+
+ public enum FieldFormat
+ {
+ None = 0,
+ HexadecimalWindow = 1,
+ Hexadecimal = 2,
+ Decimal = 3,
+ Count
+ }
+
+ public enum FrameFlags : byte
+ {
+ OK = 0,
+ OK_TestStart,
+ OK_TestEnd,
+ InvalidFrame, /// Wrong frame format of checksum error
+ SyncError,
+ }
+
+ public enum DatastreamState
+ {
+ Read,
+ Flush,
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/Factory.cs b/TBF/BenchControl/RegisterReaders/SerialStream/Factory.cs
new file mode 100644
index 000000000..bd329f312
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/Factory.cs
@@ -0,0 +1,26 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System.Collections.Generic;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public class Factory : IComponentFactory
+ {
+ public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
+
+ public void ResetStaticProperties() { SerialStream.ResetStaticProperties(); }
+
+ public IComponent DummyComponent() { return new SerialStream(); }
+
+ public IComponent GetComponent(IComponentCfg cfg, IList components) { return new SerialStream(cfg); }
+
+ public IComponentCfg DefaultConfig() { return new SerialStreamCfg(this.GetType().Namespace.Substring(17), this); }
+
+ public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
+ {
+ return ComponentCfgBase.CreateFromDbEntity(SerialStreamCfg.Serializer, component, this);
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/FrameFormat.cs b/TBF/BenchControl/RegisterReaders/SerialStream/FrameFormat.cs
new file mode 100644
index 000000000..030ed7b94
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/FrameFormat.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public class FrameFormat
+ {
+ public int FrameLength; /// Frame length in characters/bytes, 0 = unknown
+
+ public readonly Config.Unit VolumeUnits;
+ public readonly double VolumeScaleFactor; /// Volume =
+ public readonly int VolumeFieldStart; /// Start of the volume field: 0-based index of the first character, -1 = no volume field in the frame
+ public readonly int VolumeFieldEnd; /// End of the volume field: 0-based index of the last character
+ public readonly FieldFormat VolumeFieldFormat; /// Hexadecimal or Decimal
+
+ public readonly Config.Unit TimeUnits;
+ public readonly double TimeScaleFactor; /// Time =
+ public readonly int TimeFieldStart; /// Start of the time field: 0-based index of the first character, -1 = no time field in the frame
+ public readonly int TimeFieldEnd; /// End of the time field: 0-based index of the last character
+ public readonly FieldFormat TimeFieldFormat; /// Hexadecimal or Decimal
+
+ ///
+ /// Calculated in constructor
+ ///
+ public readonly int VolumeFieldLength;
+ public readonly UInt64 VolumeWindowMask;
+ public readonly Int64 VolumeWrapIncrement;
+ public readonly Int64 VolumeWrapThreshold;
+
+ public readonly int TimeFieldLength;
+ public readonly UInt64 TimeWindowMask;
+ public readonly Int64 TimeWrapIncrement;
+ public readonly Int64 TimeWrapThreshold;
+
+ public FrameFormat(int frameLength,
+ Config.Unit volumeUnits, double volumeScaleFactor,
+ int volumeFieldStart, int volumeFieldEnd, FieldFormat volumeFieldFormat,
+ Config.Unit timeUnits, double timeScaleFactor,
+ int timeFieldStart, int timeFieldEnd, FieldFormat timeFieldFormat)
+ {
+ FrameLength = frameLength;
+
+ VolumeUnits = volumeUnits;
+ VolumeScaleFactor = volumeScaleFactor;
+ VolumeFieldStart = volumeFieldStart;
+ VolumeFieldEnd = volumeFieldEnd;
+ VolumeFieldFormat = volumeFieldFormat;
+
+ TimeUnits = timeUnits;
+ TimeScaleFactor = timeScaleFactor;
+ TimeFieldStart = timeFieldStart;
+ TimeFieldEnd = timeFieldEnd;
+ TimeFieldFormat = timeFieldFormat;
+
+
+ VolumeFieldLength = VolumeFieldEnd - VolumeFieldStart + 1;
+
+ if (volumeFieldFormat == FieldFormat.HexadecimalWindow &&
+ volumeFieldStart >= 0 && VolumeFieldLength >= 1 && VolumeFieldLength <= 8)
+ {
+ VolumeWindowMask = 0xFFFFFFFFFFFFFFFFUL << (4 * VolumeFieldLength);
+ VolumeWrapIncrement = 1L << (4 * VolumeFieldLength);
+ VolumeWrapThreshold = 1L << (4 * VolumeFieldLength - 1);
+ }
+
+ TimeFieldLength = TimeFieldEnd - TimeFieldStart + 1;
+
+ if (volumeFieldFormat == FieldFormat.HexadecimalWindow &&
+ volumeFieldStart >= 0 && TimeFieldLength >= 1 && TimeFieldLength <= 8)
+ {
+ TimeWindowMask = 0xFFFFFFFFFFFFFFFFUL << (4 * TimeFieldLength);
+ TimeWrapIncrement = 1L << (4 * TimeFieldLength);
+ TimeWrapThreshold = 1L << (4 * TimeFieldLength - 1);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/FrameReceivedEventArgs.cs b/TBF/BenchControl/RegisterReaders/SerialStream/FrameReceivedEventArgs.cs
new file mode 100644
index 000000000..3935d0ae3
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/FrameReceivedEventArgs.cs
@@ -0,0 +1,20 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public class FrameReceivedEventArgs : EventArgs
+ {
+ public string Data;
+
+ public FrameReceivedEventArgs(string data)
+ {
+ this.Data = data;
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/ProcParams.cs b/TBF/BenchControl/RegisterReaders/SerialStream/ProcParams.cs
new file mode 100644
index 000000000..49f5e82c1
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/ProcParams.cs
@@ -0,0 +1,287 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.IO;
+using System.Xml.Serialization;
+using Config.Entities;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public class ProcParams : ProcedureParamsBase, IParamsProvider, IProcedureParams
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(ProcParams) })[0];
+ public override XmlSerializer GetSerializer() { return Serializer; }
+
+
+ public StreamType StreamType; /// Enumerated stream type
+ public int FrameLength; /// Frame length in characters/bytes, 0 = unknown
+ public double FrameFrequency; /// Number of frames per second, 0 = unknown
+
+ public Config.Unit VolumeUnits;
+ public double VolumeScaleFactor; /// Volume =
+ public int VolumeFieldStart; /// Start of the volume field: 0-based index of the first character, -1 = no volume field in the frame
+ public int VolumeFieldEnd; /// End of the volume field: 0-based index of the last character
+ public FieldFormat VolumeFieldFormat; /// Hexadecimal or Decimal
+
+ public Config.Unit TimeUnits;
+ public double TimeScaleFactor; /// Time =
+ public int TimeFieldStart; /// Start of the time field: 0-based index of the first character, -1 = no time field in the frame
+ public int TimeFieldEnd; /// End of the time field: 0-based index of the last character
+ public FieldFormat TimeFieldFormat; /// Hexadecimal or Decimal
+
+
+ public override void InitializeAll()
+ {
+ /// Values are for iPerl DN15 in data stream mode at 8 Hz
+ StreamType = StreamType.Flexible;
+ FrameLength = 42;
+ FrameFrequency = 8;
+
+ VolumeUnits = Config.Unit.l;
+ VolumeScaleFactor = 16000;
+ VolumeFieldStart = 17;
+ VolumeFieldEnd = 22;
+ VolumeFieldFormat = FieldFormat.HexadecimalWindow;
+
+ TimeUnits = Config.Unit.s;
+ TimeScaleFactor = 8192;
+ TimeFieldStart = 29;
+ TimeFieldEnd = 36;
+ TimeFieldFormat = FieldFormat.HexadecimalWindow;
+ }
+
+ string[] paramNames = new string[]
+ {
+ "Stream type",
+ "Frame Length",
+ "Frame Frequency",
+
+ "Volume Units",
+ "Volume Scale Factor",
+ "Volume Field Start",
+ "Volume Field End",
+ "Volume Field Format",
+
+ "Time Units",
+ "Time Scale Factor",
+ "Time Field Start",
+ "Time Field End",
+ "Time Field Format",
+ };
+ public override string ParamName(int i) { return paramNames[i]; }
+ public override int ParamsCount() { return paramNames.Length; }
+
+ public override string ToString(int i)
+ {
+ switch (i)
+ {
+ case 0: return StreamType.ToString();
+ case 1: return FrameLength.ToString();
+ case 2: return FrameFrequency.ToString();
+
+ case 3: return VolumeUnits.ToDescription();
+ case 4: return VolumeScaleFactor.ToString();
+ case 5: return VolumeFieldStart.ToString();
+ case 6: return VolumeFieldEnd.ToString();
+ case 7: return VolumeFieldFormat.ToString();
+
+ case 8: return TimeUnits.ToDescription();
+ case 9: return TimeScaleFactor.ToString();
+ case 10: return TimeFieldStart.ToString();
+ case 11: return TimeFieldEnd.ToString();
+ case 12: return TimeFieldFormat.ToString();
+
+ default: return string.Empty;
+ }
+ }
+
+ public void UpdateParam(int i, string strValue)
+ {
+ switch (i)
+ {
+ case 0:
+ for (StreamType st = 0; st < StreamType.Count; st++)
+ {
+ if (st.ToString().Equals(strValue)) { StreamType = st; return; }
+ }
+ break;
+ case 1: FrameLength = int.Parse(strValue); return;
+ case 2: FrameFrequency = Utils.ParseUDouble(strValue); return;
+
+ case 3:
+ for (Config.Unit u = 0; u < Config.Unit.Count; u++)
+ {
+ if (Config.Units.IsQuantity(u, Config.Quantity.Volume) && u.ToDescription().Equals(strValue))
+ {
+ VolumeUnits = u;
+ return;
+ }
+ }
+ break;
+ case 4: VolumeScaleFactor = Utils.ParseSDouble(strValue); return;
+ case 5: VolumeFieldStart = int.Parse(strValue); return;
+ case 6: VolumeFieldEnd = int.Parse(strValue); return;
+ case 7:
+ for (FieldFormat ff = 0; ff < FieldFormat.Count; ff++)
+ {
+ if (ff.ToString().Equals(strValue)) { VolumeFieldFormat = ff; return; }
+ }
+ break;
+
+ case 8:
+ for (Config.Unit u = 0; u < Config.Unit.Count; u++)
+ {
+ if (Config.Units.IsQuantity(u, Config.Quantity.Time) && u.ToDescription().Equals(strValue))
+ {
+ TimeUnits = u;
+ return;
+ }
+ }
+ break;
+ case 9: TimeScaleFactor = Utils.ParseSDouble(strValue); return;
+ case 10: TimeFieldStart = int.Parse(strValue); return;
+ case 11: TimeFieldEnd = int.Parse(strValue); return;
+ case 12:
+ for (FieldFormat ff = 0; ff < FieldFormat.Count; ff++)
+ {
+ if (ff.ToString().Equals(strValue)) { TimeFieldFormat = ff; return; }
+ }
+ break;
+
+ default: return;
+ }
+ }
+
+ public bool ValidateParam(int i, string strValue, out string message)
+ {
+ message = string.Empty;
+
+ int iDummy;
+ double fDummy;
+
+ switch (i)
+ {
+ case 0:
+ for (StreamType st = 0; st < StreamType.Count; st++)
+ {
+ if (st.ToString().Equals(strValue)) return true;
+ }
+ break;
+ case 1:
+ if (int.TryParse(strValue, out iDummy) && iDummy >= 0 && iDummy <= 8000) return true;
+ break;
+ case 2:
+ if (Utils.TryParseUDouble(strValue, out fDummy) && fDummy <= 1000.0f) return true;
+ break;
+
+ case 3:
+ for (Config.Unit u = 0; u < Config.Unit.Count; u++)
+ {
+ if (Config.Units.IsQuantity(u, Config.Quantity.Volume) && u.ToDescription().Equals(strValue))
+ {
+ return true;
+ }
+ }
+ break;
+ case 4: return Utils.TryParseSDouble(strValue, out fDummy);
+ case 5: return (int.TryParse(strValue, out iDummy) && (iDummy >= -1));
+ case 6: return (int.TryParse(strValue, out iDummy) && (iDummy >= 0));
+ case 7:
+ for (FieldFormat ff = 0; ff < FieldFormat.Count; ff++)
+ {
+ if (ff.ToString().Equals(strValue)) return true;
+ }
+ break;
+
+ case 8:
+ for (Config.Unit u = 0; u < Config.Unit.Count; u++)
+ {
+ if (Config.Units.IsQuantity(u, Config.Quantity.Time) && u.ToDescription().Equals(strValue))
+ {
+ return true;
+ }
+ }
+ break;
+ case 9: return Utils.TryParseSDouble(strValue, out fDummy);
+ case 10: return (int.TryParse(strValue, out iDummy) && (iDummy >= -1));
+ case 11: return (int.TryParse(strValue, out iDummy) && (iDummy >= 0));
+ case 12:
+ for (FieldFormat ff = 0; ff < FieldFormat.Count; ff++)
+ {
+ if (ff.ToString().Equals(strValue)) return true;
+ }
+ break;
+
+ default:
+ message = "Invalid index";
+ return false;
+ }
+
+ message = ParamName(i) + " is invalid";
+ return false;
+ }
+
+ void CopyContentTo(ProcParams prms)
+ {
+ prms.StreamType = this.StreamType;
+ prms.FrameLength = this.FrameLength;
+ prms.FrameFrequency = this.FrameFrequency;
+
+ prms.VolumeUnits = this.VolumeUnits;
+ prms.VolumeScaleFactor = this.VolumeScaleFactor;
+ prms.VolumeFieldStart = this.VolumeFieldStart;
+ prms.VolumeFieldEnd = this.VolumeFieldEnd;
+ prms.VolumeFieldFormat = this.VolumeFieldFormat;
+
+ prms.TimeUnits = this.TimeUnits;
+ prms.TimeScaleFactor = this.TimeScaleFactor;
+ prms.TimeFieldStart = this.TimeFieldStart;
+ prms.TimeFieldEnd = this.TimeFieldEnd;
+ prms.TimeFieldFormat = this.TimeFieldFormat;
+ }
+
+ public IParamsProvider Clone()
+ {
+ ProcParams pars = new ProcParams();
+ CopyContentTo(pars);
+ return pars;
+ }
+
+ public override void UpdateFromDbEntity(ComponentProcedure dbEntity)
+ {
+ if (dbEntity == null) return;
+ try
+ {
+ ProcParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcParams;
+
+ procedureParamsEntity = dbEntity;
+ componentName = dbEntity.CmpntName;
+ procedure = dbEntity.Procedure;
+
+ if (tmp != null) tmp.CopyContentTo(this);
+ }
+ catch
+ {
+ }
+ }
+
+
+ public ProcParams()
+ {
+ }
+
+ public ProcParams(bool initialize)
+ {
+ if (initialize) InitializeAll();
+ }
+
+ public ProcParams(ComponentProcedure procParamsEntity, string componentName, Procedure procedure)
+ {
+ this.procedureParamsEntity = procParamsEntity;
+ this.componentName = componentName;
+ this.procedure = procedure;
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/SerialStream.cs b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStream.cs
new file mode 100644
index 000000000..5a6b4aefb
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStream.cs
@@ -0,0 +1,594 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.IO;
+using System.IO.Ports;
+using log4net;
+using Config.Entities;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ ///
+ /// This component = instance of this class is a placeholder for a combined main watermeter
+ ///
+ public class SerialStream : ComponentBase, IDevice, GenericDevices.IRegisterReader, IOperation
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(SerialStream));
+ public override string ToString() { return string.Format("RegisterReaders.SerialStream({0})", Cfg.ToString(1)); }
+
+
+ readonly SerialStreamCfg myCfg;
+
+ public Config.Entities.RegisterReaderType RegisterReaderType { get { return Config.Entities.RegisterReaderType.DataStream; } }
+ public double PulsesPerLtr { get { return 1000.0; } }
+ public double LtrsPerPulse { get { return 1 / PulsesPerLtr; } }
+
+
+ ///
+ /// Wrappers for procedure parameters
+ ///
+ public StreamType StreamType { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.StreamType : StreamType.None; } }
+ public int FrameLength { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.FrameLength : 0; } }
+ public double FrameFrequency { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.FrameFrequency : 0; } }
+
+ public Config.Unit VolumeUnits { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.VolumeUnits : Config.Unit.l; } }
+ public double VolumeScaleFactor { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.VolumeScaleFactor : 1; } }
+ public int VolumeFieldStart { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.VolumeFieldStart : 0; } }
+ public int VolumeFieldEnd { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.VolumeFieldEnd : 0; } }
+ public FieldFormat VolumeFieldFormat { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.VolumeFieldFormat : FieldFormat.None; } }
+
+ public Config.Unit TimeUnits { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.TimeUnits : Config.Unit.s; } }
+ public double TimeScaleFactor { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.TimeScaleFactor : 1; } }
+ public int TimeFieldStart { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.TimeFieldStart : 0; } }
+ public int TimeFieldEnd { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.TimeFieldEnd : 0; } }
+ public FieldFormat TimeFieldFormat { get { return (myCfg.ProcParams != null) ? myCfg.ProcParams.TimeFieldFormat : FieldFormat.None; } }
+
+
+ public bool CommFailed
+ {
+ get { return commFailed; }
+ set { commFailed = value; }
+ }
+ bool commFailed;
+
+
+ ///
+ /// Passed to OptoTelegramRaw.UpdateFromString(...)
+ ///
+ Int64 volumeRawExtLast;
+ Int64 timestampExtLast;
+
+
+ ///
+ /// Required for IRegisterReader interface
+ ///
+ public int WMPulses { get { return wmPulses; } }
+ public int WMRefPulses { get { return wmRefPulses; } }
+ public double WMVolume { get { return wmVolume; } }
+ public double BeginWMState { get { return beginWMState; } }
+ public double EndWMState { get { return endWMState; } }
+ public double WMTestTime { get { return wmTestTime; } }
+
+
+ double beginWMState;
+ double endWMState;
+ double wmVolume;
+ int wmPulses;
+ int wmRefPulses;
+ double wmTestTime;
+
+
+
+ /// Name set by the test, to be used as a part of the opto-data log file name
+ public string TestName;
+
+ /// Name set by the test, to be used as a part of the opto-data log file name
+ public string BenchName;
+
+ ///
+ /// Volume of water from the opto telegram
+ ///
+ private Int64 lastVolumeRaw; /// Last read raw volume
+ private double volumeLtr; ///
+ private double volumeLtr0;
+
+ public double VolumeLtrStart; /// Test start volume for metrology
+ public double VolumeLtrEnd; /// Test end volume for metrology
+ public double VolumeLtrRef; /// Reference volume or metrology
+ double volumeLtrEnd1; /// auxiliary buffer1 to keep the end volume before test stops
+ double volumeLtrEnd2; /// auxiliary buffer2 to keep the end volume before test stops
+ double volumeLtrEnd3; /// auxiliary buffer3 to keep the end volume before test stops
+
+ ///
+ /// Timestamp from the opto telegram
+ ///
+ bool lastTimestampValid;
+ private Int64 lastTimestamp;
+ private double timestampSec;
+ private double timestampSec0;
+
+ public bool NoSamples { get { return (timestampSecEnd - timestampSecStart) < float.Epsilon; } }
+ public double TimestampSecStart { get { return timestampSecStart; } }
+ public double TimestampSecEnd { get { return timestampSecEnd; } }
+ double timestampSecStart;
+ double timestampSecEnd;
+ double timestampSecEnd1;
+ double timestampSecEnd2;
+ double timestampSecEnd3;
+
+ private int frameIx;
+ public int TestStartFrameIx;
+ public int TestEndFrameIx;
+
+
+ DatastreamFrame[] datastreamFrames;
+ const int MaxDatastreamFramesCount = 40000;
+ int datastreamFramesCount;
+ string datastreamLogFileName;
+
+
+ DatastreamFrame toBeFlushed;
+ int flushedFramesCount;
+ public int FlushedFramesCount
+ {
+ get { return flushedFramesCount; }
+ set { flushedFramesCount = lastFlushedFramesCount = lastFlushedFramesCount_1 = value; }
+ }
+
+ int lastFlushedFramesCount_1;
+ int lastFlushedFramesCount;
+ public int FlushedFramesDelta
+ {
+ get
+ {
+ int retval = Math.Max(flushedFramesCount - lastFlushedFramesCount, lastFlushedFramesCount - lastFlushedFramesCount_1);
+ lastFlushedFramesCount_1 = lastFlushedFramesCount;
+ lastFlushedFramesCount = flushedFramesCount;
+ return retval;
+ }
+ }
+
+
+
+ ///
+ /// Opto serial port and worker thread related private variables
+ ///
+ private SerialPort serialPort;
+
+
+ public SerialStream()
+ {
+ }
+
+ public SerialStream(Generic.IComponentCfg cfg)
+ : base(cfg)
+ {
+ ClearData();
+
+ myCfg = cfg as SerialStreamCfg;
+
+ log.Debug(this.ToString());
+ }
+
+ ///
+ /// Clear data related to a specific water meter
+ ///
+ public void ClearData()
+ {
+ commFailed = false;
+ datastreamFramesCount = 0;
+ }
+
+ public void Initialize()
+ {
+ ClearData();
+
+ if (DebugLevel == DebugMode.Normal)
+ {
+ datastreamParsingEnabled = false;
+ currentFrameFormat = new FrameFormat(FrameLength,
+ VolumeUnits, VolumeScaleFactor,
+ VolumeFieldStart, VolumeFieldEnd, VolumeFieldFormat,
+ TimeUnits, TimeScaleFactor,
+ TimeFieldStart, TimeFieldEnd, TimeFieldFormat);
+
+ /// Allocate memory for opto-data from iPerl
+ datastreamFrames = new DatastreamFrame[MaxDatastreamFramesCount];
+ for (int i = 0; i < MaxDatastreamFramesCount; i++) datastreamFrames[i] = new DatastreamFrame();
+
+ toBeFlushed = new DatastreamFrame();
+ flushedFramesCount = 0;
+
+ synchronized = false;
+ synchronized2 = false;
+ partOfTelegram = string.Empty;
+
+ /// Prepare serial port
+ serialPort = new SerialPort(string.Format("COM{0}", myCfg.ComPortNr),
+ myCfg.BaudRate,
+ myCfg.Parity,
+ myCfg.DataBits,
+ myCfg.StopBits);
+ serialPort.Handshake = myCfg.Handshake;
+
+ serialPort.Open();
+ }
+ }
+
+ public void RunDeviceBefore()
+ {
+ if (DebugLevel == DebugMode.Normal)
+ {
+ try
+ {
+ if (datastreamParsingEnabled)
+ ReadDatastream(DatastreamState.Read, currentFrameFormat);
+ else
+ ReadDatastream(DatastreamState.Flush, currentFrameFormat);
+ }
+ catch (Exception e)
+ {
+ DebugLevel = DebugMode.FailureDuringOperation;
+
+ log.FatalFormat("Opto-data serial port failure : {0}", e.Message);
+ if (e.InnerException != null)
+ {
+ log.FatalFormat("InnerMessage : {0}", e.InnerException.Message);
+ }
+ }
+ }
+ else if (DebugLevel == DebugMode.FailureDuringOperation)
+ {
+ }
+ }
+
+ public void RunDeviceAfter() { }
+
+ public void StopDevice()
+ {
+ try
+ {
+ if (DebugLevel == DebugMode.Normal && serialPort != null)
+ {
+ serialPort.Close();
+ serialPort = null;
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ public void StopDevice2() { }
+
+ ///
+ /// Events: Event.ReadRegisterDone, Event.Error
+ ///
+ /// ReadWaterMeter instance reference casted to IOperaton
+ public IOperation ReadRegisterOp()
+ {
+ return this;
+ }
+
+ ///
+ /// Clear data/counters related to a specific tests
+ ///
+ public void Clear()
+ {
+ sampleNr = 0;
+
+ volumeLtr = 0;
+ volumeLtr0 = 0;
+ timestampSec = 0;
+ timestampSec0 = 0;
+
+ ReadPulses();
+ }
+
+ public void TestCompleted()
+ {
+ /// TODO: Implement
+ }
+
+
+ int sampleNr; /// This is to determine when the test start sample should be taken
+
+ /// Start this operation
+ public void Start()
+ {
+ Clear();
+
+ /// Reset opto data
+ datastreamFramesCount = 0;
+ TestStartFrameIx = 0;
+ TestEndFrameIx = 0;
+
+ /// File name
+ int digitIx = Name.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
+ string position = (digitIx >= 0) ? Name.Substring(digitIx) : string.Empty;
+
+ datastreamLogFileName = string.Format("{0}_{1}_{2}.txt",
+ StateMachine.CycleStartTimeStamp.ToString("HHmmss"),
+ position,
+ TestName);
+
+ StartParsingDatastream(new FrameFormat(FrameLength,
+ VolumeUnits, VolumeScaleFactor,
+ VolumeFieldStart, VolumeFieldEnd, VolumeFieldFormat,
+ TimeUnits, TimeScaleFactor,
+ TimeFieldStart, TimeFieldEnd, TimeFieldFormat));
+ }
+
+ /// Run this operation
+ /// eventDone
+ public Event Run()
+ {
+ sampleNr++;
+ ReadPulses();
+ if (sampleNr == 4)
+ {
+ /// Take the test start sample
+ VolumeLtrStart = volumeLtr;
+ timestampSecStart = timestampSec;
+ TestStartFrameIx = frameIx;
+ }
+
+ /// Shift data in pipelines
+ VolumeLtrEnd = volumeLtrEnd3;
+ volumeLtrEnd3 = volumeLtrEnd2;
+ volumeLtrEnd2 = volumeLtrEnd1;
+ volumeLtrEnd1 = volumeLtr;
+
+ timestampSecEnd = timestampSecEnd3;
+ timestampSecEnd3 = timestampSecEnd2;
+ timestampSecEnd2 = timestampSecEnd1;
+ timestampSecEnd1 = timestampSec;
+
+ TestEndFrameIx = frameIx - 3;
+
+ return Event.ReadRegisterDone;
+ }
+
+ /// Stop this operation
+ public void Stop()
+ {
+ if (TestStartFrameIx > 0 && TestStartFrameIx < datastreamFrames.Length && datastreamFrames[TestStartFrameIx].Flags == FrameFlags.OK)
+ {
+ datastreamFrames[TestStartFrameIx].Flags = FrameFlags.OK_TestStart;
+ DatastreamFrame.TestStartTimestampDec = datastreamFrames[TestStartFrameIx].TimestampDec();
+ }
+
+ if (TestEndFrameIx > 0 && TestEndFrameIx < datastreamFrames.Length && datastreamFrames[TestEndFrameIx].Flags == FrameFlags.OK)
+ {
+ datastreamFrames[TestEndFrameIx].Flags = FrameFlags.OK_TestEnd;
+ }
+
+ StopParsingDatastream();
+ DatastreamFrame.FIRFilterFlow(datastreamFrames, datastreamFramesCount);
+ SaveDatastreamLog();
+ }
+
+
+ void SaveDatastreamLog()
+ {
+ string directory = string.Format("C:\\TBF\\ProcessData\\{0}\\{1}\\{2}\\",
+ StateMachine.CycleStartTimeStamp.ToString("yy"),
+ StateMachine.CycleStartTimeStamp.ToString("MM"),
+ StateMachine.CycleStartTimeStamp.ToString("dd"));
+ string datastreamLogPathName = directory + datastreamLogFileName;
+ try
+ {
+ Directory.CreateDirectory(directory);
+
+ double scalFact = ScalingFactor();
+
+ using (TextWriter datastreamLogFile = new StreamWriter(datastreamLogPathName))
+ {
+ datastreamLogFile.WriteLine(datastreamFrames[0].ToString(scalFact, null));
+ for (int i = 1; i < datastreamFramesCount; i++) datastreamLogFile.WriteLine(datastreamFrames[i].ToString(scalFact, datastreamFrames[i - 1]));
+ datastreamLogFile.Close();
+ }
+ }
+ catch (Exception exc)
+ {
+ File.Delete(datastreamLogPathName);
+ log.ErrorFormat(string.Format("Error writing into file {0}", datastreamLogPathName));
+ log.ErrorFormat(string.Format("Exception message: {0}", exc.Message));
+ }
+ }
+
+ void ReadPulses()
+ {
+ beginWMState = volumeLtr0;
+ endWMState = volumeLtr;
+ wmVolume = Math.Abs(endWMState - beginWMState);
+ wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
+ wmRefPulses = StateMachine.ControlBoard.EtPulses(0);
+ wmTestTime = timestampSec - timestampSec0;
+ }
+
+
+ bool datastreamParsingEnabled;
+ FrameFormat currentFrameFormat; /// Set by StartParsingDatastream()
+
+ /// Flush internal buffers and start parsing the opto serial port data
+ void StartParsingDatastream(FrameFormat ff)
+ {
+ currentFrameFormat = ff;
+ datastreamParsingEnabled = true;
+ }
+
+ /// Stop parsig the opto serial port data
+ void StopParsingDatastream()
+ {
+ datastreamParsingEnabled = false;
+ }
+
+ ///
+ /// Variables storing the context of serial port data parsing (ReadOptoSerialPort(...))
+ ///
+ bool synchronized;
+ bool synchronized2;
+ string partOfTelegram;
+
+ ///
+ /// 9600 Bd, 8 data bits, 1 stop bit, no parity
+ ///
+ /// Telegram description:
+ ///
+ /// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
+ ///
+ /// Example:
+ /// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
+ /// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
+ /// ...
+ ///
+ /// OptoState.Read or OptoState.Flush
+ void ReadDatastream(DatastreamState streamState, FrameFormat ff)
+ {
+ int nrBytes = serialPort.BytesToRead;
+ if (nrBytes > 0)
+ {
+ char[] buffer = new char[nrBytes];
+ serialPort.Read(buffer, 0, nrBytes);
+ string received = new string(buffer);
+
+ string allRcvd = partOfTelegram + received;
+
+ while (true)
+ {
+ int pos = allRcvd.IndexOf("\r\n");
+
+ if (pos < 0)
+ {
+ /// No CR+LF found, wait for more characters in the next invocation
+ partOfTelegram = allRcvd;
+ return;
+ }
+ else
+ {
+ // CR+LF found
+ if (streamState == DatastreamState.Read)
+ {
+ if (pos < FrameLength - 2)
+ {
+ /// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
+ allRcvd = allRcvd.Substring(pos + 2);
+ if (synchronized)
+ {
+ datastreamFrames[datastreamFramesCount].Counter = datastreamFramesCount;
+ datastreamFrames[datastreamFramesCount++].SetFlags(FrameFlags.SyncError);
+ }
+ synchronized = true;
+ }
+ // CR+LF found and (pos >= OptoTelegramRaw.Length - 2)
+ else if (datastreamFrames[datastreamFramesCount].UpdateFromString(allRcvd.Substring(pos - ff.FrameLength + 2), ff, datastreamFramesCount, ref volumeRawExtLast, ref timestampExtLast))
+ {
+ OptoTelegramRreceived(datastreamFramesCount++, synchronized2);
+ synchronized2 = synchronized;
+ allRcvd = allRcvd.Substring(pos + 2);
+ }
+ else
+ {
+ datastreamFrames[datastreamFramesCount].Counter = datastreamFramesCount;
+ datastreamFramesCount++;
+ allRcvd = allRcvd.Substring(pos + 2);
+ }
+ }
+ else /// optoState == SerialStreamState.Flush
+ {
+ if (pos < FrameLength - 2)
+ {
+ /// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
+ allRcvd = allRcvd.Substring(pos + 2);
+ synchronized = true;
+ }
+ // CR+LF found and (pos >= OptoTelegram.Length - 2)
+ else if (toBeFlushed.UpdateFromStringDummy(allRcvd.Substring(pos - ff.FrameLength + 2), ff))
+ {
+ flushedFramesCount++;
+ synchronized2 = synchronized;
+ allRcvd = allRcvd.Substring(pos + 2);
+ }
+ else
+ {
+ allRcvd = allRcvd.Substring(pos + 2);
+ }
+ }
+ }
+ }
+
+ //OnFrameReceived(this, new FrameReceivedEventArgs(s));
+ }
+ else
+ {
+ //OnFrameReceived(this, new FrameReceivedEventArgs("."));
+ }
+ }
+
+
+ void OptoTelegramRreceived(int currentIx, bool async)
+ {
+ DatastreamFrame optoTelegram = datastreamFrames[currentIx];
+ frameIx = currentIx;
+
+ lastVolumeRaw = volumeRawExtLast;
+ lastTimestamp = timestampExtLast;
+
+ if (volumeLtr == 0 && volumeLtr0 == 0)
+ {
+ volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
+ volumeLtr0 = volumeLtr;
+ }
+ else
+ {
+ volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
+ }
+
+ if (timestampSec == 0 && timestampSec0 == 0)
+ {
+ timestampSec = (double)lastTimestamp / 8192.0;
+ timestampSec0 = timestampSec;
+ }
+ else
+ {
+ timestampSec = (double)lastTimestamp / 8192.0;
+ }
+
+ //optoDataLogger.InfoFormat("{0} {1} {2} ltr {3} {4}", optoTelegram, lastTimestamp, lastVolumeRaw.ToString("X8"), timestampSec.ToString("F1"), volumeLtr.ToString("F3"));
+ }
+
+
+ ///
+ /// Called from the state machine when a test is selected and UI needs to be updated.
+ ///
+ public void OnFrameReceived(object sender, FrameReceivedEventArgs args)
+ {
+ if (FrameReceivedHandler == null) return;
+ try { FrameReceivedHandler(sender, args); }
+ catch (Exception) { }
+ }
+
+ public event EventHandler FrameReceivedHandler;
+
+
+ public static double UnitVolume(Config.Unit units, double scaleFactor)
+ {
+ if (Config.Units.IsQuantity(units, Config.Quantity.Volume))
+ {
+ return Config.Units.ConvertFrom(units, 1.0) * scaleFactor;
+ }
+ else
+ {
+ return 1.0;
+ }
+ }
+
+ public double ScalingFactor()
+ {
+ return 1.0;
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfg.cs b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfg.cs
new file mode 100644
index 000000000..961f76b1f
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfg.cs
@@ -0,0 +1,63 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.IO.Ports;
+using System.Xml.Serialization;
+using Config.Entities;
+using TBF.BenchControl.Generic;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public class SerialStreamCfg : ComponentCfgBase, Generic.IComponentCfg
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(SerialStreamCfg) })[0];
+ protected override XmlSerializer GetSerializer() { return Serializer; }
+
+ public IComponentCfgCtrl GetControl() { return new SerialStreamCfgCtrl(); }
+
+ ///
+ /// Serialized parameters
+ ///
+ public int ComPortNr;
+ public int BaudRate;
+ public Parity Parity;
+ public int DataBits;
+ public StopBits StopBits;
+ public Handshake Handshake;
+
+ ///
+ /// Procedure parameters
+ ///
+ [XmlIgnore]
+ public ProcParams ProcParams;
+ public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
+ public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
+
+ /// Private parameterless constructor invoked by all other (public) constructors
+ SerialStreamCfg()
+ {
+ ProcParams = CreateProcParamsProvider() as ProcParams;
+ }
+
+ public SerialStreamCfg(string name, IComponentFactory factory)
+ : this()
+ {
+ Name = name;
+ Factory = factory;
+ ParentName = string.Empty;
+ ComPortNr = 21;
+ BaudRate = 9600;
+ Parity = System.IO.Ports.Parity.None;
+ DataBits = 8;
+ StopBits = System.IO.Ports.StopBits.One;
+ Handshake = System.IO.Ports.Handshake.None;
+ }
+
+ public string ToString(int i)
+ {
+ return string.Format("{0} Com{1}, {2}Bd, {3}-bits, parity={4}, stopBits={5}, {6}",
+ Name, ComPortNr, BaudRate, DataBits, Parity, StopBits, Handshake);
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.cs b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.cs
new file mode 100644
index 000000000..624a94f34
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.cs
@@ -0,0 +1,173 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+using System;
+using System.Windows.Forms;
+using System.IO.Ports;
+using log4net;
+using TBF.BenchControl.Generic;
+using TBF.Resources;
+
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ public partial class SerialStreamCfgCtrl : UserControl, IComponentCfgCtrl
+ {
+ static readonly ILog log = LogManager.GetLogger(typeof(SerialStreamCfgCtrl));
+
+ public bool ShowMore { get { return false; } }
+
+ SerialStreamCfg config;
+ public IComponentCfg Config
+ {
+ get { return config as IComponentCfg; }
+ set
+ {
+ config = value as SerialStreamCfg;
+ Redraw();
+ }
+ }
+
+ public SerialStreamCfgCtrl()
+ {
+ InitializeComponent();
+
+ parityComboBox.Items.Add(Parity.None.ToString());
+ parityComboBox.Items.Add(Parity.Even.ToString());
+ parityComboBox.Items.Add(Parity.Odd.ToString());
+
+ stopBitsComboBox.Items.Add(StopBits.None.ToString());
+ stopBitsComboBox.Items.Add(StopBits.One.ToString());
+ stopBitsComboBox.Items.Add(StopBits.OnePointFive.ToString());
+ stopBitsComboBox.Items.Add(StopBits.Two.ToString());
+
+ handshakeComboBox.Items.Add(Handshake.None.ToString());
+ handshakeComboBox.Items.Add(Handshake.RequestToSend.ToString());
+ handshakeComboBox.Items.Add(Handshake.XOnXOff.ToString());
+ }
+
+ int GetParityIx(Parity par)
+ {
+ if (par == Parity.None) return 0;
+ if (par == Parity.Even) return 0;
+ if (par == Parity.Odd) return 0;
+ return -1;
+ }
+ Parity GetParity(string str)
+ {
+ if (str.Equals(Parity.None.ToString())) return Parity.None;
+ if (str.Equals(Parity.Even.ToString())) return Parity.Even;
+ if (str.Equals(Parity.Odd.ToString())) return Parity.Odd;
+ return (Parity)(-1);
+ }
+
+ int GetStopBitsIx(StopBits sb)
+ {
+ if (sb == StopBits.None) return 0;
+ if (sb == StopBits.One) return 1;
+ if (sb == StopBits.OnePointFive) return 2;
+ if (sb == StopBits.Two) return 3;
+ return -1;
+ }
+ StopBits GetStopBits(string str)
+ {
+ if (str.Equals(StopBits.None.ToString())) return StopBits.None;
+ if (str.Equals(StopBits.One.ToString())) return StopBits.One;
+ if (str.Equals(StopBits.OnePointFive.ToString())) return StopBits.OnePointFive;
+ if (str.Equals(StopBits.Two.ToString())) return StopBits.Two;
+ return (StopBits)(-1);
+ }
+
+ int GetHandshakeIx(Handshake par)
+ {
+ if (par == Handshake.None) return 0;
+ if (par == Handshake.RequestToSend) return 0;
+ if (par == Handshake.XOnXOff) return 0;
+ return -1;
+ }
+ Handshake GetHandshake(string str)
+ {
+ if (str.Equals(Handshake.None.ToString())) return Handshake.None;
+ if (str.Equals(Handshake.RequestToSend.ToString())) return Handshake.RequestToSend;
+ if (str.Equals(Handshake.XOnXOff.ToString())) return Handshake.XOnXOff;
+ return (Handshake)(-1);
+ }
+
+ private void ModbusCfgCtrl_Load(object sender, EventArgs e)
+ {
+ nameLabel.Text = Strings.Name;
+ Redraw();
+ }
+
+ public void Closing()
+ {
+ }
+
+ void Redraw()
+ {
+ if (config == null) return; /// Control was not loaded, settings were not changed
+
+ classNameLabel.Text = config.Factory.ClassName;
+ nameTextBox.Text = config.Name;
+ comPortNrTextBox.Text = config.ComPortNr.ToString();
+ baudRateTextBox.Text = config.BaudRate.ToString();
+ parityComboBox.Text = config.Parity.ToString();
+ dataBitsTextBox.Text = config.DataBits.ToString();
+ stopBitsComboBox.Text = config.StopBits.ToString();
+ handshakeComboBox.Text = config.Handshake.ToString();
+ }
+
+ public void Unlock()
+ {
+ nameTextBox.Enabled = true;
+ comPortNrTextBox.Enabled = true;
+ baudRateTextBox.Enabled = true;
+ parityComboBox.Enabled = true;
+ dataBitsTextBox.Enabled = true;
+ stopBitsComboBox.Enabled = true;
+ handshakeComboBox.Enabled = true;
+ }
+
+ public CfgUpdateFlags UpdateCfg()
+ {
+ CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
+
+ if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
+
+ config.Name = nameTextBox.Text;
+ config.ComPortNr = int.Parse(comPortNrTextBox.Text);
+ config.BaudRate = int.Parse(baudRateTextBox.Text);
+ config.Parity = GetParity(parityComboBox.SelectedItem.ToString());
+ config.DataBits = int.Parse(dataBitsTextBox.Text);
+ config.StopBits = GetStopBits(stopBitsComboBox.SelectedItem.ToString());
+ config.Handshake = GetHandshake(handshakeComboBox.SelectedItem.ToString());
+
+ return flags;
+ }
+
+ public CfgUpdateFlags VerifyCfg(ref string message)
+ {
+ CfgUpdateFlags flags = CfgUpdateFlags.None;
+
+ int dummy;
+ if (!int.TryParse(comPortNrTextBox.Text, out dummy) || dummy < 1 || dummy > 999)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'Serial Port Number' is not valid";
+ }
+
+ if (!int.TryParse(baudRateTextBox.Text, out dummy) || dummy < 150 || dummy > 115200)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'Baud Rate' is not valid";
+ }
+
+ if (!int.TryParse(dataBitsTextBox.Text, out dummy) || dummy < 7 || dummy > 8)
+ {
+ flags |= CfgUpdateFlags.Error;
+ message += Environment.NewLine + "'Data Bits' is not valid";
+ }
+
+ return flags;
+ }
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.designer.cs b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.designer.cs
new file mode 100644
index 000000000..4985c2fb2
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.designer.cs
@@ -0,0 +1,229 @@
+///
+/// Copyright (c) 2018 Sensus Slovensko a.s.
+///
+namespace TBF.BenchControl.RegisterReaders.SerialStream
+{
+ partial class SerialStreamCfgCtrl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.comPortNrTextBox = new System.Windows.Forms.TextBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.baudRateLabel = new System.Windows.Forms.Label();
+ this.baudRateTextBox = new System.Windows.Forms.TextBox();
+ this.partityLabel = new System.Windows.Forms.Label();
+ this.dataBitsLabel = new System.Windows.Forms.Label();
+ this.dataBitsTextBox = new System.Windows.Forms.TextBox();
+ this.stopBitsLabel = new System.Windows.Forms.Label();
+ this.handshakeLabel = new System.Windows.Forms.Label();
+ this.nameTextBox = new System.Windows.Forms.TextBox();
+ this.nameLabel = new System.Windows.Forms.Label();
+ this.classNameLabel = new System.Windows.Forms.Label();
+ this.parityComboBox = new System.Windows.Forms.ComboBox();
+ this.stopBitsComboBox = new System.Windows.Forms.ComboBox();
+ this.handshakeComboBox = new System.Windows.Forms.ComboBox();
+ this.SuspendLayout();
+ //
+ // comPortNrTextBox
+ //
+ this.comPortNrTextBox.Enabled = false;
+ this.comPortNrTextBox.Location = new System.Drawing.Point(140, 52);
+ this.comPortNrTextBox.Name = "comPortNrTextBox";
+ this.comPortNrTextBox.Size = new System.Drawing.Size(34, 20);
+ this.comPortNrTextBox.TabIndex = 4;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(29, 55);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(98, 13);
+ this.label1.TabIndex = 3;
+ this.label1.Text = "Serial Port Number:";
+ //
+ // baudRateLabel
+ //
+ this.baudRateLabel.AutoSize = true;
+ this.baudRateLabel.Location = new System.Drawing.Point(29, 79);
+ this.baudRateLabel.Name = "baudRateLabel";
+ this.baudRateLabel.Size = new System.Drawing.Size(58, 13);
+ this.baudRateLabel.TabIndex = 5;
+ this.baudRateLabel.Text = "Baud Rate";
+ //
+ // baudRateTextBox
+ //
+ this.baudRateTextBox.Enabled = false;
+ this.baudRateTextBox.Location = new System.Drawing.Point(140, 76);
+ this.baudRateTextBox.Name = "baudRateTextBox";
+ this.baudRateTextBox.Size = new System.Drawing.Size(130, 20);
+ this.baudRateTextBox.TabIndex = 6;
+ //
+ // partityLabel
+ //
+ this.partityLabel.AutoSize = true;
+ this.partityLabel.Location = new System.Drawing.Point(29, 103);
+ this.partityLabel.Name = "partityLabel";
+ this.partityLabel.Size = new System.Drawing.Size(33, 13);
+ this.partityLabel.TabIndex = 7;
+ this.partityLabel.Text = "Parity";
+ //
+ // dataBitsLabel
+ //
+ this.dataBitsLabel.AutoSize = true;
+ this.dataBitsLabel.Location = new System.Drawing.Point(29, 127);
+ this.dataBitsLabel.Name = "dataBitsLabel";
+ this.dataBitsLabel.Size = new System.Drawing.Size(50, 13);
+ this.dataBitsLabel.TabIndex = 9;
+ this.dataBitsLabel.Text = "Data Bits";
+ //
+ // dataBitsTextBox
+ //
+ this.dataBitsTextBox.Enabled = false;
+ this.dataBitsTextBox.Location = new System.Drawing.Point(140, 124);
+ this.dataBitsTextBox.Name = "dataBitsTextBox";
+ this.dataBitsTextBox.Size = new System.Drawing.Size(34, 20);
+ this.dataBitsTextBox.TabIndex = 10;
+ //
+ // stopBitsLabel
+ //
+ this.stopBitsLabel.AutoSize = true;
+ this.stopBitsLabel.Location = new System.Drawing.Point(29, 151);
+ this.stopBitsLabel.Name = "stopBitsLabel";
+ this.stopBitsLabel.Size = new System.Drawing.Size(49, 13);
+ this.stopBitsLabel.TabIndex = 11;
+ this.stopBitsLabel.Text = "Stop Bits";
+ //
+ // handshakeLabel
+ //
+ this.handshakeLabel.AutoSize = true;
+ this.handshakeLabel.Location = new System.Drawing.Point(29, 175);
+ this.handshakeLabel.Name = "handshakeLabel";
+ this.handshakeLabel.Size = new System.Drawing.Size(62, 13);
+ this.handshakeLabel.TabIndex = 13;
+ this.handshakeLabel.Text = "Handshake";
+ //
+ // nameTextBox
+ //
+ this.nameTextBox.Enabled = false;
+ this.nameTextBox.Location = new System.Drawing.Point(140, 28);
+ this.nameTextBox.Name = "nameTextBox";
+ this.nameTextBox.Size = new System.Drawing.Size(130, 20);
+ this.nameTextBox.TabIndex = 2;
+ //
+ // nameLabel
+ //
+ this.nameLabel.AutoSize = true;
+ this.nameLabel.Location = new System.Drawing.Point(30, 31);
+ this.nameLabel.Name = "nameLabel";
+ this.nameLabel.Size = new System.Drawing.Size(35, 13);
+ this.nameLabel.TabIndex = 1;
+ this.nameLabel.Text = "Name";
+ //
+ // classNameLabel
+ //
+ this.classNameLabel.AutoSize = true;
+ this.classNameLabel.Location = new System.Drawing.Point(137, 7);
+ this.classNameLabel.Name = "classNameLabel";
+ this.classNameLabel.Size = new System.Drawing.Size(83, 13);
+ this.classNameLabel.TabIndex = 0;
+ this.classNameLabel.Text = "ComonentName";
+ //
+ // parityComboBox
+ //
+ this.parityComboBox.Enabled = false;
+ this.parityComboBox.FormattingEnabled = true;
+ this.parityComboBox.Location = new System.Drawing.Point(140, 100);
+ this.parityComboBox.Name = "parityComboBox";
+ this.parityComboBox.Size = new System.Drawing.Size(130, 21);
+ this.parityComboBox.TabIndex = 8;
+ //
+ // stopBitsComboBox
+ //
+ this.stopBitsComboBox.Enabled = false;
+ this.stopBitsComboBox.FormattingEnabled = true;
+ this.stopBitsComboBox.Location = new System.Drawing.Point(140, 148);
+ this.stopBitsComboBox.Name = "stopBitsComboBox";
+ this.stopBitsComboBox.Size = new System.Drawing.Size(130, 21);
+ this.stopBitsComboBox.TabIndex = 12;
+ //
+ // handshakeComboBox
+ //
+ this.handshakeComboBox.Enabled = false;
+ this.handshakeComboBox.FormattingEnabled = true;
+ this.handshakeComboBox.Location = new System.Drawing.Point(140, 172);
+ this.handshakeComboBox.Name = "handshakeComboBox";
+ this.handshakeComboBox.Size = new System.Drawing.Size(130, 21);
+ this.handshakeComboBox.TabIndex = 14;
+ //
+ // AmbientCfgCtrl
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Control;
+ this.Controls.Add(this.handshakeComboBox);
+ this.Controls.Add(this.stopBitsComboBox);
+ this.Controls.Add(this.parityComboBox);
+ this.Controls.Add(this.nameTextBox);
+ this.Controls.Add(this.nameLabel);
+ this.Controls.Add(this.classNameLabel);
+ this.Controls.Add(this.handshakeLabel);
+ this.Controls.Add(this.stopBitsLabel);
+ this.Controls.Add(this.dataBitsTextBox);
+ this.Controls.Add(this.dataBitsLabel);
+ this.Controls.Add(this.partityLabel);
+ this.Controls.Add(this.baudRateTextBox);
+ this.Controls.Add(this.baudRateLabel);
+ this.Controls.Add(this.comPortNrTextBox);
+ this.Controls.Add(this.label1);
+ this.Name = "AmbientCfgCtrl";
+ this.Size = new System.Drawing.Size(300, 240);
+ this.Load += new System.EventHandler(this.ModbusCfgCtrl_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox comPortNrTextBox;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label baudRateLabel;
+ private System.Windows.Forms.TextBox baudRateTextBox;
+ private System.Windows.Forms.Label partityLabel;
+ private System.Windows.Forms.Label dataBitsLabel;
+ private System.Windows.Forms.TextBox dataBitsTextBox;
+ private System.Windows.Forms.Label stopBitsLabel;
+ private System.Windows.Forms.Label handshakeLabel;
+ private System.Windows.Forms.TextBox nameTextBox;
+ private System.Windows.Forms.Label nameLabel;
+ private System.Windows.Forms.Label classNameLabel;
+ private System.Windows.Forms.ComboBox parityComboBox;
+ private System.Windows.Forms.ComboBox stopBitsComboBox;
+ private System.Windows.Forms.ComboBox handshakeComboBox;
+
+ }
+}
diff --git a/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.resx b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.resx
new file mode 100644
index 000000000..1af7de150
--- /dev/null
+++ b/TBF/BenchControl/RegisterReaders/SerialStream/SerialStreamCfgCtrl.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/TBF/BenchControl/TbfComponents.cs b/TBF/BenchControl/TbfComponents.cs
index d4c3cf059..0dcc77bce 100644
--- a/TBF/BenchControl/TbfComponents.cs
+++ b/TBF/BenchControl/TbfComponents.cs
@@ -134,7 +134,8 @@ namespace TBF.BenchControl
Factories.Add(new Elde.RegisterReader.Factory()); /// 'RegisterReader'
Factories.Add(new Elde.FixedStartRegisterReader.RegisterReaderFactory()); /// 'RegisterReader for standing start/stop'
Factories.Add(new TestMethods.iPerlCommunication.iPerlHead.Factory()); /// 'RegisterReader for iPerl'
- Factories.Add(new Elde.RegulValve.RegulValveFactory());
+ Factories.Add(new RegisterReaders.SerialStream.Factory()); /// 'RegisterReader for generic serial stream'
+ Factories.Add(new Elde.RegulValve.RegulValveFactory());
Factories.Add(new Elde.RegulValveCoax.Factory());
Factories.Add(new Elde.RegulValveTandem.RegulValveTandemFactory());
Factories.Add(new DB.SensusOracle.DatabaseFactory());
diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs
index 1789c1112..185e4b85e 100644
--- a/TBF/Properties/AssemblyInfo.cs
+++ b/TBF/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
-[assembly: AssemblyVersion("2.18.907.0")]
-[assembly: AssemblyFileVersion("2.18.907.0")]
+[assembly: AssemblyVersion("2.18.909.0")]
+[assembly: AssemblyFileVersion("2.18.909.0")]
diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj
index 9fe5ea648..c7b67f9e2 100644
--- a/TBF/TBF.csproj
+++ b/TBF/TBF.csproj
@@ -883,6 +883,20 @@
WriterCfgCtrl.cs
+
+
+
+
+ UserControl
+
+
+ SerialStreamCfgCtrl.cs
+
+
+
+
+
+
@@ -2376,6 +2390,9 @@
WriterCfgCtrl.cs
+
+ SerialStreamCfgCtrl.cs
+
WMErrorsForm.cs