From 8157a752bdff1a99c592dee69253b9729fec165b Mon Sep 17 00:00:00 2001 From: Michal Buzik Date: Tue, 25 Aug 2026 10:26:15 +0200 Subject: [PATCH] Ally multy reg readers implementation in SmartCommunicationForm.cs --- TBF/Rig/TbfComponents.cs | 2 + .../SmartCommunicationForm.cs | 199 +++++++++--------- .../implementations/AllyCorrections.cs | 37 ++++ .../implementations/GenesisCorrections.cs | 33 +++ .../implementations/IPerlASICCorrections.cs | 52 ++--- .../IPerlASICOpticalHeadTest.cs | 105 +++++++++ .../implementations/IPerlCorrections.cs | 53 ++--- .../ManualSmartCorrectionsBase.cs | 127 +++++++++++ .../implementations/PoseidonCorrections.cs | 11 +- TBF/TBF.csproj | 4 + TBF/UI/MainWnd.cs | 2 +- .../protocol/CordonelProtocolTests.cs | 22 +- .../IperlHatIntegrationTests.cs | 25 ++- .../SmartCommunicationFormTests.cs | 193 +++++++++++++++++ .../CorrectionsContractTests.cs | 92 ++++++++ .../RegisterReaderCorrectionWiringTests.cs | 78 +++++++ TBFTests/TBFTests.csproj | 3 + 17 files changed, 879 insertions(+), 159 deletions(-) create mode 100644 TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/AllyCorrections.cs create mode 100644 TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/GenesisCorrections.cs create mode 100644 TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICOpticalHeadTest.cs create mode 100644 TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/ManualSmartCorrectionsBase.cs create mode 100644 TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs create mode 100644 TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/CorrectionsContractTests.cs create mode 100644 TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/RegisterReaderCorrectionWiringTests.cs diff --git a/TBF/Rig/TbfComponents.cs b/TBF/Rig/TbfComponents.cs index c8993abaa..576d51676 100644 --- a/TBF/Rig/TbfComponents.cs +++ b/TBF/Rig/TbfComponents.cs @@ -144,6 +144,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.AllyReader.Factory(), // ALLY IPerl, 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' @@ -201,6 +202,7 @@ namespace TBF.Rig //new TestMethods.GenesisCommunication.GenesisHead.Factory(), new TestMethods.GrabImage.Factory(), new TestMethods.iPerlCommunication.TestMethodFactory(), /// iPerlCommunication + new TestMethods.AllyCalibration.Factory(), // Ally meter test new TestMethods.LeakTest.Factory(), new TestMethods.LiveStream.Factory(), new TestMethods.ManualEntry.Factory(), diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs index a4f41c33a..1130ce621 100644 --- a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs @@ -103,6 +103,9 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication /// RFID multiplexer PCB / RFID serial port and worker thread related variables /// private static List _corrections; + // Setting the initial combobox item raises SelectedIndexChanged before the + // constructor has finished preparing the form and its local settings. + private bool initializingMeterTypeItems; private static ICorrections Correction { get @@ -169,68 +172,38 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication } private static List GetNewCorrectionList(SmartCommunicationForm form) { - List correctionsList = new List(); + // Manual communication is extended by registering a correction adapter here. + // An adapter is included only when at least one configured reader belongs to it. + // This keeps unsupported protocols out of the type selector and context menu. + List candidates = new List + { + new IPerlCorrections(form), + new IPerlASICCorrections(form), + new GenesisCorrections(form), + new AllyCorrections(form), + new PoseidonCorrections(form) + }; - foreach (var smartHead in ProcessData.SmartHeadsUni) - { - try{ - if (smartHead is IperlHead iperlHead) - { - if (correctionsList.Any(x => x is IPerlCorrections)) - continue; - correctionsList.Add(new IPerlCorrections(form)); - continue; - } + if (ProcessData.SmartHeadsUni == null) + return new List(); - if (smartHead is SmartReader smartReader) - { - if (correctionsList.Any(x => x is SmartReader)) - continue; - correctionsList.Add(new PoseidonCorrections(form)); - continue; - } - - throw new Exception("Unknown smart head type"); - } - catch (Exception e) - { - log.Error("IdentifyReaderTypes()", e); - } - } - return correctionsList; - + return candidates + .Where(correction => ProcessData.SmartHeadsUni.Any(correction.IsFamilyOfSmartReader)) + .ToList(); } public static List IdentifyReaderTypes() { List typeReaders = new List(); - foreach (var smartHead in ProcessData.SmartHeadsUni) - { - try - { - if (typeReaders.Contains(smartHead.ClassName)) - continue; + if (_corrections == null || ProcessData.SmartHeadsUni == null) + return typeReaders; - if (smartHead is IperlHead iperlHead) - { - typeReaders.Add(iperlHead.ClassName); - continue; - } - - if (smartHead is SmartReader smartReader) - { - typeReaders.Add(smartReader.ClassName); - continue; - } - - throw new Exception("Unknown smart head type"); - } - catch (Exception e) - { - log.Error("IdentifyReaderTypes()", e); - } - } + foreach (ICorrections correction in _corrections) + { + if (ProcessData.SmartHeadsUni.Any(correction.IsFamilyOfSmartReader)) + typeReaders.Add(correction.TypeIdentificatorName()); + } return typeReaders; } @@ -411,33 +384,27 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication waterMeterPositions0?.Clear(); if (waterMeterPositions0 == null) waterMeterPositions0 = new List(); //iperlHeads = ProcessData.SmartHeadsUni; - string comparedTypeReader = SelectedTypeReader; + ICorrections corre = null; if (string.IsNullOrEmpty(SelectedTypeReader)) { - comparedTypeReader = ProcessData.SmartHeadsUni?.First()?.GetType().Name; + ISmartReader firstReader = ProcessData.SmartHeadsUni?.FirstOrDefault(); + corre = _corrections.FirstOrDefault(correction => correction.IsFamilyOfSmartReader(firstReader)); } - - ICorrections corre = null; - foreach (ICorrections correction in _corrections) + else { - if (correction.TypeIdentificatorName() == SelectedTypeReader) - { - corre = correction; - break; - } + corre = _corrections.FirstOrDefault( + correction => correction.TypeIdentificatorName() == SelectedTypeReader); } if (corre != null) { - int wmPos = 0; - foreach (var smartHead in ProcessData.SmartHeadsUni) + for (int wmPos = 0; wmPos < ProcessData.SmartHeadsUni.Count; wmPos++) { + ISmartReader smartHead = ProcessData.SmartHeadsUni[wmPos]; if (corre.IsFamilyOfSmartReader(smartHead)) { iperlHeads.Add(smartHead); waterMeterPositions0.Add(wmPos); - wmPos++; - if (wmPos >= ProcessData.WMsCount) break; } } @@ -448,8 +415,8 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication WaterMetersCount = iperlHeads.Count; ShuffleTextBoxes(WaterMetersCount, ProcessData.LineSize); - this.ContextMenu = Correction.GetContextMenu(); - Correction.PrepareForTestsActivities(WaterMetersCount); + this.ContextMenu = corre.GetContextMenu(); + corre.PrepareForTestsActivities(WaterMetersCount); } else { @@ -458,35 +425,53 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication } } - private void InitializeMeterTypeItems() - { - if (_corrections.Count >= 0) - { - meterTypeComboBox.Items.Clear(); - int iItem = 0; - foreach (ICorrections correction in _corrections) - { - string name = correction?.TypeIdentificatorName(); - if (string.IsNullOrEmpty(name)) - { - name = iItem.ToString(); - } - meterTypeComboBox.Items.Add(name); - if (iItem == 0) - SelectedTypeReader = name; - iItem++; - } - meterTypeComboBox.Visible = true; - meterTypeComboBox.Enabled = true; - meterTypeComboBox.SelectedIndex = 0; - } - } + private void InitializeMeterTypeItems() + { + if (_corrections != null && _corrections.Count > 0) + { + initializingMeterTypeItems = true; + try + { + meterTypeComboBox.Items.Clear(); + int iItem = 0; + foreach (ICorrections correction in _corrections) + { + string name = correction?.TypeIdentificatorName(); + if (string.IsNullOrEmpty(name)) + { + name = iItem.ToString(); + } + meterTypeComboBox.Items.Add(name); + if (iItem == 0) + SelectedTypeReader = name; + iItem++; + } + + meterTypeComboBox.Visible = true; + meterTypeComboBox.Enabled = true; + meterTypeComboBox.SelectedIndex = 0; + } + finally + { + initializingMeterTypeItems = false; + } + } + } private void InitializeWaterMeterData() { - InitializeSmartReaderLists(); - if (iperlHeads.Count <= 0) + if (checkBoxesEditMode) + { + // In manual mode the selected family must determine the displayed heads. + // Do not preload all smart readers and overwrite the combobox selection. UpdateHeads(); + } + else + { + InitializeSmartReaderLists(); + if (iperlHeads.Count <= 0) + UpdateHeads(); + } WaterMetersCount = iperlHeads.Count; ShuffleTextBoxes(WaterMetersCount, ProcessData.LineSize); @@ -674,6 +659,13 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication samplePictureBox3.Visible = false; } + if (_corrections == null || _corrections.Count == 0 || Correction == null) + { + ContextMenu = null; + log.Warn("SmartCommunicationForm opened without a registered manual communication adapter."); + return; + } + if (_corrections.Count > 0)//enable combo for choose SmartMeter { meterTypeComboBox.Visible = true; @@ -684,9 +676,19 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication /// Set location and checkbox states to values stored in local settings /// TBF.LocalSettings ls = Program.LocalSettings; - Left = (ls.iPerlCommunicationsFormLeft != 0) ? ls.iPerlCommunicationsFormLeft : 150; - Top = (ls.iPerlCommunicationsFormTop != 0) ? ls.iPerlCommunicationsFormTop : 150; - SetCheckBoxStates(ls.OptoHeadsEnabled); + if (ls == null) + { + log.Warn("SmartCommunicationForm loaded before LocalSettings were initialized; using default window and checkbox state."); + Left = 150; + Top = 150; + SetCheckBoxStates(0); + } + else + { + Left = (ls.iPerlCommunicationsFormLeft != 0) ? ls.iPerlCommunicationsFormLeft : 150; + Top = (ls.iPerlCommunicationsFormTop != 0) ? ls.iPerlCommunicationsFormTop : 150; + SetCheckBoxStates(ls.OptoHeadsEnabled); + } if (!checkBoxesEditMode) { @@ -919,7 +921,8 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication private void meterTypeComboBox_SelectedIndexChanged(object sender, EventArgs e) { - ComboBox senderCombo = sender as ComboBox; + if (initializingMeterTypeItems) return; + ComboBox senderCombo = sender as ComboBox; if (senderCombo == null) return; SelectedTypeReader = senderCombo.SelectedItem?.ToString(); UpdateHeads(); diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/AllyCorrections.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/AllyCorrections.cs new file mode 100644 index 000000000..46a5926c2 --- /dev/null +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/AllyCorrections.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Windows.Forms; +using TBF.Rig.RegisterReaders.AllyReader; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; + +namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations +{ + internal sealed class AllyCorrections : ManualSmartCorrectionsBase + { + private const int CommandTimeoutMs = 5000; + + public AllyCorrections(SmartCommunicationForm parent) : base(parent) { } + + public override string TypeIdentificatorName() => "ALLY"; + public override bool IsFamilyOfSmartReader(ISmartReader smartHead) => smartHead is AllyMeterReader; + + protected override IEnumerable CreateMenuItems() + { + yield return new MenuItem("Read Serial Number") { Tag = "ReadSerialNumber" }; + yield return new MenuItem("Read Version and Type") { Tag = "ReadVersion" }; + yield return new MenuItem("Set RFID mode") { Tag = "SetRfid" }; + yield return new MenuItem("Set NFC mode") { Tag = "SetNfc" }; + } + + protected override string ExecuteManualCommand(AllyMeterReader reader, string command) + { + switch (command) + { + case "ReadSerialNumber": return reader.ReadSerialNumber(CommandTimeoutMs); + case "ReadVersion": return reader.ReadVersionAndType(CommandTimeoutMs).ToString(); + case "SetRfid": reader.SetRfidInterface(); return "OK"; + case "SetNfc": reader.SetNfcInterface(); return "OK"; + default: return "Unsupported ALLY operation"; + } + } + } +} diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/GenesisCorrections.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/GenesisCorrections.cs new file mode 100644 index 000000000..7b846f139 --- /dev/null +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/GenesisCorrections.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Windows.Forms; +using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; + +namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations +{ + internal sealed class GenesisCorrections : ManualSmartCorrectionsBase + { + public GenesisCorrections(SmartCommunicationForm parent) : base(parent) { } + + public override string TypeIdentificatorName() => "Genesis"; + public override bool IsFamilyOfSmartReader(ISmartReader smartHead) => smartHead is GenesisSmartReader; + + protected override IEnumerable CreateMenuItems() + { + yield return new MenuItem("Read PCB Number") { Tag = "ReadPcb" }; + yield return new MenuItem("Set RFID mode") { Tag = "SetRfid" }; + yield return new MenuItem("Set NFC mode") { Tag = "SetNfc" }; + } + + protected override string ExecuteManualCommand(GenesisSmartReader reader, string command) + { + switch (command) + { + case "ReadPcb": return reader.OptoHeadTest.ReadRequest_PCB(); + case "SetRfid": reader.SetRfidInterface(); return "OK"; + case "SetNfc": reader.SetNfcInterface(); return "OK"; + default: return "Unsupported Genesis operation"; + } + } + } +} diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICCorrections.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICCorrections.cs index 75ac506dd..296cc73a2 100644 --- a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICCorrections.cs +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICCorrections.cs @@ -13,10 +13,9 @@ using Sensus.iPerl.NfcHandler; using TBF.Resources; using TBF.Rig.Generic; using TBF.Rig.RegisterReaders.CommonRR.IPerl; -using TBF.Rig.RegisterReaders.IPerlReader.implementations; +using TBF.Rig.RegisterReaders.iPerlASICReader.implementations; using TBF.Rig.Sequences; using TBF.Rig.TestMethods.iPerlCommunication; -using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead; using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; using CalibrationStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStruct; using CalibrationStructV4 = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStructV4; @@ -24,7 +23,7 @@ using CheckBoxImage = TBF.Boxes.CheckBoxImage; using Command = TBF.Rig.RegisterReaders.CommonRR.Command; using CommunicationInterface = TBF.Rig.RegisterReaders.CommonRR.CommunicationInterface; using ConfigStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.ConfigStruct; -using Factory = TBF.Rig.RegisterReaders.iPerlReaderUNI.Factory; +using Factory = TBF.Rig.RegisterReaders.iPerlASICReader.Factory; using MessageID = TBF.Rig.RegisterReaders.CommonRR.MessageID; using MeterState = TBF.Rig.RegisterReaders.CommonRR.MeterState; using MeterType = TBF.Rig.RegisterReaders.CommonRR.MeterType; @@ -228,7 +227,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++) { - IperlHead ihead = iperlHeads[wmNr0] as IperlHead; + SmartReader ihead = iperlHeads[wmNr0] as SmartReader; if ((ihead.Group == group) && (threadIx < muxBrdOrGroup14Nrs.Count) && (ihead.MuxBoardNrOrGroup14 == muxBrdOrGroup14Nrs[threadIx])) { @@ -306,14 +305,13 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations activityLabel.Text = menuItem.Text; List tasks = new List(); - foreach (var iSmartReader in ProcessData.SmartHeadsUni) + for (int position = 0; position < iperlHeads.Count; position++) { - if (!(iSmartReader is IperlHead iHead)) + if (!(iperlHeads[position] is SmartReader iHead)) { continue;//ignore different types of heads } - int position = iHead.Position - 1; if (position < 0 || position >= checkBoxes.Length || position >= messages.Length) { continue; // Skip this head if position is out of range @@ -342,37 +340,39 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations await Task.WhenAll(tasks); } - private async Task ProcessTask(IperlHead head, object tag) + private async Task ProcessTask(SmartReader head, object tag) { string txt = ""; switch (tag) { case "ReadPCB": - txt = OpticalHeadTest.ReadRequest_PCB(head); + txt = IPerlASICOpticalHeadTest.ReadPcb(head); break; case "WriteRequestPort_u8_Customer_Text": - txt = OpticalHeadTest.WriteRequestPort_u8_Customer_Text(head); + txt = "Unsupported ASIC operation"; break; case "OpenSealing": - txt = OpticalHeadTest.OpenSealing(head); + txt = "Unsupported ASIC operation"; break; case "StartTestMode": - txt = OpticalHeadTest.SetTestMode(head); + txt = IPerlASICOpticalHeadTest.SetTestMode(head); break; case "TurnOffTestMode": - txt = OpticalHeadTest.SetActiveMode(head); + txt = IPerlASICOpticalHeadTest.SetActiveMode(head); break; case "TurnOffRadio": - txt = OpticalHeadTest.TurnOffRadio(head); + txt = IPerlASICOpticalHeadTest.TurnOffRadio(head); break; case "SetProductionMode": - txt = OpticalHeadTest.SetProductionMode(head); + txt = IPerlASICOpticalHeadTest.SetProductionMode(head); break; case "SetRFID": - txt = OpticalHeadTest.SetRfidMode(head); + head.SetRfidInterface(); + txt = "OK"; break; case "SetNFC": - txt = OpticalHeadTest.SetNfcMode(head); + head.SetNfcInterface(); + txt = "OK"; break; } @@ -399,10 +399,12 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations /// for (int i = 0; i < textBoxesCount; i++) { - labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true; + bool hasHead = i < iperlHeads.Count; + labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = hasHead; + if (!hasHead) continue; - IperlHead iperlHead = iperlHeads[i] as IperlHead; + SmartReader iperlHead = iperlHeads[i] as SmartReader; if (!checkBoxesEditMode && (iperlHead == null || iperlHead.Disabled)) { /// iPerl position i+1 is disabled @@ -441,7 +443,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations { try { - var iPerl = iSmartReader as IperlHead; + var iPerl = iSmartReader as SmartReader; if (iPerl != null && iPerl.Group > lastGroup) lastGroup = iPerl.Group; } catch (Exception E) @@ -468,7 +470,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations { foreach (ISmartReader iPerl in iperlHeads) { - if (iPerl is IperlHead ihead) + if (iPerl is SmartReader ihead) { if (!muxBrdOrGroup14Nrs.Contains(ihead.MuxBoardNrOrGroup14)) muxBrdOrGroup14Nrs.Add(ihead.MuxBoardNrOrGroup14); @@ -2591,8 +2593,8 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations private static readonly List SupportedReaders = new List() { - typeof(IperlHead), - typeof(TestMethods.iPerlCommunication.iPerlHead.Factory) + typeof(SmartReader), + typeof(TBF.Rig.RegisterReaders.iPerlASICReader.Factory) }; public bool IsFamilyOfSmartReader(ISmartReader smartHead) { @@ -2814,7 +2816,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations } else { - IperlHead iperlHead = (iperlHeads[i] as IperlHead); + SmartReader iperlHead = (iperlHeads[i] as SmartReader); OptoHeadState checkFlowDirection = ((iperlHead == null) ? OptoHeadState.Disabled : iperlHead.CheckFlowDirection()); @@ -2875,4 +2877,4 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations } -} \ No newline at end of file +} diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICOpticalHeadTest.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICOpticalHeadTest.cs new file mode 100644 index 000000000..fe5a554f3 --- /dev/null +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlASICOpticalHeadTest.cs @@ -0,0 +1,105 @@ +using System; +using Config.Resources; +using log4net; +using Sensus.iPerl.RfidCom.Helper; +using TBF.Rig.RegisterReaders.CommonRR; +using TBF.Rig.RegisterReaders.CommonRR.IPerl; +using TBF.Rig.RegisterReaders.iPerlASICReader.implementations; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; +using static Sensus.iPerl.NfcHandler.MCI_Protocol; + +namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations +{ + /// + /// Manual commands for ASIC readers. They intentionally use the ASIC correction + /// transport path and never fall back to the old Sensus iPerl implementation. + /// + internal static class IPerlASICOpticalHeadTest + { + private static readonly ILog RfidDataLogger = LogManager.GetLogger("RfidData"); + + internal static string ReadPcb(SmartReader reader) + { + try + { + byte[] pcb; + int result = IPerlASICCorrections.ReadRequestPort( + SmartCommunicationForm.TestMethodCfg, + reader, + MessageID.Configuration, + StructName.Configuration, + 16, + 5, + out pcb); + + if (result == 0 && pcb != null) + { + return RfidHelper.HexLiteral2Unsigned( + RfidHelper.SwapHexcode(BitConverter.ToString(pcb).Replace("-", string.Empty))).ToString(); + } + + RfidDataLogger.ErrorFormat("COM{0}: ASIC Read PCB failed ({1}).", reader.RfidComPortNr, result); + return "Error"; + } + catch (Exception ex) + { + return ex.Message; + } + } + + internal static string SetActiveMode(SmartReader reader) + { + return WriteCommand(reader, Command.SetActiveMode, "Error Set Active Mode"); + } + + internal static string SetTestMode(SmartReader reader) + { + return WriteCommand(reader, Command.SetTestMode, "Error Set Test Mode"); + } + + internal static string TurnOffRadio(SmartReader reader) + { + return WriteRadioValue(reader, MessageID.RadioPassthrough, StructName.RadioParams, 0x1898, 3); + } + + internal static string SetProductionMode(SmartReader reader) + { + return WriteRadioValue(reader, MessageID.RadioPassthrough, StructName.RadioInfo, 0x1804, 1); + } + + private static string WriteCommand(SmartReader reader, Command command, string errorText) + { + int result = IPerlASICCorrections.WriteRequestPort( + SmartCommunicationForm.TestMethodCfg, + reader, + MessageID.Command, + StructName.Command, + 0, + 1, + new[] { (byte)command }); + return result == 0 ? "OK" : errorText; + } + + private static string WriteRadioValue(SmartReader reader, MessageID messageId, + StructName structName, int offset, byte value) + { + try + { + int result = IPerlASICCorrections.WriteRequestPort( + SmartCommunicationForm.TestMethodCfg, + reader, + messageId, + structName, + offset, + 1, + new[] { value }); + return result == 0 ? "OK" : "Error"; + } + catch (Exception ex) + { + return ex.Message; + } + } + } +} diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlCorrections.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlCorrections.cs index d14df3d0b..1d6b4b090 100644 --- a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlCorrections.cs +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/IPerlCorrections.cs @@ -14,9 +14,9 @@ using TBF.Resources; using TBF.Rig.Generic; using TBF.Rig.RegisterReaders.CommonRR.IPerl; using TBF.Rig.RegisterReaders.IPerlReader.implementations; +using LegacyOpticalHeadTest = TBF.Rig.RegisterReaders.iPerlReaderUNI.test.OpticalHeadTest; using TBF.Rig.Sequences; using TBF.Rig.TestMethods.iPerlCommunication; -using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead; using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; using CalibrationStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStruct; using CalibrationStructV4 = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStructV4; @@ -24,7 +24,7 @@ using CheckBoxImage = TBF.Boxes.CheckBoxImage; using Command = TBF.Rig.RegisterReaders.CommonRR.Command; using CommunicationInterface = TBF.Rig.RegisterReaders.CommonRR.CommunicationInterface; using ConfigStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.ConfigStruct; -using Factory = TBF.Rig.RegisterReaders.iPerlReaderUNI.Factory; +using Factory = TBF.Rig.RegisterReaders.IPerlReader.Factory; using MessageID = TBF.Rig.RegisterReaders.CommonRR.MessageID; using MeterState = TBF.Rig.RegisterReaders.CommonRR.MeterState; using MeterType = TBF.Rig.RegisterReaders.CommonRR.MeterType; @@ -139,7 +139,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations public IPerlCorrections(SmartCommunicationForm parent) { this.ParentFrom = parent; - TBF.Rig.RegisterReaders.iPerlReaderUNI.Factory factory = new Factory(); + Factory factory = new Factory(); TestMethod method = new TestMethod(factory.DefaultConfig()); this.testMethod = method; this.Cfg = method.testMethodCfg; @@ -228,7 +228,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations for (int wmNr0 = 0; wmNr0 < iperlHeads.Count; wmNr0++) { - IperlHead ihead = iperlHeads[wmNr0] as IperlHead; + SmartReader ihead = iperlHeads[wmNr0] as SmartReader; if ((ihead.Group == group) && (threadIx < muxBrdOrGroup14Nrs.Count) && (ihead.MuxBoardNrOrGroup14 == muxBrdOrGroup14Nrs[threadIx])) { @@ -306,14 +306,13 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations activityLabel.Text = menuItem.Text; List tasks = new List(); - foreach (var iSmartReader in ProcessData.SmartHeadsUni) + for (int position = 0; position < iperlHeads.Count; position++) { - if (!(iSmartReader is IperlHead iHead)) + if (!(iperlHeads[position] is SmartReader iHead)) { continue;//ignore different types of heads } - int position = iHead.Position - 1; if (position < 0 || position >= checkBoxes.Length || position >= messages.Length) { continue; // Skip this head if position is out of range @@ -342,37 +341,39 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations await Task.WhenAll(tasks); } - private async Task ProcessTask(IperlHead head, object tag) + private async Task ProcessTask(SmartReader head, object tag) { string txt = ""; switch (tag) { case "ReadPCB": - txt = OpticalHeadTest.ReadRequest_PCB(head); + txt = LegacyOpticalHeadTest.ReadRequest_PCB(head); break; case "WriteRequestPort_u8_Customer_Text": - txt = OpticalHeadTest.WriteRequestPort_u8_Customer_Text(head); + txt = "Unsupported old iPerl operation"; break; case "OpenSealing": - txt = OpticalHeadTest.OpenSealing(head); + txt = LegacyOpticalHeadTest.OpenSealing(head); break; case "StartTestMode": - txt = OpticalHeadTest.SetTestMode(head); + txt = LegacyOpticalHeadTest.SetTestMode(head); break; case "TurnOffTestMode": - txt = OpticalHeadTest.SetActiveMode(head); + txt = LegacyOpticalHeadTest.SetActiveMode(head); break; case "TurnOffRadio": - txt = OpticalHeadTest.TurnOffRadio(head); + txt = LegacyOpticalHeadTest.TurnOffRadio(head); break; case "SetProductionMode": - txt = OpticalHeadTest.SetProductionMode(head); + txt = LegacyOpticalHeadTest.SetProductionMode(head); break; case "SetRFID": - txt = OpticalHeadTest.SetRfidMode(head); + head.SetRfidInterface(); + txt = "OK"; break; case "SetNFC": - txt = OpticalHeadTest.SetNfcMode(head); + head.SetNfcInterface(); + txt = "OK"; break; } @@ -399,10 +400,12 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations /// for (int i = 0; i < textBoxesCount; i++) { - labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true; + bool hasHead = i < iperlHeads.Count; + labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = hasHead; + if (!hasHead) continue; - IperlHead iperlHead = iperlHeads[i] as IperlHead; + SmartReader iperlHead = iperlHeads[i] as SmartReader; if (!checkBoxesEditMode && (iperlHead == null || iperlHead.Disabled)) { /// iPerl position i+1 is disabled @@ -441,7 +444,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations { try { - var iPerl = iSmartReader as IperlHead; + var iPerl = iSmartReader as SmartReader; if (iPerl != null && iPerl.Group > lastGroup) lastGroup = iPerl.Group; } catch (Exception E) @@ -468,7 +471,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations { foreach (ISmartReader iPerl in iperlHeads) { - if (iPerl is IperlHead ihead) + if (iPerl is SmartReader ihead) { if (!muxBrdOrGroup14Nrs.Contains(ihead.MuxBoardNrOrGroup14)) muxBrdOrGroup14Nrs.Add(ihead.MuxBoardNrOrGroup14); @@ -2591,8 +2594,8 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations private static readonly List SupportedReaders = new List() { - typeof(IperlHead), - typeof(TestMethods.iPerlCommunication.iPerlHead.Factory) + typeof(SmartReader), + typeof(TBF.Rig.RegisterReaders.IPerlReader.Factory) }; public bool IsFamilyOfSmartReader(ISmartReader smartHead) { @@ -2814,7 +2817,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations } else { - IperlHead iperlHead = (iperlHeads[i] as IperlHead); + SmartReader iperlHead = (iperlHeads[i] as SmartReader); OptoHeadState checkFlowDirection = ((iperlHead == null) ? OptoHeadState.Disabled : iperlHead.CheckFlowDirection()); @@ -2875,4 +2878,4 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations } -} \ No newline at end of file +} diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/ManualSmartCorrectionsBase.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/ManualSmartCorrectionsBase.cs new file mode 100644 index 000000000..f96352bf3 --- /dev/null +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/ManualSmartCorrectionsBase.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using Config.Entities; +using Results.Entities; +using TBF.Rig.Generic; +using TBF.Rig.RegisterReaders.CommonRR.IPerl; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; +using CheckBoxImage = TBF.Boxes.CheckBoxImage; + +namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations +{ + /// + /// Common manual-only adapter for smart-meter reader families. + /// Test execution is deliberately not implemented here; each protocol must provide + /// its own test adapter before it is enabled for SmartCommunication test activities. + /// + internal abstract class ManualSmartCorrectionsBase : ICorrections + where TReader : class, ISmartReader + { + protected ManualSmartCorrectionsBase(SmartCommunicationForm parent) + { + ParentFrom = parent; + } + + public abstract string TypeIdentificatorName(); + public abstract bool IsFamilyOfSmartReader(ISmartReader smartHead); + protected abstract IEnumerable CreateMenuItems(); + protected abstract string ExecuteManualCommand(TReader reader, string command); + + public DateTime StartTime { get; set; } + public int StartTimeSec { get; set; } + public SmartCommunicationForm ParentFrom { get; set; } + public ITestMethodCfg Cfg { get; set; } + public IList MultiTestParams { get; private set; } + public IList Tests { get; set; } + public IList iperlHeads => ParentFrom.Heads; + + public ContextMenu GetContextMenu() + { + ContextMenu menu = new ContextMenu(); + foreach (MenuItem item in CreateMenuItems()) + { + item.Click += OnManualCommand; + menu.MenuItems.Add(item); + } + return menu; + } + + private async void OnManualCommand(object sender, EventArgs e) + { + MenuItem item = sender as MenuItem; + if (item == null || item.Tag == null) + return; + + ParentFrom.ActivityLabel.Text = item.Text; + string command = item.Tag.ToString(); + IList heads = ParentFrom.Heads; + List> operations = heads + .Select(head => head as TReader) + .Select(reader => reader == null + ? Task.FromResult("Unsupported reader") + : Task.Run(() => ExecuteSafely(reader, command))) + .ToList(); + + string[] results = await Task.WhenAll(operations); + for (int index = 0; index < results.Length && index < ParentFrom.Messages.Length; index++) + ParentFrom.Messages[index].Text = results[index]; + } + + private string ExecuteSafely(TReader reader, string command) + { + try + { + return ExecuteManualCommand(reader, command) ?? "OK"; + } + catch (Exception exception) + { + ParentFrom.Log.Error($"{TypeIdentificatorName()} manual command '{command}' failed for {reader.Name}.", exception); + return $"Error: {exception.Message}"; + } + } + + public void Load(Label[] labels, PictureBox[] counters, TextBox[] messages, CheckBoxImage[] checkBoxes, + int[] ckbIndex, bool[] ckbState, IList heads, int textBoxesCount, bool checkBoxesEditMode) + { + for (int index = 0; index < textBoxesCount; index++) + { + bool visible = heads != null && index < heads.Count; + labels[index].Visible = counters[index].Visible = messages[index].Visible = checkBoxes[index].Visible = visible; + if (!visible) + continue; + + ISmartReader reader = heads[index]; + bool enabled = reader != null && (!reader.Disabled || checkBoxesEditMode); + checkBoxes[index].Enabled = checkBoxes[index].Checked = ckbState[index] = enabled; + counters[index].BackColor = enabled ? SystemColors.Control : iPerlCommunicationConstants.DisabledColor; + messages[index].Text = enabled ? "---" : "Disabled by user"; + } + } + + public int GetHeadsCount() => ParentFrom?.Heads?.Count ?? 0; + public void PrepareForTestsActivities(int waterMeterPositions0) { } + public void StartDataStreamProcessingForActiveMeters(int iMultiTestParamsItem) { } + public void StopWorkerThreads(bool bStopAllThreads) { } + public bool GetStopWorkerThreads() => false; + public IList GetAllThreads() => new List(); + public ICorrections GetNewCorrection() => throw new NotSupportedException("Manual adapter cannot create a test adapter."); + public void GetGroup() { } + public void Worker(object threadData) => throw new NotSupportedException($"{TypeIdentificatorName()} test communication is not implemented."); + public bool WorkerActivity(string currentActivity, ISmartReader iHead, WaterMeter wm, Test currentTest, + int wmNr0, ref CommErr error, ref string resultStr, bool[] ckbState, int threadID, int currentActivityStep) + { + error = CommErr.WrongIPerlType; + resultStr = $"{TypeIdentificatorName()} test communication is not implemented."; + return false; + } + public void ProcessResultOfWorkerActivity(int iMultiTestParamsItem, string currentActivity, int currentGroup, + ISmartReader iHead, WaterMeter wm, int wmNr0, CommErr error, string resultStr, bool[] ckbState, int threadID) { } + public void DoOnCommCompleted(object sender, CommCompletedEventArgs data, IList waterMeterPositions0) { } + public void NormalClose(IList waterMeterPositions0) { } + } +} diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/PoseidonCorrections.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/PoseidonCorrections.cs index 5ec3477f8..ee1cf8ffb 100644 --- a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/PoseidonCorrections.cs +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/PoseidonCorrections.cs @@ -294,7 +294,9 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations /// for (int i = 0; i < textBoxesCount; i++) { - labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true; + bool hasHead = i < iperlHeads.Count; + labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = hasHead; + if (!hasHead) continue; ISmartReader iperlHead = iperlHeads[i]; @@ -516,14 +518,13 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations activityLabel.Text = menuItem.Text; List tasks = new List(); - foreach (var iSmartReader in ProcessData.SmartHeadsUni) + for (int position = 0; position < iperlHeads.Count; position++) { - if (!(iSmartReader is SmartReader iHead)) + if (!(iperlHeads[position] is SmartReader iHead)) { continue;//ignore different types of heads } - int position = iHead.Position; if (position < 0 || position >= checkBoxes.Length || position >= messages.Length) { continue; // Skip this head if position is out of range @@ -617,4 +618,4 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations } -} \ No newline at end of file +} diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index 771283bd9..34a0a1824 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -2558,7 +2558,11 @@ + + + + diff --git a/TBF/UI/MainWnd.cs b/TBF/UI/MainWnd.cs index 8bb041a6f..7d29de0fc 100644 --- a/TBF/UI/MainWnd.cs +++ b/TBF/UI/MainWnd.cs @@ -1199,7 +1199,7 @@ namespace TBF.UI private void optoHeadsToolStripMenuItem_Click(object sender, EventArgs e) { - new iPerlCommunicationForm(true).ShowDialog(); + new SmartCommunicationForm(true).ShowDialog(); } private void statusStrip1_DoubleClick(object sender, EventArgs e) diff --git a/TBFTests/Rig/RegisterReaders/GenesisRegReader/protocol/CordonelProtocolTests.cs b/TBFTests/Rig/RegisterReaders/GenesisRegReader/protocol/CordonelProtocolTests.cs index 7040a7711..312c0c86d 100644 --- a/TBFTests/Rig/RegisterReaders/GenesisRegReader/protocol/CordonelProtocolTests.cs +++ b/TBFTests/Rig/RegisterReaders/GenesisRegReader/protocol/CordonelProtocolTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.IO.Ports; +using System.Linq; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed; @@ -14,13 +15,30 @@ using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons; namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.protocol { [TestClass] - public class CordonelProtocolTests + [TestCategory("HardwareIntegration")] + public class CordonelProtocolIntegrationTests { private const string ComPort = "COM3"; // CHANGE THIS private const int BaudRate = 9600; private const int BaudRateOpto = 38400; private const int ReadTimeoutMs = 2000; + [TestInitialize] + public void RequireExplicitHardwareConfiguration() + { + if (!string.Equals(Environment.GetEnvironmentVariable("GENESIS_HW_TESTS"), "1", + StringComparison.Ordinal)) + { + Assert.Inconclusive("Set GENESIS_HW_TESTS=1 to run Genesis serial hardware integration tests."); + } + + if (!SerialPort.GetPortNames().Any(port => + string.Equals(port, ComPort, StringComparison.OrdinalIgnoreCase))) + { + Assert.Inconclusive($"Genesis hardware integration requires configured port {ComPort}."); + } + } + //...MF [TestMethod] @@ -590,4 +608,4 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.protocol } } -} \ No newline at end of file +} diff --git a/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs b/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs index 8fb73d79b..b6d17dcb0 100644 --- a/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs +++ b/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.IO.Ports; +using System.Linq; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed; @@ -14,6 +15,7 @@ using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons; namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol { [TestClass] + [TestCategory("HardwareIntegration")] public class IperlHatIntegrationTests { private const string ComPort = "COM12"; // CHANGE THIS @@ -22,6 +24,23 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat private const int BaudRateOpto = 38400; private const int ReadTimeoutMs = 2000; + [TestInitialize] + public void RequireExplicitHardwareConfiguration() + { + if (!string.Equals(Environment.GetEnvironmentVariable("IPERL_ASIC_HW_TESTS"), "1", + StringComparison.Ordinal)) + { + Assert.Inconclusive("Set IPERL_ASIC_HW_TESTS=1 to run iPerl ASIC serial hardware integration tests."); + } + + string[] ports = SerialPort.GetPortNames(); + if (!ports.Any(port => string.Equals(port, ComPort, StringComparison.OrdinalIgnoreCase)) || + !ports.Any(port => string.Equals(port, ComPortOptho, StringComparison.OrdinalIgnoreCase))) + { + Assert.Inconclusive($"iPerl ASIC hardware integration requires configured ports {ComPort} and {ComPortOptho}."); + } + } + [TestMethod] [TestCategory("Hardware")] [TestCategory("Serial")] @@ -454,7 +473,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat /// [TestMethod] [TestCategory("Hardware")] - public void Serial_OptoRawSniff_Standalone() + public void Integration_Serial_OptoRawSniff_Standalone() { Serial_OptoRawSniff(); } @@ -503,7 +522,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat } catch (TimeoutException) { - Assert.Fail("Serial read timeout"); + Assert.Inconclusive("No optical packet was received within 10 seconds. Enable optical test mode before running this sniff integration test."); } } @@ -997,4 +1016,4 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat return true; } } -} \ No newline at end of file +} diff --git a/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs new file mode 100644 index 000000000..60329898e --- /dev/null +++ b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TBF.Rig.RegisterReaders.AllyReader; +using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; +using TBF.Rig.Sequences; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations; + +namespace TBFTests.Rig.Uni.SharedDialogs.SmartMetersCommunication +{ + [TestClass] + public class SmartCommunicationFormTests + { + private static readonly object ProcessDataLock = new object(); + + [TestMethod] + public void ManualForm_PopulatesAvailableFamiliesFromRegisterReaders() + { + RunInSta(() => + { + WithSmartReaders(new ISmartReader[] { CreateGenesisReader(), CreateAllyReader() }, () => + { + using (var form = new SmartCommunicationForm(true)) + { + CollectionAssert.AreEqual( + new[] { "Genesis", "ALLY" }, + SmartCommunicationForm.IdentifyReaderTypes()); + } + }); + }); + } + + [TestMethod] + public void ManualForm_SelectedFamilyPopulatesOnlyMatchingReaderAndCorrection() + { + RunInSta(() => + { + GenesisSmartReader genesis = CreateGenesisReader(); + AllyMeterReader ally = CreateAllyReader(); + WithSmartReaders(new ISmartReader[] { genesis, ally }, () => + { + using (var form = new SmartCommunicationForm(true)) + { + SelectFamily(form, "ALLY"); + Assert.AreEqual(1, form.Heads.Count); + Assert.AreSame(ally, form.Heads[0]); + Assert.IsInstanceOfType(CurrentCorrection(), typeof(AllyCorrections)); + + SelectFamily(form, "Genesis"); + Assert.AreEqual(1, form.Heads.Count); + Assert.AreSame(genesis, form.Heads[0]); + Assert.IsInstanceOfType(CurrentCorrection(), typeof(GenesisCorrections)); + } + }); + }); + } + + [TestMethod] + public void ManualForm_LoadsReaderRowsAndCheckboxTogglesSelectionState() + { + RunInSta(() => + { + AllyMeterReader first = CreateAllyReader(); + AllyMeterReader second = CreateAllyReader(); + WithSmartReaders(new ISmartReader[] { first, second }, () => + { + using (var form = new SmartCommunicationForm(true)) + { + SelectFamily(form, "ALLY"); + form.CkbIndex[0] = 0; + form.CkbIndex[1] = 1; + ICorrections correction = CurrentCorrection(); + correction.Load(form.Labels, form.Counters, form.Messages, form.CheckBoxes, + form.CkbIndex, form.CkbState, form.Heads, form.CheckBoxes.Length, true); + + Assert.AreEqual(2, form.Heads.Count); + Assert.AreSame(first, form.Heads[0]); + Assert.AreSame(second, form.Heads[1]); + Assert.IsTrue(form.CheckBoxes[0].Checked); + Assert.IsTrue(form.CheckBoxes[1].Checked); + Assert.AreEqual("---", form.Messages[0].Text); + Assert.AreEqual("---", form.Messages[1].Text); + + form.CheckBoxes[0].Checked = false; + InvokePrivate(form, "checkBoxImage1_Click", form.CheckBoxes[0], EventArgs.Empty); + Assert.IsFalse(form.CkbState[0]); + + form.CheckBoxes[0].Checked = true; + InvokePrivate(form, "checkBoxImage1_Click", form.CheckBoxes[0], EventArgs.Empty); + Assert.IsTrue(form.CkbState[0]); + Assert.IsTrue((form.GetCheckBoxStates() & 1L) != 0L); + } + }); + }); + } + + [TestMethod] + public void SelectedCorrection_ProvidesCommandsForTheSelectedRegisterReaderFamily() + { + RunInSta(() => + { + WithSmartReaders(new ISmartReader[] { CreateGenesisReader(), CreateAllyReader() }, () => + { + using (var form = new SmartCommunicationForm(true)) + { + SelectFamily(form, "ALLY"); + CollectionAssert.Contains( + CurrentCorrection().GetContextMenu().MenuItems.Cast() + .Select(item => item.Text).ToArray(), + "Read Serial Number"); + + SelectFamily(form, "Genesis"); + CollectionAssert.Contains( + CurrentCorrection().GetContextMenu().MenuItems.Cast() + .Select(item => item.Text).ToArray(), + "Read PCB Number"); + } + }); + }); + } + + private static AllyMeterReader CreateAllyReader() + { + return new AllyMeterReader(new AllyReaderCfg(new TBF.Rig.RegisterReaders.AllyReader.Factory())); + } + + private static GenesisSmartReader CreateGenesisReader() + { + return new GenesisSmartReader(new TBF.Rig.RegisterReaders.GenesisRegReader.GenesisCfg( + new TBF.Rig.RegisterReaders.GenesisRegReader.Factory())); + } + + private static void SelectFamily(SmartCommunicationForm form, string family) + { + SmartCommunicationForm.SelectedTypeReader = family; + InvokePrivate(form, "UpdateHeads"); + } + + private static ICorrections CurrentCorrection() + { + PropertyInfo property = typeof(SmartCommunicationForm).GetProperty( + "Correction", BindingFlags.NonPublic | BindingFlags.Static); + return (ICorrections)property.GetValue(null, null); + } + + private static void InvokePrivate(SmartCommunicationForm form, string methodName, params object[] parameters) + { + MethodInfo method = typeof(SmartCommunicationForm).GetMethod( + methodName, BindingFlags.NonPublic | BindingFlags.Instance); + method.Invoke(form, parameters); + } + + private static void WithSmartReaders(IList readers, Action action) + { + lock (ProcessDataLock) + { + IList previous = ProcessData.SmartHeadsUni; + string previousSelectedType = SmartCommunicationForm.SelectedTypeReader; + try + { + ProcessData.SmartHeadsUni = readers; + SmartCommunicationForm.SelectedTypeReader = null; + action(); + } + finally + { + ProcessData.SmartHeadsUni = previous; + SmartCommunicationForm.SelectedTypeReader = previousSelectedType; + } + } + } + + private static void RunInSta(Action action) + { + Exception failure = null; + Thread thread = new Thread(() => + { + try { action(); } + catch (Exception exception) { failure = exception; } + }); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + if (failure != null) + throw new AssertFailedException(failure.ToString()); + } + } +} diff --git a/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/CorrectionsContractTests.cs b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/CorrectionsContractTests.cs new file mode 100644 index 000000000..6369dfe2a --- /dev/null +++ b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/CorrectionsContractTests.cs @@ -0,0 +1,92 @@ +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TBF.Rig.RegisterReaders.AllyReader; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication; +using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations; +using LegacyIPerlReader = TBF.Rig.RegisterReaders.IPerlReader.implementations.SmartReader; +using AsicIPerlReader = TBF.Rig.RegisterReaders.iPerlASICReader.implementations.SmartReader; + +namespace TBFTests.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations +{ + /// Fast tests of the correction-adapter contract; no COM port is opened. + [TestClass] + public class CorrectionsContractTests + { + [TestMethod] + public void ManualAdapters_ExposeStableFamilyNames() + { + Assert.AreEqual("ALLY", new AllyCorrections(null).TypeIdentificatorName()); + Assert.AreEqual("Genesis", new GenesisCorrections(null).TypeIdentificatorName()); + Assert.AreEqual("iPerl", new IPerlCorrections(null).TypeIdentificatorName()); + Assert.AreEqual("iPerl ASIC", new IPerlASICCorrections(null).TypeIdentificatorName()); + Assert.AreEqual("Poseidon", new PoseidonCorrections(null).TypeIdentificatorName()); + } + + [TestMethod] + public void AllyManualAdapter_ExposesOnlySupportedSafeOperations() + { + var menu = new AllyCorrections(null).GetContextMenu(); + + CollectionAssert.AreEqual( + new[] { "Read Serial Number", "Read Version and Type", "Set RFID mode", "Set NFC mode" }, + menu.MenuItems.Cast().Select(item => item.Text).ToArray()); + } + + [TestMethod] + public void GenesisManualAdapter_ExposesOnlyImplementedOperations() + { + var menu = new GenesisCorrections(null).GetContextMenu(); + + CollectionAssert.AreEqual( + new[] { "Read PCB Number", "Set RFID mode", "Set NFC mode" }, + menu.MenuItems.Cast().Select(item => item.Text).ToArray()); + } + + [TestMethod] + public void ManualOnlyAdapters_RejectTestCommunicationExplicitly() + { + ICorrections correction = new AllyCorrections(null); + CommErr error = CommErr.None; + string result = string.Empty; + + bool handled = correction.WorkerActivity("Read Serial Number", null, null, null, 0, + ref error, ref result, new bool[0], 0, 0); + + Assert.IsFalse(handled); + Assert.AreEqual(CommErr.WrongIPerlType, error); + StringAssert.Contains(result, "test communication is not implemented"); + } + + [TestMethod] + public void IPerlCorrection_DisabledReaderStopsBusinessActivityBeforeCommunication() + { + ISmartReader reader = new LegacyIPerlReader( + new TBF.Rig.RegisterReaders.IPerlReader.Factory().DefaultConfig()) { Disabled = true }; + CommErr error = CommErr.None; + string result = string.Empty; + + bool handled = new IPerlCorrections(null).WorkerActivity("Unknown activity", reader, null, null, 0, + ref error, ref result, new[] { true }, 0, 0); + + Assert.IsTrue(handled); + Assert.AreEqual(CommErr.HeadDisabledByUser, error); + } + + [TestMethod] + public void IPerlAsicCorrection_DisabledReaderStopsBusinessActivityBeforeCommunication() + { + ISmartReader reader = new AsicIPerlReader( + new TBF.Rig.RegisterReaders.iPerlASICReader.Factory().DefaultConfig()) { Disabled = true }; + CommErr error = CommErr.None; + string result = string.Empty; + + bool handled = new IPerlASICCorrections(null).WorkerActivity("Unknown activity", reader, null, null, 0, + ref error, ref result, new[] { true }, 0, 0); + + Assert.IsTrue(handled); + Assert.AreEqual(CommErr.HeadDisabledByUser, error); + } + } +} diff --git a/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/RegisterReaderCorrectionWiringTests.cs b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/RegisterReaderCorrectionWiringTests.cs new file mode 100644 index 000000000..12a635be9 --- /dev/null +++ b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/implementations/RegisterReaderCorrectionWiringTests.cs @@ -0,0 +1,78 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TBF.Rig.Generic; +using TBF.Rig.GenericDevices; +using TBF.Rig.RegisterReaders.AllyReader; +using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations; +using IPerlSmartReader = TBF.Rig.RegisterReaders.IPerlReader.implementations.SmartReader; +using IPerlAsicSmartReader = TBF.Rig.RegisterReaders.iPerlASICReader.implementations.SmartReader; +using PoseidonSmartReader = TBF.Rig.RegisterReaders.PoseidonReader.SmartReader; + +namespace TBFTests.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations +{ + /// Reader-to-adapter wiring tests. They use real reader classes but no hardware transport. + [TestClass] + public class RegisterReaderCorrectionWiringTests + { + [TestMethod] + public void AllyReader_IsRecognizedOnlyByAllyCorrection() + { + AllyMeterReader reader = new AllyMeterReader(new AllyReaderCfg(new TBF.Rig.RegisterReaders.AllyReader.Factory())); + + Assert.IsInstanceOfType(reader, typeof(ISmartReader)); + Assert.IsInstanceOfType(reader, typeof(IRegReaderSmart)); + Assert.IsTrue(new AllyCorrections(null).IsFamilyOfSmartReader(reader)); + Assert.IsFalse(new GenesisCorrections(null).IsFamilyOfSmartReader(reader)); + Assert.IsFalse(new IPerlCorrections(null).IsFamilyOfSmartReader(reader)); + Assert.IsFalse(new IPerlASICCorrections(null).IsFamilyOfSmartReader(reader)); + } + + [TestMethod] + public void GenesisReader_IsRecognizedOnlyByGenesisCorrection() + { + GenesisSmartReader reader = new GenesisSmartReader( + new TBF.Rig.RegisterReaders.GenesisRegReader.GenesisCfg(new TBF.Rig.RegisterReaders.GenesisRegReader.Factory())); + + Assert.IsInstanceOfType(reader, typeof(ISmartReader)); + Assert.IsInstanceOfType(reader, typeof(IRegReaderSmart)); + Assert.IsTrue(new GenesisCorrections(null).IsFamilyOfSmartReader(reader)); + Assert.IsFalse(new AllyCorrections(null).IsFamilyOfSmartReader(reader)); + Assert.IsFalse(new IPerlCorrections(null).IsFamilyOfSmartReader(reader)); + Assert.IsFalse(new IPerlASICCorrections(null).IsFamilyOfSmartReader(reader)); + } + + [TestMethod] + public void ExistingSmartReaderFamilies_AreBoundToTheirOwnCorrections() + { + ISmartReader oldIperl = new IPerlSmartReader( + new TBF.Rig.RegisterReaders.IPerlReader.Factory().DefaultConfig()); + ISmartReader asic = new IPerlAsicSmartReader( + new TBF.Rig.RegisterReaders.iPerlASICReader.Factory().DefaultConfig()); + ISmartReader poseidon = new PoseidonSmartReader( + new TBF.Rig.RegisterReaders.PoseidonReader.Factory().DefaultConfig()); + + Assert.IsTrue(new IPerlCorrections(null).IsFamilyOfSmartReader(oldIperl)); + Assert.IsTrue(new IPerlASICCorrections(null).IsFamilyOfSmartReader(asic)); + Assert.IsTrue(new PoseidonCorrections(null).IsFamilyOfSmartReader(poseidon)); + + Assert.IsFalse(new IPerlCorrections(null).IsFamilyOfSmartReader(asic)); + Assert.IsFalse(new IPerlASICCorrections(null).IsFamilyOfSmartReader(oldIperl)); + Assert.IsFalse(new PoseidonCorrections(null).IsFamilyOfSmartReader(oldIperl)); + } + + [TestMethod] + public void SmartReaderContract_PreservesManualSelectionStateWithoutTransport() + { + ISmartReader reader = new AllyMeterReader(new AllyReaderCfg(new TBF.Rig.RegisterReaders.AllyReader.Factory())); + + reader.Disabled = true; + reader.SerialNr = "ALLY-TEST"; + reader.SetCommunicationInterface("RFID"); + + Assert.IsTrue(reader.Disabled); + Assert.AreEqual("ALLY-TEST", reader.SerialNr); + Assert.AreEqual("Touch-Read", reader.CommInterface); + } + } +} diff --git a/TBFTests/TBFTests.csproj b/TBFTests/TBFTests.csproj index cbefac8ed..987c2d75f 100644 --- a/TBFTests/TBFTests.csproj +++ b/TBFTests/TBFTests.csproj @@ -159,6 +159,9 @@ + + +