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