diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs index f0fdd6294..2083225a5 100644 --- a/TBF/Properties/AssemblyInfo.cs +++ b/TBF/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("3.9.3004.1")] -[assembly: AssemblyFileVersion("3.9.3004.1")] +[assembly: AssemblyVersion("3.9.3010.1")] +[assembly: AssemblyFileVersion("3.9.3010.1")] diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/Factory.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/Factory.cs index 1de2eaa95..91cb8ea98 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/Factory.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/Factory.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using TBF.Rig.Generic; +using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; using TBF.Rig.RegisterReaders.iPerlASICReader.implementations; namespace TBF.Rig.RegisterReaders.GenesisRegReader @@ -10,9 +11,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader public override string ToString() { return ClassName; } - public IComponent DummyComponent() { return new SmartReader(); } + public IComponent DummyComponent() { return new GenesisSmartReader(); } - public IComponent GetComponent(IComponentCfg cfg, IList components) { return new SmartReader(cfg); } + public IComponent GetComponent(IComponentCfg cfg, IList components) { return new GenesisSmartReader(cfg); } public IComponentCfg DefaultConfig() { return new GenesisCfg(this); } diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/GenesisCfg.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/GenesisCfg.cs index 6e51bdf1b..109826a02 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/GenesisCfg.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/GenesisCfg.cs @@ -31,6 +31,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader public int MuxBoardNr; /// 0 = use RfidComPort(Nr), otherwise mux. board nr. 1 .. 4 public int Group; /// Number written to QuidoRS to connct the watermeter to RfidComPort, 1 .. 10 public CommunicationInterface CommunicationInterface; /// Communication Interface: RFID or NFC + /// + /// Procedure parameters [XmlIgnore] @@ -46,7 +48,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader /// Private parameterless constructor invoked by all other (public) constructors GenesisCfg() { - Name = "iPerl"; + Name = "Genesis"; ParentName = string.Empty; OptoComPortNr = 10; RfidComPortNr = 0; /// = use mux. board @@ -62,6 +64,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader this.Factory = factory; } + public string ToString(int i) { return $"{Name} Group1 (mux#)={MuxBoardNr}, Group2={Group}, Opto=Com{OptoComPortNr}, {CommunicationInterface}=Com{RfidComPortNr}"; diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/common/OptoTelegramRaw.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/common/OptoTelegramRaw.cs index 7e8d2a8fc..fb5dc11f6 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/common/OptoTelegramRaw.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/common/OptoTelegramRaw.cs @@ -4,6 +4,9 @@ using System; using System.Globalization; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol; + namespace TBF.Rig.RegisterReaders.GenesisRegReader.common @@ -78,195 +81,75 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common { } - /// - /// 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 telegram, int counter, float refFlow, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast, bool isLog = false) - // { - // DateTime = DateTime.Now; - // Counter = counter; - // RefFlow = refFlow; - // - // if ((telegram == null) || (telegram.Length < Length) || - // (telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') || - // (telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') || - // (!isLog && (telegram[40] != '\r' || telegram[41] != '\n'))) - // { - // Flags = OptoTelegramFlags.InvalidTelegram; - // return false; - // } - // - // UInt32 uEmfRaw; - // bool f1 = UInt32.TryParse(telegram.Substring(0, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out uEmfRaw); - // EmfRaw = (uEmfRaw > 0x7FFFFF) ? ((int)uEmfRaw - 0x1000000) : (int)uEmfRaw; - // - // bool f2 = Int16.TryParse(telegram.Substring(7, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out MagneticFieldRaw); - // bool f3 = Int16.TryParse(telegram.Substring(12, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out FlowRaw); - // bool f4 = UInt32.TryParse(telegram.Substring(17, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw); - // bool f5 = Int16.TryParse(telegram.Substring(24, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Impedance); - // bool f6 = UInt32.TryParse(telegram.Substring(29, 8), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp); - // bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum); - // - // byte calculatedCheckSum = 0; - // for (int i = 0; i < Length - 4; i++) - // { - // calculatedCheckSum += (byte)telegram[i]; - // } - // - // bool allOk = f1 && f2 && f3 && f4 && f5 && f6 && f7 && (calculatedCheckSum == CheckSum); - // - // if (allOk) - // { - // /// - // /// Cope with 'VolumeRaw' overflow - // /// - // Int64 uncorrected = (Int64)(((UInt64)volumeRawExtLast & 0xFFFFFFFFFF000000UL) | VolumeRaw); - // if (Math.Abs(uncorrected - volumeRawExtLast) <= 0x800000L) - // { - // VolumeRawExt = volumeRawExtLast = uncorrected; - // } - // else if (Math.Abs(uncorrected + 0x1000000L - volumeRawExtLast) <= 0x800000L) - // { - // VolumeRawExt = volumeRawExtLast = uncorrected + 0x1000000L; - // } - // else if (Math.Abs(uncorrected - 0x1000000L - volumeRawExtLast) <= 0x800000L) - // { - // VolumeRawExt = volumeRawExtLast = uncorrected - 0x1000000L; - // } - // else - // { - // VolumeRawExt = volumeRawExtLast = uncorrected; - // } - // - // /// - // /// Cope with 'Timestamp' overflow - // /// - // uncorrected = (Int64)(((UInt64)timestampExtLast & 0xFFFFFFFF00000000UL) | Timestamp); - // if (Math.Abs(uncorrected - timestampExtLast) <= 0x80000000L) - // { - // TimestampExt = timestampExtLast = uncorrected; - // } - // else if (Math.Abs(uncorrected + 0x100000000L - timestampExtLast) <= 0x80000000L) - // { - // TimestampExt = timestampExtLast = uncorrected + 0x100000000L; - // } - // else if (Math.Abs(uncorrected - 0x100000000L - timestampExtLast) <= 0x80000000L) - // { - // TimestampExt = timestampExtLast = uncorrected - 0x100000000L; - // } - // else - // { - // TimestampExt = timestampExtLast = uncorrected; - // } - // } - // - // Flags = allOk ? OptoTelegramFlags.OK : OptoTelegramFlags.InvalidTelegram; - // - // return allOk; - // } - // -------- TIMESTAMP (seconds) -------- - // bbbbbbbb – unsigned 32 bit ASIC time stamp in 8192 ticks per second– rolls over after 2^32 - private const double TS_TICKS_PER_SEC = 8192.0; - private const double TS_RANGE = 4294967296.0 / TS_TICKS_PER_SEC; // 2^32 / 8192 = 524288 sec + private const double TS_RANGE = StreamingDecoder.CpuTimeOverflowS; // -------- VOLUME (liters) -------- - // vvvvvv is unsigned 24-bit, 1 tick = 1/4 ml = 0.00025 L - private const double VOL_LITERS_PER_TICK = 0.00025; // liters per tick - private const double VOL_RANGE = 16777216.0 * VOL_LITERS_PER_TICK; // 2^24 * 0.00025 = 4194.304 L - - // -------- VOLUME (liters) -------- - private const double GAL_TO_LITER = 3.785411784; + private const double VOL_RANGE = StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000; public void UpdateFromSmart( - Object data, + CalibrationRecord data, int counter, float refFlow, ref double volumeRawExtLast, ref double timestampExtLast) { + int iChannel = data.Channel - 1; DateTime = DateTime.Now; Counter = counter; RefFlow = refFlow; - - throw new NotImplementedException(); - // FlowRaw = data.RawFlow; - // VolumeRaw = data.RawVolume; - // - // // ---- TIMESTAMP RAW (seconds, modulo TS_RANGE) ---- - // // If upstream conversion ever produced negative values, normalize them. - // double ts = data.AsicTimestamp; // already in seconds, but wraps every TS_RANGE - // ts = ts % TS_RANGE; - // if (ts < 0) ts += TS_RANGE; - // - // Timestamp = ts; - // - // // ---------- VOLUME UNWRAP ---------- - // double v = VolumeRaw; - // - // if (double.IsNaN(volumeRawExtLast)) - // { - // VolumeRawExt = volumeRawExtLast = v; - // } - // else - // { - // // nearest-lap unwrap - // //double k = Math.Round(volumeRawExtLast - v) / VOL_RANGE); - // if (v < volumeRawExtLast) - // { - // VolumeRawExt = volumeRawExtLast = v + VOL_RANGE; - // } - // else - // { - // VolumeRawExt = volumeRawExtLast = v; - // } - // } - // - // // ---------- TIMESTAMP UNWRAP (seconds) ---------- - // if (double.IsNaN(timestampExtLast)) - // { - // TimestampExt = timestampExtLast = ts; - // } - // else - // { - // // robust unwrap: choose the smallest jump across the modulo boundary - // double lastMod = timestampExtLast % TS_RANGE; - // if (lastMod < 0) lastMod += TS_RANGE; - // - // double delta = ts - lastMod; - // - // if (delta < -TS_RANGE / 2.0) delta += TS_RANGE; - // else if (delta > TS_RANGE / 2.0) delta -= TS_RANGE; - // - // TimestampExt = timestampExtLast = timestampExtLast + delta; - // } + FlowRaw = 0; + VolumeRaw = data.VolumeCm * 1000; // volume in litters + + // ---- TIMESTAMP RAW (seconds, modulo TS_RANGE) ---- + // If upstream conversion ever produced negative values, normalize them. + double ts = data.TimeS; // already in seconds, but wraps every TS_RANGE + ts = ts % TS_RANGE; + if (ts < 0) ts += TS_RANGE; + + Timestamp = ts; + + // ---------- VOLUME UNWRAP ---------- + double v = VolumeRaw; + + if (double.IsNaN(volumeRawExtLast)) + { + VolumeRawExt = volumeRawExtLast = v; + } + else + { + // nearest-lap unwrap + //double k = Math.Round(volumeRawExtLast - v) / VOL_RANGE); + if (v < volumeRawExtLast) + { + VolumeRawExt = volumeRawExtLast = v + VOL_RANGE; + } + else + { + VolumeRawExt = volumeRawExtLast = v; + } + } + + // ---------- TIMESTAMP UNWRAP (seconds) ---------- + if (double.IsNaN(timestampExtLast)) + { + TimestampExt = timestampExtLast = ts; + } + else + { + // robust unwrap: choose the smallest jump across the modulo boundary + double lastMod = timestampExtLast % TS_RANGE; + if (lastMod < 0) lastMod += TS_RANGE; + + double delta = ts - lastMod; + + if (delta < -TS_RANGE / 2.0) delta += TS_RANGE; + else if (delta > TS_RANGE / 2.0) delta -= TS_RANGE; + + TimestampExt = timestampExtLast = timestampExtLast + delta; + } } diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/BendDetectDataEventArgs.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/BendDetectDataEventArgs.cs index c19a76d41..54fde9cf2 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/BendDetectDataEventArgs.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/BendDetectDataEventArgs.cs @@ -1,5 +1,5 @@ using System; -using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/CalibDataEventArgs.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/CalibDataEventArgs.cs index a284932fb..db479c51e 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/CalibDataEventArgs.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/CalibDataEventArgs.cs @@ -1,5 +1,5 @@ using System; -using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/FlowDataEventArgs.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/FlowDataEventArgs.cs index b5119eb7e..d5b19ed21 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/FlowDataEventArgs.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/EventArguments/FlowDataEventArgs.cs @@ -1,5 +1,5 @@ using System; -using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/BendDetectionRecord.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/BendDetectionRecord.cs index e52ca8761..588a982c5 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/BendDetectionRecord.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/BendDetectionRecord.cs @@ -2,7 +2,7 @@ using System.Text; using Xylem.Common.Metrology.Measurements; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords { /// /// diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/CalibrationRecord.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/CalibrationRecord.cs index 5211dff81..86b22eab5 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/CalibrationRecord.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/CalibrationRecord.cs @@ -2,7 +2,7 @@ using System.Text; using Xylem.Common.Metrology.Measurements; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords { /// /// diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/FlowTestRecord.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/FlowTestRecord.cs index 17242528d..d8bc490f6 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/FlowTestRecord.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/MeasurementRecords/FlowTestRecord.cs @@ -2,7 +2,7 @@ using System.Text; using Xylem.Common.Metrology.Measurements; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords { /// /// diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RadioConfigurationParams.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RadioConfigurationParams.cs index 1d96b541f..bef9e4093 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RadioConfigurationParams.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RadioConfigurationParams.cs @@ -1,6 +1,6 @@ using System; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages { /// diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RegisterToDb.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RegisterToDb.cs index cdd4d56b2..4a99f4b40 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RegisterToDb.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/RegisterToDb.cs @@ -1,6 +1,6 @@ using System; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages { /// /// diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/TempTofCalc.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/TempTofCalc.cs index ef9ffabef..71cf121a9 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/TempTofCalc.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/DataPackages/TempTofCalc.cs @@ -1,6 +1,6 @@ using System; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages { /// /// Temperature Time Of Flight calculation diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/BaseProtocol.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/BaseProtocol.cs index 7c0709dfe..53efe1ddc 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/BaseProtocol.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/BaseProtocol.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Threading; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments; -using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore.EventArguments; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol; using BaseDataEventArgs = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments.BaseDataEventArgs; diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/IProtocol.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/IProtocol.cs index 1467dae16..91d942834 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/IProtocol.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Interfaces/Protocols/ProtocolCore/IProtocol.cs @@ -1,6 +1,5 @@ using System; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments; -using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore.EventArguments; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol; using BaseDataEventArgs = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments.BaseDataEventArgs; diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/RequestProtocol/RequestRecord.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/RequestProtocol/RequestRecord.cs index f7a196c10..f141af08c 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/RequestProtocol/RequestRecord.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/RequestProtocol/RequestRecord.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers; -using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig; using CommunicationConfig = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig.CommunicationConfig; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/StreamingProtocol/StreamingDecoder.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/StreamingProtocol/StreamingDecoder.cs index 19c99d042..f5454daf6 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/StreamingProtocol/StreamingDecoder.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Protocols/StreamingProtocol/StreamingDecoder.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; -using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; // ReSharper disable UnusedMember.Local @@ -15,13 +15,13 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protoco { private const Double MilliLitersToCmFactor = 1.0E-6; private const Double CpuTimeToSecondsFactor = 1.0 / 0x10000; - private const Double CpuTimeOverflowS = 0x100000000 * CpuTimeToSecondsFactor; + public const Double CpuTimeOverflowS = 0x100000000 * CpuTimeToSecondsFactor; private const Double LitersPerSecondToCmPerHourFactor = 3600.0 / 1000.0; private const Double DefaultVolumeScaleRawPerMl = 1024.0; private const Double DefaultVolumeFactorRawToCm = MilliLitersToCmFactor / DefaultVolumeScaleRawPerMl; private const Double MaxGenesisAccuVolumeRaw = UInt32.MaxValue; //0x100000000; //2^32 - private const Double DefaultAccuDutOverflowVolumeCm = MaxGenesisAccuVolumeRaw * DefaultVolumeFactorRawToCm; + public const Double DefaultAccuDutOverflowVolumeCm = MaxGenesisAccuVolumeRaw * DefaultVolumeFactorRawToCm; private const Double DisplayMlSetupDutOverflowVolumeCm = 1000.0; //overflow of LCD if set to ml private const Double TofToSecondsFactor38Bit = 1.0 / 0x4000000000; // 2^38 diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/ByteArray.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/ByteArray.cs index 92a66d441..b01777095 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/ByteArray.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/ByteArray.cs @@ -1,4 +1,4 @@ -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { public struct ByteArray { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Enum8.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Enum8.cs index ccd1f56e6..359ff4ba6 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Enum8.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Enum8.cs @@ -1,4 +1,4 @@ -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { // internal struct Enum8 public struct Enum8 diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Rpc.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Rpc.cs index 3b6b7fa05..120253b91 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Rpc.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/Rpc.cs @@ -1,4 +1,4 @@ -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { public struct Rpc { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StaticType.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StaticType.cs index 935412ac5..9a7bdd974 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StaticType.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StaticType.cs @@ -1,6 +1,6 @@ using System; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { /// /// Static type to define restore capability diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StatusT.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StatusT.cs index 1f1ea26ac..771aad1a2 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StatusT.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/StatusT.cs @@ -1,4 +1,4 @@ -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { public struct StatusT { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/TimeT.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/TimeT.cs index da8e20ea3..eefe2b4a3 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/TimeT.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/TimeT.cs @@ -1,6 +1,6 @@ using System; -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { /// diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/UInt672.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/UInt672.cs index 7538afa2a..64499add8 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/UInt672.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/UInt672.cs @@ -1,6 +1,4 @@ -using System; - -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { public struct UInt672 { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_dewa.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_dewa.cs index e62556c84..183cfea77 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_dewa.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_dewa.cs @@ -1,4 +1,4 @@ -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { // ReSharper disable once InconsistentNaming internal class st_radio_dewa diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_tfx.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_tfx.cs index ad92ed7fc..bb6b96407 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_tfx.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/DataTypes/st_radio_tfx.cs @@ -1,4 +1,4 @@ -namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes { // ReSharper disable once InconsistentNaming internal class st_radio_tfx diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterCheck.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterCheck.cs index 0a7c87e06..3a5724c6a 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterCheck.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterCheck.cs @@ -1,6 +1,6 @@ using System; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; -using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterConverter.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterConverter.cs index 8dba1d758..427b5cc3e 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterConverter.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterConverter.cs @@ -4,8 +4,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; -using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; -using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers { diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterDefinition.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterDefinition.cs index fba1e0716..5494ffc47 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterDefinition.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Genesis/Registers/RegisterDefinition.cs @@ -1,6 +1,6 @@ using System; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes; using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json; -using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes; using Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/OptoHeadTest.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/OptoHeadTest.cs index bccf6601a..aad213dfd 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/OptoHeadTest.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/OptoHeadTest.cs @@ -17,11 +17,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication private GenesisSmartReader genesisHead; private SerialDriver serialDriver; - public static SerialDriver BuildConnection(GenesisSmartReader iHead) + public static SerialDriver BuildConnection(GenesisSmartReader genesidHead) { return new SerialDriverBuilder() - .WithPort($"COM{iHead.RfidComPortNr}") - .WithBaudRate(2400) + .WithPort($"COM{genesidHead.RfidComPortNr}") + .WithBaudRate(9600) .WithDataBits(8) .WithParity(Parity.None) .WithStopBits(StopBits.One) diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/ISerialDriver.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/ISerialDriver.cs index 3e9b8362a..cc07bcf90 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/ISerialDriver.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/ISerialDriver.cs @@ -1,9 +1,25 @@ -namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils +using System; +using System.Text; + +namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils { - public interface ISerialDriver + public interface ISerialDriver : IDisposable { - bool IsOpen(); - bool Open(); - byte[] SendAndWait(byte[] request, int timeout); + bool IsOpen(); + bool Open(); + void CloseConnection(); + + void DiscardInBuffer(); + void DiscardOutBuffer(); + + int BytesToRead { get; } + int BytesToWrite { get; } + + string ReadLine(); + string ReadExisting(); + + Encoding Encoding { get; } + + byte[] SendAndWait(byte[] request, int timeout); } } \ No newline at end of file diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriver.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriver.cs index a13bce574..4f450c91f 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriver.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriver.cs @@ -1,66 +1,73 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO.Ports; -using System.Threading; -using FluentNHibernate.Conventions; -using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.hexLogger; +using System.Text; +using System.Threading.Tasks; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils { - public class SerialDriver : IDisposable, TestMethods.iPerlCommunication.communication.Utils.ISerialDriver + public class SerialDriver : IDisposable, ISerialDriver { readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(SerialDriver)); + public string ErrorMessage { get; private set; } - private List SerialPortReadBuffer = new List(); private SerialPort _serialPort; - private readonly List _binMessages = new List(); - private bool _isReading; - // Stored configuration (used by Builder) private readonly string _portName; private readonly int _baudRate; private readonly int _dataBits; private readonly Parity _parity; private readonly StopBits _stopBits; + private readonly Handshake _handshake; private readonly int _readTimeout; private readonly int _writeTimeout; - - private readonly ManualResetEvent _responseReceived = new ManualResetEvent(false); + private readonly int _openTimeoutMs; + private readonly string _newLine; + private readonly Encoding _encoding; + private readonly bool _dtrEnable; + private readonly bool _rtsEnable; + private readonly bool _discardInBufferOnOpen; + private readonly bool _discardOutBufferOnOpen; - #region Constructors - - // Default constructor (legacy support) public SerialDriver() { _serialPort = new SerialPort(); } - // Builder constructor internal SerialDriver( string portName, int baudRate, int dataBits, Parity parity, StopBits stopBits, + Handshake handshake, int readTimeout, - int writeTimeout) + int writeTimeout, + int openTimeoutMs, + string newLine, + Encoding encoding, + bool dtrEnable, + bool rtsEnable, + bool discardInBufferOnOpen, + bool discardOutBufferOnOpen) { _portName = portName; _baudRate = baudRate; _dataBits = dataBits; _parity = parity; _stopBits = stopBits; + _handshake = handshake; _readTimeout = readTimeout; _writeTimeout = writeTimeout; + _openTimeoutMs = openTimeoutMs; + _newLine = newLine; + _encoding = encoding; + _dtrEnable = dtrEnable; + _rtsEnable = rtsEnable; + _discardInBufferOnOpen = discardInBufferOnOpen; + _discardOutBufferOnOpen = discardOutBufferOnOpen; } - #endregion - - #region Open / Close - - // Builder-based open public bool Open() { return OpenConnection( @@ -69,12 +76,19 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils _dataBits, _parity, _stopBits, + _handshake, _readTimeout, - _writeTimeout + _writeTimeout, + _openTimeoutMs, + _newLine, + _encoding, + _dtrEnable, + _rtsEnable, + _discardInBufferOnOpen, + _discardOutBufferOnOpen ); } - // Legacy API (unchanged) public bool OpenConnection( string comPort, int baudrate, @@ -83,6 +97,42 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils StopBits stopbits, int readTimeout = 1000, int writeTimeout = 1000) + { + return OpenConnection( + comPort, + baudrate, + dataBits, + parity, + stopbits, + Handshake.None, + readTimeout, + writeTimeout, + 5000, + "\n", + Encoding.ASCII, + true, + true, + true, + true + ); + } + + public bool OpenConnection( + string comPort, + int baudrate, + int dataBits, + Parity parity, + StopBits stopbits, + Handshake handshake, + int readTimeout, + int writeTimeout, + int openTimeoutMs, + string newLine, + Encoding encoding, + bool dtrEnable, + bool rtsEnable, + bool discardInBufferOnOpen, + bool discardOutBufferOnOpen) { lock (this) { @@ -94,35 +144,54 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils _serialPort = new SerialPort(comPort, baudrate, parity, dataBits, stopbits) { + Handshake = handshake, + NewLine = newLine, + Encoding = encoding, ReadTimeout = readTimeout, - WriteTimeout = writeTimeout + WriteTimeout = writeTimeout, + DtrEnable = dtrEnable, + RtsEnable = rtsEnable }; - _serialPort.DataReceived += DataReceivedHandler; - _serialPort.Open(); + var openTask = Task.Run(() => _serialPort.Open()); + + if (!openTask.Wait(openTimeoutMs)) + { + _serialPort.Dispose(); + _serialPort = null; + + ErrorMessage = $"COM error: Opening serial port {comPort} timed out after {openTimeoutMs} ms."; + return false; + } + + if (!_serialPort.IsOpen) + { + ErrorMessage = $"COM error: Can't open {comPort}."; + return false; + } + + if (discardInBufferOnOpen) + _serialPort.DiscardInBuffer(); + + if (discardOutBufferOnOpen) + _serialPort.DiscardOutBuffer(); + + log.Debug("SerialDriver opened successfully for port: " + comPort); + return true; } catch (Exception ex) { ErrorMessage = $"COM error: Open failed {comPort}. {ex.Message}"; + CloseConnection(); return false; } - - if (!_serialPort.IsOpen) - { - ErrorMessage = $"COM error: Can't open {comPort}."; - return false; - } - - log.Debug("SerialDriver opened successfully for port: " + comPort); } - return true; } public void CloseConnection() { if (_serialPort != null) { - _serialPort.DataReceived -= DataReceivedHandler; if (_serialPort.IsOpen) _serialPort.Close(); @@ -133,176 +202,111 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils public bool IsOpen() => _serialPort?.IsOpen == true; - #endregion + public void DiscardInBuffer() + { + if (_serialPort != null && _serialPort.IsOpen) + _serialPort.DiscardInBuffer(); + } - #region Send / Receive + public void DiscardOutBuffer() + { + if (_serialPort != null && _serialPort.IsOpen) + _serialPort.DiscardOutBuffer(); + } + + public string ReadExisting() + { + if (!IsOpen()) + throw new InvalidOperationException("Serial port not open"); + + return _serialPort.ReadExisting(); + } + + public Encoding Encoding => _serialPort?.Encoding ?? _encoding; + + public int BytesToRead => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToRead : 0; + + public int BytesToWrite => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToWrite : 0; + + public string ReadLine() + { + if (!IsOpen()) + throw new InvalidOperationException("Serial port not open"); + + return _serialPort.ReadLine(); + } + + public string ReadLine(int timeoutMs) + { + if (!IsOpen()) + throw new InvalidOperationException("Serial port not open"); + + int originalTimeout = _serialPort.ReadTimeout; + + try + { + _serialPort.ReadTimeout = timeoutMs; + return _serialPort.ReadLine(); + } + finally + { + _serialPort.ReadTimeout = originalTimeout; + } + } public bool SendMessage(byte[] sendDataBytes, int length, int readTimeout = 1000, int writeTimeout = 1000) { if (!IsOpen()) return false; - if (sendDataBytes.Length == 0) return true; + if (sendDataBytes == null || sendDataBytes.Length == 0) return true; try { - PrepareReading(); - _serialPort.WriteTimeout = writeTimeout; _serialPort.ReadTimeout = readTimeout; _serialPort.Write(sendDataBytes, 0, length); - - _isReading = true; - - var stopwatch = Stopwatch.StartNew(); - while (_isReading) - { - if (stopwatch.ElapsedMilliseconds > readTimeout) - { - ErrorMessage = "COM error: Receive timeout"; - return false; - } - } + return true; } catch (Exception ex) { ErrorMessage = $"COM error: Transmit failed {_serialPort.PortName}. {ex.Message}"; return false; } - - return true; } - private void PrepareReading() - { - _serialPort.DiscardInBuffer(); - _binMessages.Clear(); - _responseReceived.Reset(); - SerialPortReadBuffer.Clear(); - _isReading = true; - } - - public byte[] GetRawData() - { - return _binMessages.ToArray(); - } - - private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) - { - lock (this) - { - if (_serialPort == null || !_serialPort.IsOpen) return; - - try - { - //Thread.Sleep(5); - - if (!SerialPortReadBuffer.IsEmpty()) - { - SerialPortReadBuffer.Clear(); - } - - int iWordCounter = 0; - bool isStart = false; - bool isQuestion = false; - int iLength = 0; - while (true)//_serialPort.BytesToRead > 0 - { - byte readByte = (byte)_serialPort.ReadByte(); - - //I have START - if (readByte == TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol.IperlHatProtocolConstants.Start) - { - iWordCounter++; - isStart = true; - } - // I have QUESTION - if (readByte == TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol.IperlHatProtocolConstants.Question) - { - iWordCounter++; - isQuestion = true; - } - //I count length from start - if (iWordCounter > 0) - iWordCounter++; - - if (iWordCounter > 0) - { - //Store byte to data - SerialPortReadBuffer.Add(readByte); - } - // we have length - if (iLength == 0 && isStart && SerialPortReadBuffer.Count > 2 ) - { - iLength = (int)SerialPortReadBuffer[2]; - } - - //If we have enough bytes - if (isStart && iLength > 0 - && (SerialPortReadBuffer.Count >= iLength || - readByte == TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol.IperlHatProtocolConstants.End - ) - ) - { - break; - } - //if we read END - if (isQuestion && readByte == TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol.IperlHatProtocolConstants.End) - { - break; - } - } - - if (SerialPortReadBuffer.Count > 0) - { - _binMessages.AddRange(SerialPortReadBuffer.ToArray()); - _responseReceived.Set(); - } - } - catch (TimeoutException te) - { - // Ignore shutdown race conditions - } - finally - { - _isReading = false; - } - } - } - public byte[] SendAndWait(byte[] data, int timeoutMs) { if (!IsOpen()) throw new InvalidOperationException("Serial port not open"); - log.Debug("SendAndWait() - TX: " + HexFormatter.ToHex(data)); - PrepareReading(); - _serialPort.Write(data, 0, data.Length); - - if (!_responseReceived.WaitOne(timeoutMs)) + try + { + _serialPort.Write(data, 0, data.Length); + string line = ReadLine(timeoutMs); + return Encoding.GetBytes(line); + } + catch (TimeoutException) { - log.Error("SendAndWait() - Response timeout! Details: " + - " SerialPortReadBuffer: " + HexFormatter.ToHex(SerialPortReadBuffer.ToArray()) + - " _binMessages" + HexFormatter.ToHex(_binMessages.ToArray()) + - "_responseReceived: " + _responseReceived.WaitOne(0) - ); - ErrorMessage = "COM error: response timeout"; return null; } - - return GetRawData(); + catch (Exception ex) + { + ErrorMessage = $"COM error: SendAndWait failed {_serialPort.PortName}. {ex.Message}"; + return null; + } } - - #endregion - public void Dispose() { CloseConnection(); } - + public override string ToString() { - return "SerialDriver: " + _serialPort.PortName + " (opened status:" + _serialPort.IsOpen +")"; + if (_serialPort == null) + return "SerialDriver: (opened status:false)"; + + return "SerialDriver: " + _serialPort.PortName + " (opened status:" + _serialPort.IsOpen + ")"; } } -} +} \ No newline at end of file diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriverBuilder.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriverBuilder.cs index 36651723b..85f2b498a 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriverBuilder.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriverBuilder.cs @@ -1,17 +1,35 @@ using System; using System.IO.Ports; +using System.Text; +using System.Threading.Tasks; namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils { - public class SerialDriverBuilder + using System; + using System.IO.Ports; + using System.Text; + + public class SerialDriverBuilder { private string _portName; private int _baudRate = 9600; private int _dataBits = 8; private Parity _parity = Parity.None; private StopBits _stopBits = StopBits.One; + private Handshake _handshake = Handshake.None; + private int _readTimeout = 1000; private int _writeTimeout = 1000; + private int _openTimeoutMs = 5000; + + private string _newLine = "\n"; + private Encoding _encoding = Encoding.ASCII; + + private bool _dtrEnable = true; + private bool _rtsEnable = true; + + private bool _discardInBufferOnOpen = true; + private bool _discardOutBufferOnOpen = true; public SerialDriverBuilder WithPort(string portName) { @@ -43,6 +61,12 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils return this; } + public SerialDriverBuilder WithHandshake(Handshake handshake) + { + _handshake = handshake; + return this; + } + public SerialDriverBuilder WithTimeouts(int readTimeout, int writeTimeout) { _readTimeout = readTimeout; @@ -50,32 +74,112 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils return this; } + public SerialDriverBuilder WithReadTimeout(int readTimeout) + { + _readTimeout = readTimeout; + return this; + } + + public SerialDriverBuilder WithWriteTimeout(int writeTimeout) + { + _writeTimeout = writeTimeout; + return this; + } + + public SerialDriverBuilder WithOpenTimeout(int openTimeoutMs) + { + _openTimeoutMs = openTimeoutMs; + return this; + } + + public SerialDriverBuilder WithNewLine(string newLine) + { + _newLine = newLine; + return this; + } + + public SerialDriverBuilder WithEncoding(Encoding encoding) + { + _encoding = encoding ?? throw new ArgumentNullException(nameof(encoding)); + return this; + } + + public SerialDriverBuilder WithDtrEnable(bool enabled = true) + { + _dtrEnable = enabled; + return this; + } + + public SerialDriverBuilder WithRtsEnable(bool enabled = true) + { + _rtsEnable = enabled; + return this; + } + + public SerialDriverBuilder WithDiscardInputBufferOnOpen(bool discard = true) + { + _discardInBufferOnOpen = discard; + return this; + } + + public SerialDriverBuilder WithDiscardOutputBufferOnOpen(bool discard = true) + { + _discardOutBufferOnOpen = discard; + return this; + } + + private void Validate() + { + if (string.IsNullOrWhiteSpace(_portName)) + throw new InvalidOperationException("Port name must be specified."); + + if (_baudRate <= 0) + throw new InvalidOperationException("Baud rate must be greater than 0."); + + if (_dataBits <= 0) + throw new InvalidOperationException("Data bits must be greater than 0."); + + if (_openTimeoutMs <= 0) + throw new InvalidOperationException("Open timeout must be greater than 0."); + } + /// /// Build driver WITHOUT opening connection /// - public TestMethods.iPerlCommunication.communication.Utils.SerialDriver Build() + public SerialDriver Build() { - return new TestMethods.iPerlCommunication.communication.Utils.SerialDriver( + Validate(); + + return new SerialDriver( _portName, _baudRate, _dataBits, _parity, _stopBits, + _handshake, _readTimeout, - _writeTimeout + _writeTimeout, + _openTimeoutMs, + _newLine, + _encoding, + _dtrEnable, + _rtsEnable, + _discardInBufferOnOpen, + _discardOutBufferOnOpen ); } /// /// Build driver AND open connection /// - public TestMethods.iPerlCommunication.communication.Utils.SerialDriver BuildAndConnect() + public SerialDriver BuildAndOpen() { var driver = Build(); if (!driver.Open()) { throw new InvalidOperationException(driver.ErrorMessage); } + return driver; } } diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/FlowDirectionDetection.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/FlowDirectionDetection.cs index 78b20ed94..aa9b8372f 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/FlowDirectionDetection.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/FlowDirectionDetection.cs @@ -3,6 +3,7 @@ /// using System; +using System.Linq; using Common; using log4net; using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead; @@ -19,127 +20,154 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations private readonly double[] volumeRawFifo; private readonly double[] timestampFifo; // centered timestamps - private int fifoCount; - private int fifoIx; - private DateTime lastFifoWriteTime; + private int iChanelsCount = 3; + private int[] fifoCount; + private int[] fifoIx; + private DateTime[] lastFifoWriteTime; // regression sums (double is ideal here) - private double sumXX; - private double sumX; - private double sumXY; - private double sumY; + private double[] sumXX; + private double[] sumX; + private double[] sumXY; + private double[] sumY; - private double minSlope; - private double maxSlope; + private double[] minSlope; + private double[] maxSlope; // timestamp centering for numerical stability - private double firstTimestamp = double.NaN; + private double[] firstTimestamp; public FlowDirectionDetection() { volumeRawFifo = new double[FIFO_SIZE]; timestampFifo = new double[FIFO_SIZE]; + + fifoCount = new int[iChanelsCount]; + fifoIx = new int[iChanelsCount]; + lastFifoWriteTime = new DateTime[iChanelsCount]; + + sumXX = new double[iChanelsCount]; + sumX = new double[iChanelsCount]; + sumXY = new double[iChanelsCount]; + sumY = new double[iChanelsCount]; + + minSlope = new double[iChanelsCount]; + maxSlope = new double[iChanelsCount]; + + firstTimestamp = new []{double.NaN,double.NaN,double.NaN}; + ClearFifo(); } public void ClearFifo() { - fifoCount = 0; - fifoIx = 0; - lastFifoWriteTime = DateTime.MinValue; + + Array.Clear(fifoCount, 0, fifoCount.Length); + Array.Clear(fifoIx, 0, fifoIx.Length); + + lastFifoWriteTime = Enumerable + .Repeat(DateTime.MinValue, lastFifoWriteTime.Length) + .ToArray(); + + Array.Clear(sumXX, 0, sumXX.Length); + Array.Clear(sumX, 0, sumX.Length); + Array.Clear(sumXY, 0, sumXY.Length); + Array.Clear(sumY, 0, sumY.Length); - sumXX = 0; - sumX = 0; - sumXY = 0; - sumY = 0; - - minSlope = 0; - maxSlope = 0; - firstTimestamp = double.NaN; + Array.Clear(minSlope, 0, minSlope.Length); + Array.Clear(maxSlope, 0, maxSlope.Length); + + // clear FIFO - TODO test if this is necessary + Array.Clear(volumeRawFifo, 0, volumeRawFifo.Length); + Array.Clear(timestampFifo, 0, timestampFifo.Length); + + firstTimestamp = Enumerable + .Repeat(double.NaN, firstTimestamp.Length) + .ToArray(); } /// /// Add sample to rolling FIFO and update regression sums /// - public void WriteToFifo(double volumeRaw, double timestamp) + public void WriteToFifo(double[] volumeRaw, double[] timestamp, int iChanel) { // establish time origin (CRITICAL for double precision) - if (double.IsNaN(firstTimestamp)) - firstTimestamp = timestamp; + if (double.IsNaN(firstTimestamp[iChanel])) + firstTimestamp[iChanel] = timestamp[iChanel]; - double x = timestamp - firstTimestamp; // centered time - double y = volumeRaw; + double x = timestamp[iChanel] - firstTimestamp[iChanel]; // centered time + double y = volumeRaw[iChanel]; // remove oldest sample if buffer full - if (fifoCount == FIFO_SIZE) + if (fifoCount[iChanel] == FIFO_SIZE) { - double oldX = timestampFifo[fifoIx]; - double oldY = volumeRawFifo[fifoIx]; + double oldX = timestampFifo[fifoIx[iChanel]]; + double oldY = volumeRawFifo[fifoIx[iChanel]]; - sumXX -= oldX * oldX; - sumX -= oldX; - sumXY -= oldX * oldY; - sumY -= oldY; + sumXX[iChanel] -= oldX * oldX; + sumX[iChanel] -= oldX; + sumXY[iChanel] -= oldX * oldY; + sumY[iChanel] -= oldY; } else { - fifoCount++; + fifoCount[iChanel]++; } // add new sample - sumXX += x * x; - sumX += x; - sumXY += x * y; - sumY += y; + sumXX[iChanel] += x * x; + sumX[iChanel] += x; + sumXY[iChanel] += x * y; + sumY[iChanel] += y; // store sample - timestampFifo[fifoIx] = x; - volumeRawFifo[fifoIx] = y; + timestampFifo[fifoIx[iChanel]] = x; + volumeRawFifo[fifoIx[iChanel]] = y; - fifoIx = (fifoIx + 1) % FIFO_SIZE; - lastFifoWriteTime = DateTime.Now; + fifoIx[iChanel] = (fifoIx[iChanel] + 1) % FIFO_SIZE; + lastFifoWriteTime[iChanel] = DateTime.Now; } - public bool AreFifoDataValid() - { - return (DateTime.Now.Subtract(lastFifoWriteTime).TotalSeconds <= MAX_OPTO_DROPOUT) - && (fifoCount == FIFO_SIZE); - } + // public bool AreFifoDataValid() + // { + // return (DateTime.Now.Subtract(lastFifoWriteTime).TotalSeconds <= MAX_OPTO_DROPOUT) + // && (fifoCount == FIFO_SIZE); + // } - public OptoHeadState CheckFlowDirection(Counting counting, string iPerlHeadName) - { - if (!AreFifoDataValid()) - return OptoHeadState.OptoNok; - - try - { - double N = fifoCount; - - double numer = N * sumXY - sumX * sumY; - double denom = N * sumXX - sumX * sumX; - - if (Math.Abs(denom) < 1e-12) - return OptoHeadState.DirNok; - - double slope = numer / denom; - - if (slope > maxSlope) maxSlope = slope; - if (slope < minSlope) minSlope = slope; - - if (counting == Counting.Arbitrary || - (counting == Counting.Positive && maxSlope > Math.Abs(2 * minSlope)) || - (counting == Counting.Negative && minSlope < -Math.Abs(2 * maxSlope))) - { - return OptoHeadState.OptoAndDirOK; - } - - return OptoHeadState.DirNok; - } - catch (Exception ex) - { - log.ErrorFormat("{0} : CheckFlowDirection() failed: {1}", iPerlHeadName, ex); - return OptoHeadState.DirNok; - } - } + // public OptoHeadState CheckFlowDirection(Counting counting, string iPerlHeadName) + // { + // if (!AreFifoDataValid()) + // return OptoHeadState.OptoNok; + // + // try + // { + // double N = fifoCount; + // + // double numer = N * sumXY - sumX * sumY; + // double denom = N * sumXX - sumX * sumX; + // + // if (Math.Abs(denom) < 1e-12) + // return OptoHeadState.DirNok; + // + // double slope = numer / denom; + // + // if (slope > maxSlope) maxSlope = slope; + // if (slope < minSlope) minSlope = slope; + // + // if (counting == Counting.Arbitrary || + // (counting == Counting.Positive && maxSlope > Math.Abs(2 * minSlope)) || + // (counting == Counting.Negative && minSlope < -Math.Abs(2 * maxSlope))) + // { + // return OptoHeadState.OptoAndDirOK; + // } + // + // return OptoHeadState.DirNok; + // } + // catch (Exception ex) + // { + // log.ErrorFormat("{0} : CheckFlowDirection() failed: {1}", iPerlHeadName, ex); + // return OptoHeadState.DirNok; + // } + // } } } diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisImplHeadTestCtrl.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisImplHeadTestCtrl.cs index 3f4f5e437..ef40809f4 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisImplHeadTestCtrl.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisImplHeadTestCtrl.cs @@ -14,9 +14,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations public class GenesisImplHeadTestCtrl : IUniHeadTestCtrl { Thread optoThread; - GenesisSmartReader _genesiHead; + //GenesisSmartReader _genesiHead; - public GenesisSmartReader Head { get { return _genesiHead;} } + public GenesisSmartReader Head { get { return ISmartReader as GenesisSmartReader;} } public ISmartReader ISmartReader { get; set; } public bool stopWorkerThread { get; set; } @@ -69,7 +69,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations {"Read PCB", Operations.ReadPcbCmd}, {"Set Test Mode", Operations.SetTestModeCmd}, {"Set Active Mode", Operations.SetActiveModeCmd}, -#if DEBUG +#if TRUE {"Start Read Opto Data", Operations.ReadOptoDataCmd}, {"Stop Read Opto Data", Operations.StopReadOptoDataCmd}, #endif @@ -136,11 +136,13 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { stopWorkerThread = true; a.ISmartReader.StopDataStreamProcessing(); // close opto port + //Head.OptoHeadTest.StopDataStreamProcessing(); // close opto port } if (!optoThread.IsAlive) { a.ISmartReader.StartDataStreamProcessing(); // open opto port + //Head.OptoHeadTest.StartDataStreamProcessing(); // open opto port optoThread.Start(); } diff --git a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs index 05a1bbff1..444d97fcb 100644 --- a/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs +++ b/TBF/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReader.cs @@ -9,30 +9,33 @@ using Common; using Config.Entities; using log4net; using NHibernate; +using Remotion.Linq.Parsing.Structure.IntermediateModel; using Sensus.iPerl.NfcHandler; using TBF.Rig.Generic; using TBF.Rig.GenericDevices; using TBF.Rig.RegisterReaders.GenesisRegReader.common; using TBF.Rig.RegisterReaders.GenesisRegReader.communication; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils; using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed; using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.parserer; using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead; using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; - namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { /// /// based on IPerlReader class /// - public class GenesisSmartReader : ComponentBase, IDevice, IRegReaderDatastream, ISessionDataMngmnt, IOperation, /*ISmartReader,*/ IRegReaderSmart + public class GenesisSmartReader : ComponentBase, IDevice, IRegReaderDatastream, ISessionDataMngmnt, IOperation, ISmartReader, IRegReaderSmart { private static readonly ILog log = LogManager.GetLogger(typeof(GenesisSmartReader)); private static readonly ILog logStream = LogManager.GetLogger("StreamData"); public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } - + #if TURA_SPECIAL public const int OptoDataBufferSize = 250000; #else @@ -60,7 +63,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations readonly GenesisCfg genesisHeadCfg; + string ISmartReader.CommInterface => _commInterface; + public int RfidComPortNr { get { return genesisHeadCfg.RfidComPortNr; } } + public bool CommFailed { get; set; } + public bool Disabled { get; set; } public int OptoComPortNr { get { return genesisHeadCfg.OptoComPortNr; } } public int MuxBoardNrOrGroup14 { get { return genesisHeadCfg.MuxBoardNr; } } public int Group { get { return genesisHeadCfg.Group; } } @@ -108,8 +115,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations } } - public bool Disabled; - public bool CommFailed; + //public bool Disabled; + //public bool CommFailed; public int ResultCode; @@ -120,11 +127,13 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations public float[] X { get { return x; } } + private static int iChanelsCount = 3; + private int firstChanel; /// /// Passed to OptoTelegramRaw.UpdateFromString(...) /// - double volumeRawExtLast; - double timestampExtLast; + double[] volumeRawExtLast; + double[] timestampExtLast; FlowDirectionDetection flowDirectionDetection; @@ -182,7 +191,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations public int WMPulses { get { return wmPulses; } } public int WMRefPulses { get { return wmRefPulses; } } public double BeginWMState { get { return ResolveNaNDouble(beginWMState); } } - public double EndWMState { get { return ResolveNaNDouble(endWMState); } } + double ISmartReader.EndWMState { get; set; } + double ISmartReader.BeginWMState { get; set; } + double ICommonRegReader.EndWMState { get; set; } + double ICommonRegReader.BeginWMState { get; set; } + public double EndWMState { get { return ResolveNaNDouble(endWMState); } } public double WMVolume { get { return ResolveNaNDouble(wmVolume); } } public double WMTestTime { get { return ResolveNaNDouble(wmTestTime); } } @@ -204,7 +217,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations double endWMState; double wmVolume; double wmTestTime; - + /// /// New calibration factor calculated from the original factor (argument) @@ -390,9 +403,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations /// /// Timestamp from the opto telegram /// - private double lastTimestamp; - private double timestampSec; - private double timestampSec0; + private double[] lastTimestamp; + private double[] timestampSec; + private double[] timestampSec0; /// Test start volume for metrology in seconds public double TimestampSecStart @@ -413,9 +426,17 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations /// /// Volume of water from the opto telegram /// - private double lastVolumeRaw; /// Last read raw volume - private double volumeLtr; - private double volumeLtr0; + private double[] lastVolumeRaw; /// Last read raw volume + private double[] volumeLtr; + private double[] volumeLtr0; + + private int channel0 = -1; + + + private double Average(double[] data) + { + return data.Sum() / data.Length; + } /// Test start volume for metrology in liters public double VolumeLtrStart @@ -437,7 +458,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations /// /// Opto serial port and worker thread related private variables /// - private SerialPort optoSerialPort; + public ISerialDriver optoSerialPort; public GenesisSmartReader() { } @@ -447,11 +468,32 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { genesisHeadCfg = cfg as GenesisCfg; } + + private readonly Func _serialFactory; + + public GenesisSmartReader(Generic.IComponentCfg cfg, Func serialFactory = null) + : base(cfg) + { + genesisHeadCfg = cfg as GenesisCfg; + _serialFactory = serialFactory; + } + public override void Initialize() { x = new float[FeatureVectorSize]; flowDirectionDetection = new FlowDirectionDetection(); + + volumeRawExtLast = new double[iChanelsCount]; + timestampExtLast = new double[iChanelsCount]; + + lastTimestamp = new double[iChanelsCount]; + timestampSec = new double[iChanelsCount]; + timestampSec0 = new double[iChanelsCount]; + + lastVolumeRaw = new double[iChanelsCount]; /// Last read raw volume + volumeLtr = new double[iChanelsCount]; + volumeLtr0 = new double[iChanelsCount]; /// Allocate memory for opto-data from iPerl optoData = new OptoTelegramRaw[OptoDataBufferSize]; @@ -467,6 +509,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations synchronized2 = false; partOfTelegram = string.Empty; optoSerialPort = null; + if (DebugLevel == DebugMode.Normal) { @@ -474,12 +517,13 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations /// Check whether head is connected, working try { - OpenOptoSerialPort($"COM{genesisHeadCfg.OptoComPortNr}", 38400, Parity.None, 8, StopBits.One, Handshake.None); + OpenOptoSerialPort($"COM{genesisHeadCfg.OptoComPortNr}", 115200, Parity.None, 8, StopBits.One, Handshake.None); CloseOptoSerialPort(); log.FatalFormat($"{Name} initialized: {this}"); } catch (Exception ex) { + log.FatalFormat($"{Name} initialization failed: {ex.Message}"); throw new Exception(ex.Message); } } @@ -534,31 +578,31 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations Counting currentFlowDir; /// - public OptoHeadState CheckFlowDirection() - { - return (flowDirectionDetection != null) ? flowDirectionDetection.CheckFlowDirection(currentFlowDir, Name) : OptoHeadState.DirNok; - } - /// - public void ChangeFlowDirection() - { - switch (InitFlowDir) - { - case Counting.Positive: - currentFlowDir = Counting.Negative; - break; - - case Counting.Negative: - currentFlowDir = Counting.Positive; - break; - - case Counting.Arbitrary: - default: - currentFlowDir = Counting.Arbitrary; - break; - } - - if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection - } + // public OptoHeadState CheckFlowDirection() + // { + // return (flowDirectionDetection != null) ? flowDirectionDetection.CheckFlowDirection(currentFlowDir, Name) : OptoHeadState.DirNok; + // } + // /// + // public void ChangeFlowDirection() + // { + // switch (InitFlowDir) + // { + // case Counting.Positive: + // currentFlowDir = Counting.Negative; + // break; + // + // case Counting.Negative: + // currentFlowDir = Counting.Positive; + // break; + // + // case Counting.Arbitrary: + // default: + // currentFlowDir = Counting.Arbitrary; + // break; + // } + // + // if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection + // } public void RunDeviceBefore() @@ -619,10 +663,10 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { ResultCode = 0; - volumeLtr = Double.NaN; - volumeLtr0 = Double.NaN; - timestampSec = Double.NaN; - timestampSec0 = Double.NaN; + volumeLtr = Enumerable.Repeat(Double.NaN, volumeLtr.Length).ToArray(); + volumeLtr0 = Enumerable.Repeat(Double.NaN, volumeLtr.Length).ToArray(); + timestampSec = Enumerable.Repeat(Double.NaN, volumeLtr.Length).ToArray(); + timestampSec0 = Enumerable.Repeat(Double.NaN, volumeLtr.Length).ToArray(); extraDataPath = null; @@ -884,21 +928,66 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations void ReadPulses() { - beginWMState = volumeLtr0; - endWMState = volumeLtr; + if (channel0 == -1) + return; + + if (channel0 == -2) + { + + beginWMState = 0; + endWMState = CalculateVolumeByChannels(); + wmVolume = Math.Abs(endWMState - beginWMState); + wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5); + log.Debug("wmPulses = " + wmPulses + "wmVolume = " + wmVolume + "PulsesPerLtr = " + PulsesPerLtr + ""); + if (StateMachine.ControlBoardMain != null) + wmRefPulses = StateMachine.ControlBoardMain.RefPulses; + else + wmRefPulses = 0; + wmTestTime = CalculateTimeByChannels(); + + return; + } + + beginWMState = volumeLtr0[channel0]; + endWMState = volumeLtr[channel0]; wmVolume = Math.Abs(endWMState - beginWMState); wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5); log.Debug("wmPulses = " + wmPulses + "wmVolume = " + wmVolume + "PulsesPerLtr = " + PulsesPerLtr + ""); - wmRefPulses = StateMachine.ControlBoardMain.RefPulses; - wmTestTime = timestampSec - timestampSec0; + if (StateMachine.ControlBoardMain != null) + wmRefPulses = StateMachine.ControlBoardMain.RefPulses; + else + wmRefPulses = 0; + wmTestTime = timestampSec[channel0] - timestampSec0[channel0]; } + private double CalculateTimeByChannels() + { + double[] timestampSec = new double[iChanelsCount]; + for (int iChanel = 0; iChanel < iChanelsCount; iChanel++) + { + timestampSec[iChanel] = timestampSec[iChanel] - timestampSec0[iChanel]; + } + + return Average(timestampSec); + } + + private double CalculateVolumeByChannels() + { + double[] volumeDelta = new double[iChanelsCount]; + for (int iChanel = 0; iChanel < iChanelsCount; iChanel++) + { + volumeDelta[iChanel] = volumeLtr[iChanel] - volumeLtr0[iChanel]; + } + + return Average(volumeDelta); + } + private void OpenOptoSerialPort( - string comPort, - int baudRate, - Parity parity, - int dataBits, - StopBits stopBit, + string comPort, + int baudRate, + Parity parity, + int dataBits, + StopBits stopBit, Handshake handshake, int openTimeoutMs = 3000) { @@ -909,42 +998,46 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations log.FatalFormat($"{Name} OproPort simulated: {this}"); return; } - + try { CloseOptoSerialPort(); - var port = new SerialPort(comPort, baudRate, parity, dataBits, stopBit) - { - Handshake = handshake, - NewLine = "\r\n", - Encoding = Encoding.ASCII - }; + log.Info($"Opening serial port {comPort} at {baudRate} baud rate"); - port.ReadTimeout = 5000; - port.WriteTimeout = 5000; - port.DtrEnable = true; - port.RtsEnable = true; - - // Run Open() on separate task - var openTask = Task.Run(() => port.Open()); - - if (!openTask.Wait(openTimeoutMs)) + if (_serialFactory != null) { - port.Dispose(); - throw new TimeoutException( - $"Opening serial port {comPort} timed out after {openTimeoutMs} ms."); + optoSerialPort = _serialFactory(); + if (!optoSerialPort.Open()) + throw new InvalidOperationException("Failed to open injected serial driver."); + } + else + { + optoSerialPort = new SerialDriverBuilder() + .WithPort(comPort) + .WithBaudRate(baudRate) + .WithParity(parity) + .WithDataBits(dataBits) + .WithStopBits(stopBit) + .WithHandshake(handshake) + .WithNewLine("\n") + .WithReadTimeout(5000) + .WithWriteTimeout(5000) + .WithDtrEnable(true) + .WithRtsEnable(true) + .WithOpenTimeout(openTimeoutMs) + .WithDiscardInputBufferOnOpen(true) + .WithDiscardOutputBufferOnOpen(true) + .BuildAndOpen(); } - - optoSerialPort = port; log.FatalFormat($"{Name} OptoPort opened: {this}"); } catch (Exception ex) { - log.FatalFormat($"{Name} OptoPort - error opening port: {this}" + log.FatalFormat($"{Name} OptoPort - error opening port: {this}" + Environment.NewLine + ex.Message); - throw; // NEVER use "throw ex;" (destroys stack trace) + throw; } } @@ -952,7 +1045,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { if (optoSerialPort != null) { - optoSerialPort.Close(); + optoSerialPort.CloseConnection(); optoSerialPort = null; log.FatalFormat($"{Name} OptoPort closed: {this}"); } @@ -967,7 +1060,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { try { - OpenOptoSerialPort($"COM{genesisHeadCfg.OptoComPortNr}", 38400, Parity.None, 8, StopBits.One, Handshake.None); + OpenOptoSerialPort($"COM{genesisHeadCfg.OptoComPortNr}", 115200, Parity.None, 8, StopBits.One, Handshake.None); } catch (Exception) { @@ -983,7 +1076,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations endTelegramIdx3 = 0; TestEndTelegramIx = 0; - if (optoSerialPort != null && optoSerialPort.IsOpen) optoSerialPort.DiscardInBuffer(); + if (optoSerialPort != null && optoSerialPort.IsOpen()) optoSerialPort.DiscardInBuffer(); if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection @@ -991,6 +1084,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations dataStreamState = DataStreamState.ProcessAndSave; } + public void SetCommunicationInterface(string commInterface) + { + throw new NotImplementedException(); + } + /// /// Returns true when processing and saving datastream data is in progress /// @@ -999,6 +1097,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations return dataStreamState == DataStreamState.ProcessAndSave; } + void ISmartReader.SetRfidInterface() + { + SetRfidInterface(); + } + /// /// Stop processing and saving datastream data /// @@ -1014,17 +1117,12 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations bool synchronized; bool synchronized2; string partOfTelegram; - - DiagnosticLedParser parser = new DiagnosticLedParser(DiagnosticLedState.State4); + + private string _commInterface; /// /// Reads opto-datastream via serial port. Invoked from RunDeviceBefore() /// - /// 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 @@ -1041,54 +1139,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { // This will now wait max 3 seconds (ReadTimeout) string line = optoSerialPort.ReadLine(); - - byte[] bytes = optoSerialPort.Encoding.GetBytes(line); - log.Debug("ComPort: " + OptoComPortNr + " OPTHO RX ← " + HexFormatter.ToSerialHex(bytes)); - - if (optoState == DataStreamState.ProcessAndSave) - { - DiagnosticLedState4Data data = - (DiagnosticLedState4Data)parser.ParseLine(line, false); - - int bufferIx = BufferIdx(optoDataCount); - - if (synchronized) - { - optoData[bufferIx].Counter = optoDataCount; - optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError); - } - - if (data != null) - { - log.Debug($"OPTHO {OptoComPortNr} Parsed optho data:" + data); - logStream.Debug($"ID: {OptoComPortNr} " + data); - - optoData[bufferIx].UpdateFromSmart( - data, - optoDataCount, - Convert.ToSingle(Sequences.ProcessData.RefFlow.Val), - ref volumeRawExtLast, - ref timestampExtLast); - - flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast); - - OptoTelegramReceived( - optoDataCount, - true, - volumeRawExtLast, - timestampExtLast); - } - - optoDataCount++; - } - else - { - // Flush mode - DiagnosticLedState4Data data = - (DiagnosticLedState4Data)parser.ParseLine(line, false); - - flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast); - } + ProcessOptoLine(line, optoState); } } catch (TimeoutException) @@ -1105,25 +1156,144 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations } } + + public void ProcessOptoLine(string line, DataStreamState optoState) + { + var encoding = optoSerialPort?.Encoding ?? Encoding.ASCII; + byte[] bytes = encoding.GetBytes(line); + log.Debug("ComPort: " + OptoComPortNr + " OPTHO RX ← " + HexFormatter.ToSerialHex(bytes)); + + StreamingDecoder streamingDecode = new StreamingDecoder(true); + streamingDecode.DecodeMsg(line); + + CalibrationRecord data = streamingDecode.DataCalib; + + if (optoState == DataStreamState.ProcessAndSave) + { + int bufferIx = BufferIdx(optoDataCount); + + if (synchronized) + { + optoData[bufferIx].Counter = optoDataCount; + optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError); + } + + if (data != null) + { + int iChanel = data.Channel - 1; + if (iChanel >= 0 && iChanel < iChanelsCount) + { + optoData[bufferIx].UpdateFromSmart( + data, + optoDataCount, + Convert.ToSingle(Sequences.ProcessData.RefFlow.Val), + ref volumeRawExtLast[iChanel], + ref timestampExtLast[iChanel]); + + flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast, iChanel); + + OptoTelegramReceived( + optoDataCount, + true, + volumeRawExtLast[iChanel], + timestampExtLast[iChanel], + iChanel); + } + } + + optoDataCount++; + } + else + { + if (data != null) + { + int iChanel = data.Channel - 1; + if (iChanel >= 0 && iChanel < iChanelsCount) + { + flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast, iChanel); + } + } + } + } + + void ISmartReader.ResetNfcInterface(bool? nfc_on) + { + ResetNfcInterface(nfc_on); + } + + private string _rxBuffer = ""; + public string ReadOptoData() { if (optoSerialPort is null) return ""; + string received = "."; lock (this) { try { - string line = optoSerialPort.ReadLine(); // string - byte[] bytes = optoSerialPort.Encoding.GetBytes(line); + string line = optoSerialPort.ReadLine(); + + var encoding = optoSerialPort?.Encoding ?? Encoding.ASCII; + byte[] bytes = encoding.GetBytes(line); received = HexFormatter.ToSerialHex(bytes); log.Debug("RX ← " + received); + + try + { + StreamingDecoder _streamingDecode = new StreamingDecoder(true); + _streamingDecode.DecodeMsg(line); + CalibrationRecord data = _streamingDecode.DataCalib; + log.Info($"OPTHO {OptoComPortNr} Parsed optho data:" + data); + } + catch (Exception ex) + { + log.Error($"OPTHO {OptoComPortNr} Read error: {ex.Message}"); + } + + // string line = optoSerialPort.ReadExisting(); + // + // if (!string.IsNullOrEmpty(line)) + // { + // string visual = line.Replace("\r", "\\r").Replace("\n", "\\n"); + // + // bool hasCR = line.Contains('\r'); + // bool hasLF = line.Contains('\n'); + // bool hasCRLF = line.Contains("\r\n"); + // + // log.Debug($"RAW: [{visual}]"); + // log.Debug($"CR: {hasCR}, LF: {hasLF}, CRLF: {hasCRLF}"); + // + // _rxBuffer += line; + // + // string[] parts = _rxBuffer.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None); + // + // // incomplete tail + // if (parts.Length > 0) + // { + // _rxBuffer = parts[parts.Length - 1]; + // } + // else + // { + // _rxBuffer = ""; + // } + // // ~ incomplete tail + // + // for (int i = 0; i < parts.Length - 1; i++) + // { + // string parsed = parts[i]; + // log.Debug($"PARSED: [{parsed}]"); + // + // byte[] bytes = optoSerialPort.Encoding.GetBytes(parsed); + // received = HexFormatter.ToSerialHex(bytes); + // + // log.Debug("RX ← " + received); + // } + //} } catch (TimeoutException) { - // ✅ No data received within 3 seconds - log.Debug($"ReadOptoData() OPTHO {OptoComPortNr} ReadLine timeout (3s) - continuing."); - - // Just continue without parsing + log.Debug($"ReadOptoData() OPTHO {OptoComPortNr} timeout - continuing."); } catch (Exception ex) { @@ -1132,7 +1302,12 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations } return received; } - + + void ISmartReader.SetNfcInterface() + { + SetNfcInterface(); + } + public async Task ReadOptoDataWithTimeoutAsync(int timeoutMs = 5000) { if (optoSerialPort == null) @@ -1194,31 +1369,36 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations } - void OptoTelegramReceived(int currentIx, bool async, double volumeRawExt, double timestampRawExt) + void OptoTelegramReceived(int currentIx, bool async, double volumeRawExt, double timestampRawExt, int iChanel) { currentTelegramIx = currentIx; - lastVolumeRaw = volumeRawExt; - lastTimestamp = timestampRawExt; + lastVolumeRaw[iChanel] = volumeRawExt; + lastTimestamp[iChanel] = timestampRawExt; - if (Double.IsNaN(volumeLtr) && Double.IsNaN(volumeLtr0)) + if (channel0 == -1) { - volumeLtr = lastVolumeRaw; - volumeLtr0 = volumeLtr; + channel0 = iChanel; + } + + if (Double.IsNaN(volumeLtr[iChanel]) && Double.IsNaN(iChanel)) + { + volumeLtr[iChanel] = lastVolumeRaw[iChanel]; + volumeLtr0[iChanel] = volumeLtr[iChanel]; } else { - volumeLtr = lastVolumeRaw; + volumeLtr[iChanel] = lastVolumeRaw[iChanel]; } - if (Double.IsNaN(timestampSec)&& Double.IsNaN(timestampSec0)) + if (Double.IsNaN(timestampSec[iChanel])&& Double.IsNaN(timestampSec0[iChanel])) { - timestampSec = lastTimestamp; - timestampSec0 = timestampSec; + timestampSec[iChanel] = lastTimestamp[iChanel]; + timestampSec0[iChanel] = timestampSec[iChanel]; } else { - timestampSec = lastTimestamp; + timestampSec[iChanel] = lastTimestamp[iChanel]; } } @@ -1673,10 +1853,10 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations public async Task DataEntry_EndVolumeAsync() { - if (optoSerialPort == null || !optoSerialPort.IsOpen) + if (optoSerialPort == null || !optoSerialPort.IsOpen()) { StartDataStreamProcessing(); - if (optoSerialPort == null || !optoSerialPort.IsOpen) + if (optoSerialPort == null || !optoSerialPort.IsOpen()) { log.Error($"optoSerialPort COM: {this.OptoComPortNr} is not open - DataEntry_EndVolumeAsync()"); return Double.NaN; @@ -1686,10 +1866,10 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations return await Task.Run(() => { log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}"); - volumeLtr = Double.NaN; + volumeLtr[channel0] = Double.NaN; int counter = 0; - while (Double.IsNaN(volumeLtr) && counter < 2) + while (Double.IsNaN(volumeLtr[channel0]) && counter < 2) { counter++; try @@ -1699,9 +1879,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { try { - DiagnosticLedState4Data data = - (DiagnosticLedState4Data)parser.ParseLine(readOptoDataWithTimeout, false); - volumeLtr = data.RawVolume; + StreamingDecoder _streamingDecode = new StreamingDecoder(true); + _streamingDecode.DecodeMsg(readOptoDataWithTimeout); + CalibrationRecord data = _streamingDecode.DataCalib; + log.Info($"OPTHO {OptoComPortNr} Parsed optho data:" + data); + volumeLtr[channel0] = data.VolumeCm * 1000; break; } catch (Exception ex) @@ -1718,11 +1900,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr}"); - if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort(); + if (optoSerialPort != null && optoSerialPort.IsOpen()) CloseOptoSerialPort(); - if (!Double.IsNaN(volumeLtr)) + if (!Double.IsNaN(volumeLtr[channel0])) { - endWMState = volumeLtr; + endWMState = volumeLtr[channel0]; if (!Double.IsNaN(beginWMState) && !Double.IsNaN(endWMState)) { //Solve roll over @@ -1731,7 +1913,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations log.Debug($"Solve roll over! endWMState: {endWMState} < beginWMState: {beginWMState}"); const double VOL_RANGE_LITERS = 16777216.0 * 0.00025; // 4,194.304 l endWMState += VOL_RANGE_LITERS; - volumeLtr = endWMState; + volumeLtr[channel0] = endWMState; ReadPulses(); log.Debug($"Solve roll over! Upgraded endWMState: {endWMState}, beginWMState: {beginWMState}"); } @@ -1761,9 +1943,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations Start(); - volumeLtr0 = Double.NaN; + volumeLtr0[channel0] = Double.NaN; int counter = 0; - while (Double.IsNaN(volumeLtr0) && counter < 10) + while (Double.IsNaN(volumeLtr0[channel0]) && counter < 10) { counter++; try @@ -1773,9 +1955,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations { try { - DiagnosticLedState4Data data = - (DiagnosticLedState4Data)parser.ParseLine(readOptoDataWithTimeout, false); - volumeLtr0 = data.RawVolume; + StreamingDecoder _streamingDecode = new StreamingDecoder(true); + _streamingDecode.DecodeMsg(readOptoDataWithTimeout); + CalibrationRecord data = _streamingDecode.DataCalib; + log.Info($"OPTHO {OptoComPortNr} Parsed optho data:" + data); + volumeLtr0[channel0] = data.VolumeCm * 1000; break; } catch (Exception ex) @@ -1791,11 +1975,11 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations } log.Debug($"Try get Start Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr0}"); - if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort(); + if (optoSerialPort != null && optoSerialPort.IsOpen()) CloseOptoSerialPort(); - if (!Double.IsNaN(volumeLtr0)) + if (!Double.IsNaN(volumeLtr0[channel0])) { - beginWMState = volumeLtr0; + beginWMState = volumeLtr0[channel0]; ReadPulses(); return beginWMState; } @@ -1822,9 +2006,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations return await Task.Run(() => { - - - + log.Debug($"Try get ReadSerialNr! COM: {this.RfidComPortNr}"); if (OptoHeadTest.ReadSerialNr()) { diff --git a/TBF/Rig/RegisterReaders/iPerlASICReader/implementations/SmartReader.cs b/TBF/Rig/RegisterReaders/iPerlASICReader/implementations/SmartReader.cs index 28b42e19f..bbeb66220 100644 --- a/TBF/Rig/RegisterReaders/iPerlASICReader/implementations/SmartReader.cs +++ b/TBF/Rig/RegisterReaders/iPerlASICReader/implementations/SmartReader.cs @@ -394,6 +394,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations } catch (Exception ex) { + log.FatalFormat("Opto-data serial port failure : {0}", ex.Message); throw new Exception(ex.Message); } } diff --git a/TBF/Rig/TbfComponents.cs b/TBF/Rig/TbfComponents.cs index 8a1ab2f2d..79fc0b6c0 100644 --- a/TBF/Rig/TbfComponents.cs +++ b/TBF/Rig/TbfComponents.cs @@ -138,6 +138,7 @@ namespace TBF.Rig new RegisterReaders.PoseidonReader.Factory(), new RegisterReaders.IPerlReader.Factory(), /// the same functionality as TestMethods.iPerlCommunication.iPerlHead.Factory(), new RegisterReaders.iPerlASICReader.Factory(), /// ASIC IPerl, C4 communication + new RegisterReaders.GenesisRegReader.Factory(), /// Genesis RegisterReader - dirrect communication with the head new RegisterReaders.PulsesFromUniCB.Factory(), /// 'RegisterReader' new RegisterReaders.StandingStartStop.Factory(), /// 'RegisterReader for standing start/stop' new TestMethods.iPerlCommunication.iPerlHead.Factory(), /// 'RegisterReader for iPerl' diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index c1d529130..55a064ff5 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -1411,7 +1411,6 @@ - diff --git a/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/FakeSerialDriver.cs b/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/FakeSerialDriver.cs new file mode 100644 index 000000000..d745532de --- /dev/null +++ b/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/FakeSerialDriver.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils; + +namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations +{ + + + public sealed class FakeSerialDriver : ISerialDriver + { + private readonly Queue _lines = new Queue(); + private bool _isOpen; + + public int OpenCalls { get; private set; } + public int CloseCalls { get; private set; } + public int DiscardInCalls { get; private set; } + public int DiscardOutCalls { get; private set; } + + public Encoding Encoding { get; set; } = Encoding.ASCII; + + public int BytesToRead => _lines.Count > 0 ? 1 : 0; + public int BytesToWrite => 0; + + public void EnqueueLine(string line) => _lines.Enqueue(line); + + public bool IsOpen() => _isOpen; + + public bool Open() + { + OpenCalls++; + _isOpen = true; + return true; + } + + public void CloseConnection() + { + CloseCalls++; + _isOpen = false; + } + + public void DiscardInBuffer() + { + DiscardInCalls++; + _lines.Clear(); + } + + public void DiscardOutBuffer() + { + DiscardOutCalls++; + } + + public string ReadLine() + { + if (!_isOpen) + throw new InvalidOperationException("Port not open."); + + if (_lines.Count == 0) + throw new TimeoutException(); + + return _lines.Dequeue(); + } + + public string ReadExisting() + { + if (!_isOpen) + throw new InvalidOperationException("Port not open."); + + if (_lines.Count == 0) + return string.Empty; + + return _lines.Dequeue(); + } + + public byte[] SendAndWait(byte[] request, int timeout) + { + return Array.Empty(); + } + + public void Dispose() + { + _isOpen = false; + } + } +} \ No newline at end of file diff --git a/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReaderTest.cs b/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReaderTest.cs new file mode 100644 index 000000000..c7db3a5c0 --- /dev/null +++ b/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReaderTest.cs @@ -0,0 +1,163 @@ +using System; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TBF.Rig; +using TBF.Rig.RegisterReaders.GenesisRegReader; +using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; + + +namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations +{ + [TestClass] + [TestSubject(typeof(GenesisSmartReader))] + public class GenesisSmartReaderTest + { + + + private static GenesisCfg CreateCfg() + { + Factory factory = new Factory(); + + return new GenesisCfg(factory); + // { + // OptoComPortNr = 7, + // RfidComPortNr = 8, + // MuxBoardNr = 1, + // Group = 1, + // CommunicationInterface = CommunicationInterface.RFID, + // ProcParams = new ProcParams + // { + // CalibTarget = 0, + // FactorLimitLo = 0, + // FactorLimitHi = 65535, + // Counting = Counting.Arbitrary + // } + // }; + } + + private static void SetPrivateField(object target, string fieldName, object value) + { + var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + if (field == null) + throw new MissingFieldException(target.GetType().FullName, fieldName); + + field.SetValue(target, value); + } + + private static T GetPrivateField(object target, string fieldName) + { + var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + if (field == null) + throw new MissingFieldException(target.GetType().FullName, fieldName); + + return (T)field.GetValue(target); + } + + [TestMethod] + public void Start_ShouldOpenPort_AndEnableProcessing() + { + var fake = new FakeSerialDriver(); + var reader = new GenesisSmartReader(CreateCfg(), () => fake); + + reader.Initialize(); + reader.Start(); + + Assert.IsTrue(fake.IsOpen()); + Assert.IsTrue(fake.OpenCalls >= 1); + Assert.AreEqual(1, fake.DiscardInCalls); + } + + [TestMethod] + public void Stop_ShouldClosePort() + { + var fake = new FakeSerialDriver(); + var reader = new GenesisSmartReader(CreateCfg(), () => fake); + + reader.Initialize(); + reader.Start(); + + Assert.IsTrue(fake.IsOpen()); + + reader.Stop(); + + Assert.IsFalse(fake.IsOpen()); + Assert.AreEqual(2, fake.CloseCalls); + } + + [TestMethod] + public void Run_ShouldCaptureStartTelegramIndex_WhenTimeReached() + { + var fake = new FakeSerialDriver(); + var reader = new GenesisSmartReader(CreateCfg(), () => fake); + + reader.Initialize(); + + SetPrivateField(reader, "currentTelegramIx", 12); + SetPrivateField(reader, "timeFromStart", 7); + + // prevent ReadPulses() from crashing if needed + SetPrivateField(reader, "volumeLtr", 0.0); + SetPrivateField(reader, "volumeLtr0", 0.0); + SetPrivateField(reader, "timestampSec", 0.0); + SetPrivateField(reader, "timestampSec0", 0.0); + + var ev = reader.Run(); + + Assert.AreEqual(Event.ReadRegisterDone, ev); + Assert.AreEqual(12, reader.TestStartTelegramIx); + } + + [TestMethod] + public void ProcessOptoLine_ShouldIncreaseOptoDataCount() + { + var fake = new FakeSerialDriver(); + var reader = new GenesisSmartReader(CreateCfg(), () => fake); + + reader.Initialize(); + + // Important if these arrays are not initialized elsewhere + SetPrivateField(reader, "volumeRawExtLast", new double[3]); + SetPrivateField(reader, "timestampExtLast", new double[3]); + + // valid sample line for your protocol + var line = "FFFFFE\t51EA\t0000\t65324E\t0087\tF6319DFF\t86"; + + reader.optoSerialPort = fake; + fake.Open(); + + reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave); + + int optoDataCount = GetPrivateField(reader, "optoDataCount"); + Assert.AreEqual(1, optoDataCount); + } + + [DataTestMethod] + [DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)] + [DataRow("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)] + [DataRow("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)] + public void ProcessOptoLine_ShouldUpdateExpectedChannel(string line, int expectedChannelIndex) + { + var fake = new FakeSerialDriver(); + fake.Open(); + + var reader = new GenesisSmartReader(CreateCfg(), () => fake); + reader.Initialize(); + + reader.optoSerialPort = fake; + + SetPrivateField(reader, "volumeRawExtLast", new double[3]); + SetPrivateField(reader, "timestampExtLast", new double[3]); + + reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave); + + var volumeRawExtLast = GetPrivateField(reader, "volumeRawExtLast"); + var timestampExtLast = GetPrivateField(reader, "timestampExtLast"); + + Assert.IsTrue( + volumeRawExtLast[expectedChannelIndex] != 0 || + timestampExtLast[expectedChannelIndex] != 0, + $"Channel {expectedChannelIndex} was not updated"); + } + } +} \ No newline at end of file diff --git a/TBFTests/TBFTests.csproj b/TBFTests/TBFTests.csproj index 19483322f..09b3e08f3 100644 --- a/TBFTests/TBFTests.csproj +++ b/TBFTests/TBFTests.csproj @@ -103,6 +103,8 @@ + +