using EasyModbus; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Timers; using ModbusSlave.Dialog; namespace ModbusSlave { public partial class MainForm : Form { private readonly int DATA_COUNT = 20; private ModbusServer server = new ModbusServer(); private System.Timers.Timer updateTimer; private bool isFirstLog = true; public MainForm() { InitializeComponent(); InitInstance(); } private void btnRun_Click(object sender, EventArgs e) { try { server = new ModbusServer(); server.Port = (int)nudPort.Value; server.UnitIdentifier = Convert.ToByte(nudIdentifier.Value); if (!CheckPortInUse(server.Port)) { Log("[WARNING] unavailable port"); return; } server.Listen(); Log("--- Server start ---"); btnRun.Enabled = false; nudIdentifier.Enabled = false; nudPort.Enabled = false; tbStatus.Text = "Run"; tbStatus.BackColor = Color.Lime; updateTimer.Start(); } catch (Exception ex) { Log("[ERROR] " + ex.Message); } } private void btnStop_Click(object sender, EventArgs e) { try { server.StopListening(); Log("--- Server stop ---"); btnRun.Enabled = true; nudIdentifier.Enabled = true; nudPort.Enabled = true; tbStatus.Text = "Stop"; tbStatus.BackColor = Color.Silver; updateTimer.Stop(); } catch (Exception ex) { Log("[ERROR] " + ex.Message); } } private void btnClear_Click(object sender, EventArgs e) { ClearLog(); } private void Lv_MouseDoubleClick(object sender, MouseEventArgs e) { if (btnRun.Enabled) return; ListView lv = sender as ListView; if (lv.SelectedItems.Count < 1) return; int idx = lv.SelectedItems[0].Index; short tmp = 0; InputValueDialog dlg = new InputValueDialog(); dlg.Value = lv.SelectedItems[0].SubItems[1].Text; dlg.StartPosition = FormStartPosition.CenterParent; switch (lv.Name) { case "lv0x": server.coils[idx + 1] = !server.coils[idx + 1]; break; case "lv1x": server.discreteInputs[idx + 1] = !server.discreteInputs[idx + 1]; break; case "lv3x": if (dlg.ShowDialog() != DialogResult.OK) return; if (!short.TryParse(dlg.Value, out tmp)) { Log($"[ERROR] Value should be a number and in range of {short.MinValue} to {short.MaxValue}"); return; } server.inputRegisters[idx + 1] = tmp; break; case "lv4x": if (dlg.ShowDialog() != DialogResult.OK) return; if (!short.TryParse(dlg.Value, out tmp)) { Log($"[ERROR] Value should be a number and in range of {short.MinValue} to {short.MaxValue}"); return; } server.holdingRegisters[idx + 1] = tmp; break; } } private void InitInstance() { server = new ModbusServer(); updateTimer = new System.Timers.Timer(); updateTimer.Interval = 1000; updateTimer.Elapsed += UpdateTimer_Elapsed; lv0x.MouseDoubleClick += Lv_MouseDoubleClick; lv1x.MouseDoubleClick += Lv_MouseDoubleClick; lv3x.MouseDoubleClick += Lv_MouseDoubleClick; lv4x.MouseDoubleClick += Lv_MouseDoubleClick; InitListView(); } private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) { UpdateListView(0); UpdateListView(1); UpdateListView(3); UpdateListView(4); } private void Log(string text) { rtbLog.AppendText((isFirstLog ? "" : Environment.NewLine) + text); isFirstLog = false; ScrollToEnd(); } private void ScrollToEnd() { if (!chkAutoScroll.Checked) return; rtbLog.SelectionStart = rtbLog.Text.Length; rtbLog.ScrollToCaret(); } private void ClearLog() { rtbLog.Clear(); isFirstLog = true; ScrollToEnd(); } private bool CheckPortInUse(int port) { bool isAvailable = true; var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); var tcpListenerArray = ipGlobalProperties.GetActiveTcpListeners(); foreach (var ip in tcpListenerArray) { if (ip.Port == port) { isAvailable = false; break; } } return isAvailable; } private void InitListView() { for (int i = 0; i < 5; i++) { if (i == 2) continue; string controlName = $"lv{i}x"; Control[] foundControls = this.Controls.Find(controlName, true); if (foundControls.Length < 1 || !(foundControls[0] is ListView)) continue; ListView listView = foundControls[0] as ListView; listView.GetType() .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) .SetValue(listView, true, null); listView.BeginUpdate(); listView.Items.Clear(); for (int j = 0; j < DATA_COUNT; j++) { ListViewItem item = new ListViewItem(); switch(i) { case 0: // 0x: 00001 ~ 00010 item.Text = string.Format("{0:D5}", j + 1); break; case 1: // 1x: 10001 ~ 10010 item.Text = (10000 + j + 1).ToString(); break; case 3: // 3x: 30001 ~ 30010 item.Text = (30000 + j + 1).ToString(); break; case 4: // 4x: 40001 ~ 40010 item.Text = (40000 + j + 1).ToString(); break; } item.SubItems.Add("0"); listView.Items.Add(item); } listView.Columns[0].Width = -2; listView.Columns[1].Width = -2; listView.EndUpdate(); } } private void UpdateListView(int regNum) { string controlName = $"lv{regNum}x"; Control[] foundControls = this.Controls.Find(controlName, true); if (foundControls.Length < 1 || !(foundControls[0] is ListView)) return; ListView listView = foundControls[0] as ListView; if (listView.InvokeRequired) { listView.BeginInvoke(new MethodInvoker(delegate { UpdateListView(regNum); })); return; } listView.BeginUpdate(); for (int i = 0; i < DATA_COUNT; i++) { string value = ""; switch (regNum) { case 0: value = server.coils[i + 1] ? "1" : "0"; break; case 1: value = server.discreteInputs[i + 1] ? "1" : "0"; break; case 3: value = server.inputRegisters[i + 1].ToString(); break; case 4: value = server.holdingRegisters[i + 1].ToString(); break; } listView.Items[i].SubItems[1].Text = value; } listView.EndUpdate(); } } }