From e4d4d75642a042f915adbaa314931d368b72b16c Mon Sep 17 00:00:00 2001 From: Michal Buzik Date: Tue, 1 Sep 2026 09:00:12 +0200 Subject: [PATCH] feat(ally): add ASIC-style designer configuration and manual test controls --- TBF/LocalSettings.cs | 6 ++ .../DataEntry/Uni/RegisterReaderSelection.cs | 37 ++++++++-- .../AllyReader/AllyReaderCfg.cs | 2 +- .../AllyReader/AllyReaderCfgCtrl.Designer.cs | 51 +++++++++++++ .../AllyReader/AllyReaderCfgCtrl.cs | 74 +++++++++++++++++++ .../AllyReader/AllyReaderCfgCtrl.resx | 7 ++ .../AllyReaderManualTestCtrl.Designer.cs | 22 ++++++ .../AllyReader/AllyReaderManualTestCtrl.cs | 44 +++++++++++ .../AllyReader/AllyReaderManualTestCtrl.resx | 1 + TBF/Rig/StateMachine.cs | 6 ++ .../SmartCommunicationForm.cs | 41 ++++++++-- TBF/TBF.csproj | 20 +++++ .../Uni/RegisterReaderSelectionTests.cs | 34 ++++++++- .../SmartCommunicationFormTests.cs | 35 +++++++++ 14 files changed, 364 insertions(+), 16 deletions(-) create mode 100644 TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.Designer.cs create mode 100644 TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.cs create mode 100644 TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.resx create mode 100644 TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.Designer.cs create mode 100644 TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.cs create mode 100644 TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.resx diff --git a/TBF/LocalSettings.cs b/TBF/LocalSettings.cs index d114867bb..38ec2374b 100644 --- a/TBF/LocalSettings.cs +++ b/TBF/LocalSettings.cs @@ -227,6 +227,12 @@ namespace TBF /// public long OptoHeadsEnabled; /// Bit field with opto-head enabled states, used by iPerlCommunicationForm and S640CommForm + /// + /// Smart-meter reader selection used by SmartCommunicationForm and DataEntry.UNI. + /// OptoHeadsEnabled remains for the legacy iPerl and S640 dialogs. + /// + public SmartReaderSelectionSettings SmartReaderSelections; + /// Serial numbers public string[] LastSNTexts; [XmlIgnore] diff --git a/TBF/Rig/DataEntry/Uni/RegisterReaderSelection.cs b/TBF/Rig/DataEntry/Uni/RegisterReaderSelection.cs index 6099cc574..1ff536526 100644 --- a/TBF/Rig/DataEntry/Uni/RegisterReaderSelection.cs +++ b/TBF/Rig/DataEntry/Uni/RegisterReaderSelection.cs @@ -4,6 +4,8 @@ using System.Linq; using log4net; using Results.Entities; using TBF.Rig.GenericDevices; +using TBF.Rig.Sequences; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication; namespace TBF.Rig.DataEntry.Uni { @@ -23,21 +25,22 @@ namespace TBF.Rig.DataEntry.Uni ? new IRegReader[0] : readers.ToArray(); long enabledHeads = Program.LocalSettings.OptoHeadsEnabled; + SmartReaderSelection.EnsureConfiguredReaders(Program.LocalSettings, ProcessData.SmartHeadsUni); IRegReader[] selected = allSlots - .Where(reader => IsSelectedForDataEntry(reader, waterMeters, enabledHeads)) + .Where(reader => IsSelectedForDataEntry(reader, waterMeters, Program.LocalSettings, enabledHeads)) .ToArray(); string selectedPositions = string.Join(",", selected.Select(reader => reader.Position)); string skippedPositions = string.Join(",", allSlots .Where(reader => reader != null && - !IsSelectedForDataEntry(reader, waterMeters, enabledHeads)) + !IsSelectedForDataEntry(reader, waterMeters, Program.LocalSettings, enabledHeads)) .Select(reader => reader.Position)); log.DebugFormat( - "DATA_ENTRY_READER_FILTER Operation={0}, OptoHeadsEnabled=0x{1:X12}, " + + "DATA_ENTRY_READER_FILTER Operation={0}, LegacyOptoHeadsEnabled=0x{1:X12}, " + "Slots={2}, Readers={3}, Selected={4}, SelectedPositions=[{5}], " + - "SkippedPositions=[{6}], SelectionRule=SmartMeterMaskOrPrecheckedWaterMeter", + "SkippedPositions=[{6}], SelectionRule=SmartReaderFamilySelectionOrPrecheckedWaterMeter", operation, enabledHeads, allSlots.Length, @@ -56,8 +59,8 @@ namespace TBF.Rig.DataEntry.Uni reader.GetType().Name, reader.Position, reader.DebugLevel, - IsSelectedForDataEntry(reader, waterMeters, enabledHeads), - GetSelectionDetails(reader, waterMeters, enabledHeads)); + IsSelectedForDataEntry(reader, waterMeters, Program.LocalSettings, enabledHeads), + GetSelectionDetails(reader, waterMeters, Program.LocalSettings, enabledHeads)); } return selected; @@ -67,6 +70,15 @@ namespace TBF.Rig.DataEntry.Uni IRegReader reader, IList waterMeters, long enabledHeads) + { + return IsSelectedForDataEntry(reader, waterMeters, null, enabledHeads); + } + + internal static bool IsSelectedForDataEntry( + IRegReader reader, + IList waterMeters, + LocalSettings settings, + long enabledHeads) { if (reader == null) return false; @@ -87,6 +99,9 @@ namespace TBF.Rig.DataEntry.Uni if (reader is ISmartMeterReader) { + if (settings != null && settings.SmartReaderSelections != null) + return SmartReaderSelection.IsEnabled(settings, reader); + int position0 = reader.Position - 1; return position0 >= 0 && position0 < 63 && (enabledHeads & (1L << position0)) != 0; @@ -115,6 +130,7 @@ namespace TBF.Rig.DataEntry.Uni private static string GetSelectionDetails( IRegReader reader, IList waterMeters, + LocalSettings settings, long enabledHeads) { #if IPERL @@ -136,6 +152,15 @@ namespace TBF.Rig.DataEntry.Uni #endif if (reader is ISmartMeterReader) { + if (settings != null && settings.SmartReaderSelections != null) + { + return string.Format( + ", SelectionSource=SmartReaderFamily, Family={0}, ReaderKey={1}, Enabled={2}", + SmartReaderSelection.GetFamilyKey(reader), + SmartReaderSelection.GetReaderKey(reader), + SmartReaderSelection.IsEnabled(settings, reader)); + } + int position0 = reader.Position - 1; bool maskBit = position0 >= 0 && position0 < 63 && (enabledHeads & (1L << position0)) != 0; diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfg.cs b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfg.cs index de2a8172d..b8a6c78bc 100644 --- a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfg.cs +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfg.cs @@ -37,7 +37,7 @@ namespace TBF.Rig.RegisterReaders.AllyReader public IComponentCfgCtrl GetControl(IList cmpntEntities) { - return new Configs.ParamsProvider.ComponentCfgCtrl(this, null); + return new AllyReaderCfgCtrl(); } public string ComponentName { get { return Name; } } diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.Designer.cs b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.Designer.cs new file mode 100644 index 000000000..635400b0e --- /dev/null +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.Designer.cs @@ -0,0 +1,51 @@ +namespace TBF.Rig.RegisterReaders.AllyReader +{ + partial class AllyReaderCfgCtrl + { + private System.ComponentModel.IContainer components = null; + protected override void Dispose(bool disposing) { if (disposing && components != null) components.Dispose(); base.Dispose(disposing); } + private void InitializeComponent() + { + tabControl1 = new System.Windows.Forms.TabControl(); tabPage1 = new System.Windows.Forms.TabPage(); tabPage2 = new System.Windows.Forms.TabPage(); + classNameLabel = new System.Windows.Forms.Label(); nameLabel = new System.Windows.Forms.Label(); nameTextBox = new System.Windows.Forms.TextBox(); + muxBoardNrLabel = new System.Windows.Forms.Label(); muxBoardNrTextBox = new System.Windows.Forms.TextBox(); label3 = new System.Windows.Forms.Label(); + groupLabel = new System.Windows.Forms.Label(); groupTextBox = new System.Windows.Forms.TextBox(); label4 = new System.Windows.Forms.Label(); + optoDataGroupBox = new System.Windows.Forms.GroupBox(); radioButton1 = new System.Windows.Forms.RadioButton(); radioButton2 = new System.Windows.Forms.RadioButton(); + ipAddressLabel = new System.Windows.Forms.Label(); ipAddressTextBox = new System.Windows.Forms.TextBox(); tcpipPortLabel = new System.Windows.Forms.Label(); tcpipPortTextBox = new System.Windows.Forms.TextBox(); optoSerialPortLabel = new System.Windows.Forms.Label(); optoSerialPortTextBox = new System.Windows.Forms.TextBox(); + groupBox1 = new System.Windows.Forms.GroupBox(); label1 = new System.Windows.Forms.Label(); comboBoxCommunicationInterface = new System.Windows.Forms.ComboBox(); rfidSerialPortNrLabel = new System.Windows.Forms.Label(); rfidPortNrTextBox = new System.Windows.Forms.TextBox(); + groupBox2 = new System.Windows.Forms.GroupBox(); label2 = new System.Windows.Forms.Label(); headPortNrTextBox = new System.Windows.Forms.TextBox(); + tabControl1.SuspendLayout(); tabPage1.SuspendLayout(); optoDataGroupBox.SuspendLayout(); groupBox1.SuspendLayout(); groupBox2.SuspendLayout(); SuspendLayout(); + tabControl1.Controls.Add(tabPage1); tabControl1.Controls.Add(tabPage2); tabControl1.Location = new System.Drawing.Point(3,3); tabControl1.Name="tabControl1"; tabControl1.SelectedIndex=0; tabControl1.Size=new System.Drawing.Size(611,432); + tabPage1.Controls.Add(groupBox2); tabPage1.Controls.Add(label4); tabPage1.Controls.Add(label3); tabPage1.Controls.Add(groupBox1); tabPage1.Controls.Add(optoDataGroupBox); tabPage1.Controls.Add(groupTextBox); tabPage1.Controls.Add(groupLabel); tabPage1.Controls.Add(muxBoardNrTextBox); tabPage1.Controls.Add(muxBoardNrLabel); tabPage1.Controls.Add(nameTextBox); tabPage1.Controls.Add(nameLabel); tabPage1.Controls.Add(classNameLabel); tabPage1.Location=new System.Drawing.Point(4,25); tabPage1.Name="tabPage1"; tabPage1.Padding=new System.Windows.Forms.Padding(3); tabPage1.Size=new System.Drawing.Size(603,403); tabPage1.Text="Config"; tabPage1.UseVisualStyleBackColor=true; + classNameLabel.AutoSize=true; classNameLabel.Location=new System.Drawing.Point(149,11); classNameLabel.Name="classNameLabel"; classNameLabel.Text="ClassName"; + nameLabel.AutoSize=true; nameLabel.Location=new System.Drawing.Point(6,44); nameLabel.Name="nameLabel"; nameLabel.Text="Name"; + nameTextBox.Enabled=false; nameTextBox.Location=new System.Drawing.Point(153,40); nameTextBox.Name="nameTextBox"; nameTextBox.Size=new System.Drawing.Size(160,22); + muxBoardNrLabel.AutoSize=true; muxBoardNrLabel.Location=new System.Drawing.Point(6,72); muxBoardNrLabel.Name="muxBoardNrLabel"; muxBoardNrLabel.Text="Group 1 (mux. board)"; + muxBoardNrTextBox.Enabled=false; muxBoardNrTextBox.Location=new System.Drawing.Point(153,69); muxBoardNrTextBox.Name="muxBoardNrTextBox"; muxBoardNrTextBox.Size=new System.Drawing.Size(44,22); + label3.AutoSize=true; label3.Location=new System.Drawing.Point(208,72); label3.Name="label3"; label3.Text="1 .. 4"; + groupLabel.AutoSize=true; groupLabel.Location=new System.Drawing.Point(6,101); groupLabel.Name="groupLabel"; groupLabel.Text="Group 2"; + groupTextBox.Enabled=false; groupTextBox.Location=new System.Drawing.Point(153,97); groupTextBox.Name="groupTextBox"; groupTextBox.Size=new System.Drawing.Size(44,22); + label4.AutoSize=true; label4.Location=new System.Drawing.Point(208,101); label4.Name="label4"; label4.Text="1 .. 10"; + optoDataGroupBox.Controls.Add(tcpipPortLabel); optoDataGroupBox.Controls.Add(tcpipPortTextBox); optoDataGroupBox.Controls.Add(ipAddressLabel); optoDataGroupBox.Controls.Add(ipAddressTextBox); optoDataGroupBox.Controls.Add(radioButton1); optoDataGroupBox.Controls.Add(radioButton2); optoDataGroupBox.Controls.Add(optoSerialPortLabel); optoDataGroupBox.Controls.Add(optoSerialPortTextBox); optoDataGroupBox.Location=new System.Drawing.Point(10,131); optoDataGroupBox.Name="optoDataGroupBox"; optoDataGroupBox.Size=new System.Drawing.Size(552,119); optoDataGroupBox.Text="Opto-data"; + radioButton1.AutoSize=true; radioButton1.Checked=true; radioButton1.Enabled=false; radioButton1.Location=new System.Drawing.Point(29,23); radioButton1.Name="radioButton1"; radioButton1.Text="Use TCP/IP"; + radioButton2.AutoSize=true; radioButton2.Enabled=false; radioButton2.Location=new System.Drawing.Point(312,23); radioButton2.Name="radioButton2"; radioButton2.Text="Use serial port"; + ipAddressLabel.AutoSize=true; ipAddressLabel.Location=new System.Drawing.Point(41,59); ipAddressLabel.Name="ipAddressLabel"; ipAddressLabel.Text="IP address. :"; + ipAddressTextBox.Enabled=false; ipAddressTextBox.Location=new System.Drawing.Point(143,55); ipAddressTextBox.Name="ipAddressTextBox"; ipAddressTextBox.Size=new System.Drawing.Size(129,22); + tcpipPortLabel.AutoSize=true; tcpipPortLabel.Location=new System.Drawing.Point(41,87); tcpipPortLabel.Name="tcpipPortLabel"; tcpipPortLabel.Text="Port nr.:"; + tcpipPortTextBox.Enabled=false; tcpipPortTextBox.Location=new System.Drawing.Point(143,84); tcpipPortTextBox.Name="tcpipPortTextBox"; tcpipPortTextBox.Size=new System.Drawing.Size(51,22); + optoSerialPortLabel.AutoSize=true; optoSerialPortLabel.Location=new System.Drawing.Point(321,55); optoSerialPortLabel.Name="optoSerialPortLabel"; optoSerialPortLabel.Text="Serial port nr.:"; + optoSerialPortTextBox.Enabled=false; optoSerialPortTextBox.Location=new System.Drawing.Point(439,52); optoSerialPortTextBox.Name="optoSerialPortTextBox"; optoSerialPortTextBox.Size=new System.Drawing.Size(44,22); + groupBox1.Controls.Add(comboBoxCommunicationInterface); groupBox1.Controls.Add(label1); groupBox1.Controls.Add(rfidPortNrTextBox); groupBox1.Controls.Add(rfidSerialPortNrLabel); groupBox1.Location=new System.Drawing.Point(10,259); groupBox1.Name="groupBox1"; groupBox1.Size=new System.Drawing.Size(552,68); groupBox1.Text="RFID / NFC communication (in case mux. board is not used)"; + label1.AutoSize=true; label1.Location=new System.Drawing.Point(41,30); label1.Name="label1"; label1.Text="Communication Interface"; + comboBoxCommunicationInterface.Enabled=false; comboBoxCommunicationInterface.FormattingEnabled=true; comboBoxCommunicationInterface.Location=new System.Drawing.Point(201,27); comboBoxCommunicationInterface.Name="comboBoxCommunicationInterface"; comboBoxCommunicationInterface.Size=new System.Drawing.Size(71,24); + rfidSerialPortNrLabel.AutoSize=true; rfidSerialPortNrLabel.Location=new System.Drawing.Point(321,30); rfidSerialPortNrLabel.Name="rfidSerialPortNrLabel"; rfidSerialPortNrLabel.Text="Serial port nr.:"; + rfidPortNrTextBox.Enabled=false; rfidPortNrTextBox.Location=new System.Drawing.Point(439,26); rfidPortNrTextBox.Name="rfidPortNrTextBox"; rfidPortNrTextBox.Size=new System.Drawing.Size(44,22); + groupBox2.Controls.Add(headPortNrTextBox); groupBox2.Controls.Add(label2); groupBox2.Location=new System.Drawing.Point(10,335); groupBox2.Name="groupBox2"; groupBox2.Size=new System.Drawing.Size(552,50); groupBox2.Text="Head Communication"; + label2.AutoSize=true; label2.Location=new System.Drawing.Point(321,18); label2.Name="label2"; label2.Text="Serial port nr.:"; + headPortNrTextBox.Enabled=false; headPortNrTextBox.Location=new System.Drawing.Point(439,15); headPortNrTextBox.Name="headPortNrTextBox"; headPortNrTextBox.Size=new System.Drawing.Size(44,22); + tabPage2.Location=new System.Drawing.Point(4,25); tabPage2.Name="tabPage2"; tabPage2.Padding=new System.Windows.Forms.Padding(3); tabPage2.Size=new System.Drawing.Size(603,403); tabPage2.Text="Test"; tabPage2.UseVisualStyleBackColor=true; + AutoScaleDimensions=new System.Drawing.SizeF(8F,16F); AutoScaleMode=System.Windows.Forms.AutoScaleMode.Font; Controls.Add(tabControl1); Margin=new System.Windows.Forms.Padding(4); Name="AllyReaderCfgCtrl"; Size=new System.Drawing.Size(617,438); tabControl1.ResumeLayout(false); tabPage1.ResumeLayout(false); tabPage1.PerformLayout(); optoDataGroupBox.ResumeLayout(false); optoDataGroupBox.PerformLayout(); groupBox1.ResumeLayout(false); groupBox1.PerformLayout(); groupBox2.ResumeLayout(false); groupBox2.PerformLayout(); ResumeLayout(false); + } + private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage2; private System.Windows.Forms.Label classNameLabel,nameLabel,muxBoardNrLabel,groupLabel,label1,label2,label3,label4,ipAddressLabel,tcpipPortLabel,optoSerialPortLabel,rfidSerialPortNrLabel; private System.Windows.Forms.TextBox nameTextBox,muxBoardNrTextBox,groupTextBox,ipAddressTextBox,tcpipPortTextBox,optoSerialPortTextBox,rfidPortNrTextBox,headPortNrTextBox; private System.Windows.Forms.GroupBox optoDataGroupBox,groupBox1,groupBox2; private System.Windows.Forms.RadioButton radioButton1,radioButton2; private System.Windows.Forms.ComboBox comboBoxCommunicationInterface; + } +} diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.cs b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.cs new file mode 100644 index 000000000..0c586b416 --- /dev/null +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.cs @@ -0,0 +1,74 @@ +using System; +using System.Globalization; +using System.Windows.Forms; +using Common; +using TBF.Rig.Generic; + +namespace TBF.Rig.RegisterReaders.AllyReader +{ + public partial class AllyReaderCfgCtrl : UserControl, IComponentCfgCtrl + { + private AllyReaderCfg config; + private readonly AllyReaderManualTestCtrl manualTestControl; + + public AllyReaderCfgCtrl() + { + InitializeComponent(); + manualTestControl = new AllyReaderManualTestCtrl { Dock = DockStyle.Fill }; + tabPage2.Controls.Add(manualTestControl); + tabPage1.Text = "Settings"; + tabPage2.Text = "Manual test"; + groupBox1.Text = "Touch-Read communication"; + groupBox2.Text = "Touch-Read settings"; + muxBoardNrLabel.Text = "Configured meter size"; + groupLabel.Text = "Meter pulses/liter"; + label3.Text = label4.Text = string.Empty; + ipAddressLabel.Text = "TCP/IP"; + ipAddressTextBox.Text = "Not supported by ALLY"; + tcpipPortLabel.Text = "Opto baud rate:"; + label2.Text = "Baud rate:"; + comboBoxCommunicationInterface.Items.Clear(); + comboBoxCommunicationInterface.Items.Add("Touch-Read"); + comboBoxCommunicationInterface.SelectedIndex = 0; + radioButton1.Checked = false; + radioButton2.Checked = true; + } + + public IComponentCfg Config { get { return config; } set { config = value as AllyReaderCfg; Redraw(); manualTestControl.Config = config; } } + public bool ShowMore { get { return false; } } + public void Unlock() { nameTextBox.Enabled = muxBoardNrTextBox.Enabled = groupTextBox.Enabled = optoSerialPortTextBox.Enabled = tcpipPortTextBox.Enabled = rfidPortNrTextBox.Enabled = headPortNrTextBox.Enabled = true; } + + public CfgUpdateFlags VerifyCfg(ref string message) + { + int n; double pulses; AllyMeterSize size; + if (config == null || string.IsNullOrWhiteSpace(nameTextBox.Text)) return Invalid(ref message, "Name"); + if (!Enum.TryParse(muxBoardNrTextBox.Text, out size)) return Invalid(ref message, "Configured meter size"); + if (!double.TryParse(groupTextBox.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out pulses) || pulses <= 0) return Invalid(ref message, "Meter pulses/liter"); + if (!int.TryParse(optoSerialPortTextBox.Text, out n) || n <= 0) return Invalid(ref message, "Optical serial port nr."); + if (!int.TryParse(tcpipPortTextBox.Text, out n) || n <= 0) return Invalid(ref message, "Optical baud rate"); + if (!int.TryParse(rfidPortNrTextBox.Text, out n) || n <= 0) return Invalid(ref message, "Touch-Read serial port nr."); + if (!int.TryParse(headPortNrTextBox.Text, out n) || n <= 0) return Invalid(ref message, "Touch-Read baud rate"); + return CfgUpdateFlags.None; + } + public CfgUpdateFlags UpdateCfg() + { + if (config == null) return CfgUpdateFlags.Error; + config.Name = nameTextBox.Text; + config.ConfiguredMeterSize = (AllyMeterSize)Enum.Parse(typeof(AllyMeterSize), muxBoardNrTextBox.Text); + config.MeterPulsesPerLiter = double.Parse(groupTextBox.Text, CultureInfo.InvariantCulture); + config.OptoComPortNr = int.Parse(optoSerialPortTextBox.Text); config.OptoBaudRate = int.Parse(tcpipPortTextBox.Text); + config.CommandComPortNr = int.Parse(rfidPortNrTextBox.Text); config.CommandBaudRate = int.Parse(headPortNrTextBox.Text); + return CfgUpdateFlags.RestartRqrd; + } + public void Closing() { manualTestControl.StopOpticalStream(); } + private void Redraw() + { + if (config == null) return; + classNameLabel.Text = config.Factory.ClassName; nameTextBox.Text = config.Name; + muxBoardNrTextBox.Text = config.ConfiguredMeterSize.ToString(); groupTextBox.Text = config.MeterPulsesPerLiter.ToString(CultureInfo.InvariantCulture); + optoSerialPortTextBox.Text = config.OptoComPortNr.ToString(); tcpipPortTextBox.Text = config.OptoBaudRate.ToString(); + rfidPortNrTextBox.Text = config.CommandComPortNr.ToString(); headPortNrTextBox.Text = config.CommandBaudRate.ToString(); + } + private static CfgUpdateFlags Invalid(ref string message, string text) { message += Environment.NewLine + "'" + text + "' is not valid"; return CfgUpdateFlags.Error; } + } +} diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.resx b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.resx new file mode 100644 index 000000000..13913bdda --- /dev/null +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderCfgCtrl.resx @@ -0,0 +1,7 @@ + + + text/microsoft-resx + 2.0 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.Designer.cs b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.Designer.cs new file mode 100644 index 000000000..106bc24a8 --- /dev/null +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.Designer.cs @@ -0,0 +1,22 @@ +namespace TBF.Rig.RegisterReaders.AllyReader +{ + partial class AllyReaderManualTestCtrl + { + private System.ComponentModel.IContainer components = null; + protected override void Dispose(bool disposing) { if(disposing && components != null) components.Dispose(); base.Dispose(disposing); } + private void InitializeComponent() + { + optoTestGroupBox=new System.Windows.Forms.GroupBox(); optoListBox=new System.Windows.Forms.ListBox(); RfidTestGroupBox=new System.Windows.Forms.GroupBox(); rfidOutputListBox=new System.Windows.Forms.ListBox(); label2=new System.Windows.Forms.Label(); rfidCommandComboBox=new System.Windows.Forms.ComboBox(); commandTestButton=new System.Windows.Forms.Button(); + optoTestGroupBox.SuspendLayout(); RfidTestGroupBox.SuspendLayout(); SuspendLayout(); + optoTestGroupBox.Controls.Add(optoListBox); optoTestGroupBox.Location=new System.Drawing.Point(5,4); optoTestGroupBox.Name="optoTestGroupBox"; optoTestGroupBox.Size=new System.Drawing.Size(591,161); optoTestGroupBox.Text="Opto-data"; + optoListBox.FormattingEnabled=true; optoListBox.ItemHeight=16; optoListBox.Location=new System.Drawing.Point(7,22); optoListBox.Name="optoListBox"; optoListBox.Size=new System.Drawing.Size(573,132); + RfidTestGroupBox.Controls.Add(rfidOutputListBox); RfidTestGroupBox.Controls.Add(label2); RfidTestGroupBox.Controls.Add(rfidCommandComboBox); RfidTestGroupBox.Controls.Add(commandTestButton); RfidTestGroupBox.Location=new System.Drawing.Point(5,171); RfidTestGroupBox.Name="RfidTestGroupBox"; RfidTestGroupBox.Size=new System.Drawing.Size(591,224); RfidTestGroupBox.Text="RFID / NFC data"; + rfidOutputListBox.FormattingEnabled=true; rfidOutputListBox.ItemHeight=16; rfidOutputListBox.Location=new System.Drawing.Point(5,54); rfidOutputListBox.Name="rfidOutputListBox"; rfidOutputListBox.SelectionMode=System.Windows.Forms.SelectionMode.None; rfidOutputListBox.Size=new System.Drawing.Size(575,164); + label2.AutoSize=true; label2.Location=new System.Drawing.Point(2,25); label2.Name="label2"; label2.Text="Command"; + rfidCommandComboBox.FormattingEnabled=true; rfidCommandComboBox.Location=new System.Drawing.Point(86,19); rfidCommandComboBox.Name="rfidCommandComboBox"; rfidCommandComboBox.Size=new System.Drawing.Size(341,24); + commandTestButton.Location=new System.Drawing.Point(449,19); commandTestButton.Name="commandTestButton"; commandTestButton.Size=new System.Drawing.Size(126,24); commandTestButton.Text="Send command"; commandTestButton.UseVisualStyleBackColor=true; commandTestButton.MouseClick += new System.Windows.Forms.MouseEventHandler(CommandTestButtonClick); + AutoScaleDimensions=new System.Drawing.SizeF(8F,16F); AutoScaleMode=System.Windows.Forms.AutoScaleMode.Font; Controls.Add(optoTestGroupBox); Controls.Add(RfidTestGroupBox); Name="AllyReaderManualTestCtrl"; Size=new System.Drawing.Size(611,432); optoTestGroupBox.ResumeLayout(false); RfidTestGroupBox.ResumeLayout(false); RfidTestGroupBox.PerformLayout(); ResumeLayout(false); + } + private System.Windows.Forms.GroupBox optoTestGroupBox,RfidTestGroupBox; private System.Windows.Forms.ListBox optoListBox,rfidOutputListBox; private System.Windows.Forms.Label label2; private System.Windows.Forms.ComboBox rfidCommandComboBox; private System.Windows.Forms.Button commandTestButton; + } +} diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.cs b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.cs new file mode 100644 index 000000000..ac0123258 --- /dev/null +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Windows.Forms; +using TBF.Rig.Sequences; + +namespace TBF.Rig.RegisterReaders.AllyReader +{ + public partial class AllyReaderManualTestCtrl : UserControl + { + private const int CommandTimeoutMs = 5000; + private readonly Timer opticalPollTimer; + private AllyReaderCfg config; + + public AllyReaderManualTestCtrl() + { + InitializeComponent(); + rfidCommandComboBox.Items.AddRange(new object[] { "Read serial number", "Read version and type", "Set RFID mode", "Set NFC mode", "Read optical data", "Start optical stream", "Stop optical stream" }); + rfidCommandComboBox.SelectedIndex = 0; + opticalPollTimer = new Timer { Interval = 250 }; + opticalPollTimer.Tick += OpticalPollTimer_Tick; + } + + public AllyReaderCfg Config { set { config = value; } } + public void StopOpticalStream() { opticalPollTimer.Stop(); AllyMeterReader r=FindReader(); if(r!=null) try { r.StopDataStreamProcessing(); } catch(Exception e) { AddOutput(e.Message); } } + private void CommandTestButtonClick(object sender, MouseEventArgs e) + { + AllyMeterReader r = FindReader(); string cmd = rfidCommandComboBox.SelectedItem as string; + if (r == null) { AddOutput("Configured ALLY reader is not initialized on this bench."); return; } + try { + if (cmd == "Start optical stream") { r.StartDataStreamProcessing(); opticalPollTimer.Start(); AddOutput("Started"); } + else if (cmd == "Stop optical stream") { StopOpticalStream(); AddOutput("Stopped"); } + else if (cmd == "Read optical data") AddOpto(r.ReadOptoData()); + else if (cmd == "Read serial number") AddOutput(r.ReadSerialNumber(CommandTimeoutMs)); + else if (cmd == "Read version and type") AddOutput(r.ReadVersionAndType(CommandTimeoutMs).ToString()); + else if (cmd == "Set RFID mode") { r.SetRfidInterface(); AddOutput("OK"); } + else if (cmd == "Set NFC mode") { r.SetNfcInterface(); AddOutput("OK"); } + } catch(Exception x) { AddOutput("Error: " + x.Message); } + } + private void OpticalPollTimer_Tick(object sender, EventArgs e) { AllyMeterReader r=FindReader(); if(r==null) { opticalPollTimer.Stop(); return; } try { AddOpto(r.ReadOptoData()); } catch(Exception x) { AddOpto("Error: " + x.Message); } } + private AllyMeterReader FindReader() { return config == null || ProcessData.SmartHeadsUni == null ? null : ProcessData.SmartHeadsUni.OfType().FirstOrDefault(x => x.Name == config.Name); } + private void AddOpto(string text) { if(!string.IsNullOrEmpty(text)) optoListBox.Items.Insert(0,text); } + private void AddOutput(string text) { rfidOutputListBox.Items.Insert(0,text ?? string.Empty); } + } +} diff --git a/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.resx b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.resx new file mode 100644 index 000000000..b7056c456 --- /dev/null +++ b/TBF/Rig/RegisterReaders/AllyReader/AllyReaderManualTestCtrl.resx @@ -0,0 +1 @@ +text/microsoft-resx2.0System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/TBF/Rig/StateMachine.cs b/TBF/Rig/StateMachine.cs index 685b42a8c..5dd21fab0 100644 --- a/TBF/Rig/StateMachine.cs +++ b/TBF/Rig/StateMachine.cs @@ -15,6 +15,7 @@ using TBF.Rig.Sequences; using Dirichlet.Numerics; using TBF.Resources; using TBF.Rig.RegisterReaders.iPerlReaderUNI; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication; using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common; namespace TBF.Rig @@ -313,6 +314,11 @@ namespace TBF.Rig cmpnt.StartChangeHandler(); /// Start handling parameter change events } + // Keep the persisted manual/DataEntry selection aligned with the + // actual smart readers configured on this bench. This does not + // create placeholder positions for other reader families. + SmartReaderSelection.EnsureConfiguredReaders(Program.LocalSettings, ProcessData.SmartHeadsUni); + /// Pre-initialize the control board (= buffer the arguments ctrlBrdComponent, tankCapacities) if (ControlBoardMain == null) { diff --git a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs index 1130ce621..b29b3f795 100644 --- a/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs +++ b/TBF/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationForm.cs @@ -681,13 +681,14 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication log.Warn("SmartCommunicationForm loaded before LocalSettings were initialized; using default window and checkbox state."); Left = 150; Top = 150; - SetCheckBoxStates(0); + SetCheckBoxStates(SmartReaderSelection.GetMask(null, iperlHeads)); } else { Left = (ls.iPerlCommunicationsFormLeft != 0) ? ls.iPerlCommunicationsFormLeft : 150; Top = (ls.iPerlCommunicationsFormTop != 0) ? ls.iPerlCommunicationsFormTop : 150; - SetCheckBoxStates(ls.OptoHeadsEnabled); + SmartReaderSelection.EnsureConfiguredReaders(ls, ProcessData.SmartHeadsUni); + SetCheckBoxStates(SmartReaderSelection.GetMask(ls, iperlHeads)); } if (!checkBoxesEditMode) @@ -876,12 +877,30 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication private void saveButton_Click(object sender, EventArgs e) { - Program.LocalSettings.OptoHeadsEnabled = GetCheckBoxStates(); - Program.LocalSettings.Save(); + SaveCurrentFamilySelection(); DialogResult = DialogResult.OK; Close(); } + private void SaveCurrentFamilySelection() + { + if (Program.LocalSettings == null) + return; + + long checkboxStates = GetCheckBoxStates(); + SmartReaderSelection.SetMask(Program.LocalSettings, iperlHeads, checkboxStates); + + // The dedicated legacy iPerl/S640 dialogs still consume this + // bitmask. Do not overwrite it while editing another family. + if (iperlHeads != null && iperlHeads.Count > 0 && + SmartReaderSelection.GetFamilyKey(iperlHeads[0]) == "iperl") + { + Program.LocalSettings.OptoHeadsEnabled = checkboxStates; + } + + Program.LocalSettings.Save(); + } + #endregion private void SmartCommunicationForm_FormClosing(object sender, FormClosingEventArgs e) @@ -902,16 +921,21 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication long checkboxStates = GetCheckBoxStates(); if (Program.LocalSettings.iPerlCommunicationsFormLeft != Location.X || - Program.LocalSettings.iPerlCommunicationsFormTop != Location.Y || - Program.LocalSettings.OptoHeadsEnabled != checkboxStates) + Program.LocalSettings.iPerlCommunicationsFormTop != Location.Y) { /// Update local settings Program.LocalSettings.iPerlCommunicationsFormLeft = Location.X; Program.LocalSettings.iPerlCommunicationsFormTop = Location.Y; - Program.LocalSettings.OptoHeadsEnabled = checkboxStates; - Program.LocalSettings.Save(); } + SmartReaderSelection.SetMask(Program.LocalSettings, iperlHeads, checkboxStates); + if (iperlHeads != null && iperlHeads.Count > 0 && + SmartReaderSelection.GetFamilyKey(iperlHeads[0]) == "iperl") + { + Program.LocalSettings.OptoHeadsEnabled = checkboxStates; + } + Program.LocalSettings.Save(); + formCompleted = true; DialogResult = DialogResult.OK; @@ -924,6 +948,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication if (initializingMeterTypeItems) return; ComboBox senderCombo = sender as ComboBox; if (senderCombo == null) return; + SaveCurrentFamilySelection(); SelectedTypeReader = senderCombo.SelectedItem?.ToString(); UpdateHeads(); SmartCommunicationForm_Load(this, EventArgs.Empty); diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index 34a0a1824..693750689 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -1633,6 +1633,18 @@ + + UserControl + + + AllyReaderCfgCtrl.cs + + + UserControl + + + AllyReaderManualTestCtrl.cs + @@ -2571,6 +2583,7 @@ Form + SmartCommunicationForm.cs @@ -2821,6 +2834,7 @@ + @@ -3858,6 +3872,12 @@ + + AllyReaderCfgCtrl.cs + + + AllyReaderManualTestCtrl.cs + IPerlUniCfgCtrl.cs diff --git a/TBFTests/Rig/DataEntry/Uni/RegisterReaderSelectionTests.cs b/TBFTests/Rig/DataEntry/Uni/RegisterReaderSelectionTests.cs index 7f272bcec..cd9207b98 100644 --- a/TBFTests/Rig/DataEntry/Uni/RegisterReaderSelectionTests.cs +++ b/TBFTests/Rig/DataEntry/Uni/RegisterReaderSelectionTests.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Results.Entities; +using TBF; using TBF.Rig.DataEntry.Uni; using TBF.Rig.GenericDevices; +using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication; namespace TBFTests.Rig.DataEntry.Uni { @@ -83,12 +85,42 @@ namespace TBFTests.Rig.DataEntry.Uni Assert.IsFalse(selected); } - private static Mock CreateReader(int position, bool smart = false) + [TestMethod] + public void SmartReader_PerFamilySelection_IsUsedInsteadOfLegacyPositionMask() + { + var reader = CreateReader(1, true, "iPerl01"); + var settings = new LocalSettings + { + SmartReaderSelections = new SmartReaderSelectionSettings + { + LegacyOptoHeadsEnabledMigrated = true, + Families = new List + { + new SmartReaderFamilySelection + { + FamilyKey = "iperl", + Readers = new List + { + new SmartReaderSelectionItem { ReaderKey = "iPerl01", Enabled = false } + } + } + } + } + }; + + bool selected = RegisterReaderSelection.IsSelectedForDataEntry( + reader.Object, null, settings, long.MaxValue); + + Assert.IsFalse(selected); + } + + private static Mock CreateReader(int position, bool smart = false, string name = null) { var reader = new Mock(); if (smart) reader.As(); reader.SetupGet(item => item.Position).Returns(position); + reader.SetupGet(item => item.Name).Returns(name); return reader; } } diff --git a/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs index 60329898e..e40e682cf 100644 --- a/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs +++ b/TBFTests/Rig/Uni/SharedDialogs/SmartMetersCommunication/SmartCommunicationFormTests.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Reflection; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using TBF; using TBF.Rig.RegisterReaders.AllyReader; using TBF.Rig.RegisterReaders.GenesisRegReader.implementations; using TBF.Rig.Sequences; @@ -124,6 +126,39 @@ namespace TBFTests.Rig.Uni.SharedDialogs.SmartMetersCommunication }); } + [TestMethod] + public void SelectionSettings_MigratesLegacyMaskToConfiguredIPerlFamily() + { + var first = new Mock(); + first.SetupGet(reader => reader.Name).Returns("iPerl01"); + first.SetupGet(reader => reader.Position).Returns(1); + var second = new Mock(); + second.SetupGet(reader => reader.Name).Returns("iPerl02"); + second.SetupGet(reader => reader.Position).Returns(2); + var settings = new LocalSettings { OptoHeadsEnabled = 1L << 1 }; + + SmartReaderSelection.EnsureConfiguredReaders( + settings, new ISmartReader[] { first.Object, second.Object }); + + SmartReaderFamilySelection family = settings.SmartReaderSelections.Families.Single(); + Assert.AreEqual("iperl", family.FamilyKey); + Assert.IsFalse(family.Readers.Single(item => item.ReaderKey == "iPerl01").Enabled); + Assert.IsTrue(family.Readers.Single(item => item.ReaderKey == "iPerl02").Enabled); + } + + [TestMethod] + public void SelectionSettings_MigratesLegacyMaskToFirstFamilyWhenIPerlIsNotConfigured() + { + AllyMeterReader ally = CreateAllyReader(); + var settings = new LocalSettings { OptoHeadsEnabled = 1L }; + + SmartReaderSelection.EnsureConfiguredReaders(settings, new ISmartReader[] { ally }); + + SmartReaderFamilySelection family = settings.SmartReaderSelections.Families.Single(); + Assert.AreEqual("ally", family.FamilyKey); + Assert.IsTrue(family.Readers.Single().Enabled); + } + private static AllyMeterReader CreateAllyReader() { return new AllyMeterReader(new AllyReaderCfg(new TBF.Rig.RegisterReaders.AllyReader.Factory()));