From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点
---
Plugin.DODriverControl/SingleControl.xaml.cs | 585 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 585 insertions(+), 0 deletions(-)
diff --git a/Plugin.DODriverControl/SingleControl.xaml.cs b/Plugin.DODriverControl/SingleControl.xaml.cs
new file mode 100644
index 0000000..d63d0ce
--- /dev/null
+++ b/Plugin.DODriverControl/SingleControl.xaml.cs
@@ -0,0 +1,585 @@
+锘縰sing CommunityToolkit.Mvvm.ComponentModel;
+using HMI.Base;
+using HMI.Core.Util;
+using HMI.UI;
+using HMI.UI.Util;
+using HMI.Util;
+using log4net;
+using Microsoft.Win32;
+using Plugin.DODriverControl.Api;
+using Plugin.DODriverControl.Entity;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Timers;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace Plugin.DODriverControl
+{
+ /// <summary>
+ /// SingleControl.xaml 鐨勪氦浜掗�昏緫
+ /// </summary>
+ [ObservableObject]
+ public partial class SingleControl : SimpleMaskControl
+ {
+ public SingleControl(string name, string baseFolder)
+ {
+ this.name = name;
+ this.baseFolder = Path.Combine(baseFolder, name);
+ if (!Directory.Exists(this.baseFolder)) Directory.CreateDirectory(this.baseFolder);
+ this.dataFilePath = Path.Combine(this.baseFolder, "data.xml");
+ this.configFilePath = Path.Combine(this.baseFolder, "config.xml");
+ this.DataContext = this;
+
+ refreshTimer = new System.Timers.Timer();
+ refreshTimer.Interval = 200;
+ refreshTimer.AutoReset = false;
+ refreshTimer.Elapsed += RefreshTimer_Elapsed;
+
+ InitializeComponent();
+
+ commonColor = Application.Current.FindResource("VerBar") as Brush;
+ successColor = Application.Current.FindResource("GreenVerBar") as Brush;
+ errorColor = Application.Current.FindResource("DangerVerBar") as Brush;
+ }
+
+ [ObservableProperty]
+ private bool connectStatus = false;
+ [ObservableProperty]
+ private ObservableCollection<DIOData> datasource = new ObservableCollection<DIOData>();
+
+ public ILog log { get; set; }
+ public string name { get; private set; }
+ public string baseFolder { get; private set; }
+
+
+ private IDIOController controller;
+ private string dataFilePath;
+ private string configFilePath;
+ private System.Timers.Timer refreshTimer;
+
+ private ConfigData config;
+
+ private List<FrameworkElement> doControls = new List<FrameworkElement>();
+
+ private Brush successColor;
+ private Brush errorColor;
+ private Brush commonColor;
+
+ private CancellationTokenSource cts = new CancellationTokenSource();
+
+ public void Active()
+ {
+ if (refreshTimer != null) refreshTimer.Enabled = true;
+ }
+
+ public void DeActive()
+ {
+ if (refreshTimer != null) refreshTimer.Enabled = false;
+ }
+
+ public void Close()
+ {
+ cts.Cancel();
+ ConnectStatus = false;
+ if (refreshTimer != null)
+ {
+ refreshTimer.Enabled = false;
+ refreshTimer.Elapsed -= RefreshTimer_Elapsed;
+ refreshTimer = null;
+ }
+ controller?.Dispose();
+ controller = null;
+ }
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ var btn = sender as Button;
+ var data = btn?.Tag as DIOData;
+ switch (btn.Name)
+ {
+ case "addBtn":
+ DoAddData();
+ break;
+ case "editBtn":
+ DoEditData(data);
+ break;
+ case "exportBtn":
+ DoExportExcel();
+ break;
+ case "importBtn":
+ DoImportExcel();
+ break;
+ case "reConnectBtn":
+ DoReconnect();
+ break;
+ case "configBtn":
+ DoOpenConfig();
+ break;
+ case "rfhBtn":
+ Refresh();
+ break;
+ case "delBtn":
+ DoDeleteData(data);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void DoAddData()
+ {
+ var win = new DataModal
+ {
+ Owner = Window.GetWindow(this),
+ Title = "鏂板"
+ };
+ if (win.Open(new DIOData(), Datasource, dataFilePath) == true)
+ {
+ ToastHelper.Success("娣诲姞鎴愬姛");
+ Refresh();
+ }
+ }
+
+ private void DoEditData(DIOData data)
+ {
+ var win = new DataModal
+ {
+ Owner = Window.GetWindow(this),
+ Title = "缂栬緫"
+ };
+ if (win.Open(data, Datasource, dataFilePath) == true)
+ {
+ ToastHelper.Success("缂栬緫鎴愬姛");
+ Refresh();
+ }
+ }
+
+ private void DoExportExcel()
+ {
+ try
+ {
+ SaveFileDialog sfd = new SaveFileDialog();
+ sfd.Filter = "Excel|*.xls";
+ if (sfd.ShowDialog() != true)
+ {
+ return;
+ }
+ string fileName = sfd.FileName;
+ ExcelHelper.ExportExcel(fileName, Datasource);
+ ToastHelper.Success("瀵煎嚭鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ log.Error("Export Error.", ex);
+ ToastHelper.Error($"瀵煎嚭澶辫触: {ex.Message}");
+ }
+ }
+
+ private void DoImportExcel()
+ {
+ try
+ {
+ OpenFileDialog ofd = new OpenFileDialog();
+ ofd.Filter = "Excel|*.xls";
+ if (ofd.ShowDialog() != true)
+ {
+ return;
+ }
+ string fileName = ofd.FileName;
+ List<DIOData> datas = ExcelHelper.ReadFromExcel<DIOData>(fileName);
+ //foreach (var data in datas)
+ //{
+ // Datasource.Add(data);
+ //}
+ Datasource = new ObservableCollection<DIOData>(datas);
+ SaveDatasourceToXml();
+ ToastHelper.Success("瀵煎叆鎴愬姛");
+ RefreshDOControl();
+ }
+ catch (Exception ex)
+ {
+ log.Error("Export Error.", ex);
+ ToastHelper.Error($"瀵煎叆澶辫触: {ex.Message}");
+ }
+ }
+
+ private void DoReconnect()
+ {
+ ConnectStatus = false;
+ ShowMask(() =>
+ {
+ refreshTimer.Enabled = false;
+ controller?.Close();
+ controller = CreateController();
+ var res = controller.Start(out string msg);
+ if (!res)
+ {
+ throw new Exception($"{msg}");
+ }
+ refreshTimer.Enabled = true;
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ ConnectStatus = true;
+ }));
+ }, (ex) =>
+ {
+ controller = null;
+ log.Error("Reconnect Error.", ex);
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ MsgBoxHelper.Error(this, $"杩炴帴澶辫触锛歿ex.Message}");
+ }));
+ }, "璇风◢鍊�", "姝e湪閲嶈繛...");
+ }
+
+ private void DoOpenConfig()
+ {
+ ConfigModal modal = new ConfigModal
+ {
+ Title = "璁剧疆",
+ Owner = Window.GetWindow(this)
+ };
+ if (modal.Open(config) == true)
+ {
+ config = modal.config;
+ SaveConfigToXml();
+ DoReconnect();
+ }
+ }
+
+ private void DoDeleteData(DIOData data)
+ {
+ if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), "纭畾瑕佸垹闄ら�変腑鐨勬暟鎹悧锛�") == MessageBoxResult.OK)
+ {
+ int index = Datasource.IndexOf(data);
+ if (index < 0) return;
+ Datasource.RemoveAt(index);
+ SaveDatasourceToXml();
+ ToastHelper.Success("鍒犻櫎鎴愬姛");
+ RefreshDOControl();
+ }
+ }
+
+ private void SaveConfigToXml()
+ {
+ XmlHelper.SerializeToXml(configFilePath, config);
+ }
+
+ private void SaveDatasourceToXml()
+ {
+ XmlHelper.SerializeToXml(dataFilePath, Datasource);
+ }
+
+ private void RefreshDOControl()
+ {
+ try
+ {
+ refreshTimer.Enabled = false;
+ diWrap.Children.Clear();
+ doWrap.Children.Clear();
+ doControls.Clear();
+ foreach (var data in Datasource)
+ {
+ var fg = new GroupBox
+ {
+ //Header = $"{data.Name} 鍦板潃:{data.Address}",
+ Header = string.IsNullOrEmpty(data.Name)?"棰勭暀":data.Name,// $"{data.Name}",
+ Content = CreateButton(data),
+ Height = 80,
+ Width = 180
+ };
+ doControls.Add((FrameworkElement)fg.Content);
+ if (data.Dio == DIOType.DI)
+ {
+ diWrap.Children.Add(fg);
+ }
+ else
+ {
+ doWrap.Children.Add(fg);
+ }
+ }
+ }
+ finally
+ {
+ refreshTimer.Enabled = true;
+ }
+ }
+
+ private FrameworkElement CreateButton(DIOData data)
+ {
+ if (data.Dio == DIOType.DI)
+ {
+ if (data.ControlType == ControlType.BOOL)
+ {
+ return new LightBall
+ {
+ Size = 40,
+ DataContext = data
+ };
+ }
+ else
+ {
+ return new TextBox
+ {
+ IsReadOnly = true
+ };
+ }
+ }
+ Style btnStyle = this.FindResource("PrimaryButton") as Style;
+ var btn = new Button
+ {
+ DataContext = data,
+ Style = btnStyle,
+ MinWidth = 120,
+ MinHeight = 40
+ };
+ btn.Click += ControlButton_Click;
+ return btn;
+ }
+
+ private void ControlButton_Click(object sender, RoutedEventArgs args)
+ {
+ if (!ConnectStatus)
+ {
+ ToastHelper.Error("璇峰厛杩炴帴鎺у埗鍣�");
+ return;
+ }
+ Button btn = sender as Button;
+ DIOData data = btn.DataContext as DIOData;
+ try
+ {
+ refreshTimer.Enabled = false;
+ var value = btn.Content?.ToString();
+ string val = value == data.PressDownText ? data.PressUpValue : data.PressDownValue;
+ var res = controller.WriteBool(data.Address, bool.Parse(val), out string msg);
+ if (!res) throw new Exception(msg);
+ }
+ catch (Exception ex)
+ {
+ log.Error("ControlButton_Click", ex);
+ ToastHelper.Error($"{data.Name}: {ex.Message}");
+ }
+ finally
+ {
+ refreshTimer.Enabled = true;
+ }
+ }
+
+ private void Refresh()
+ {
+ try
+ {
+ if (!File.Exists(dataFilePath))
+ {
+ Datasource = new ObservableCollection<DIOData>();
+ }
+ else
+ {
+ Datasource = XmlHelper.DeserializeFromXml<ObservableCollection<DIOData>>(dataFilePath);
+ }
+ }
+ catch (Exception ex)
+ {
+ log.Error("Load Data Error.", ex);
+ Datasource = new ObservableCollection<DIOData>();
+ }
+ RefreshDOControl();
+ }
+
+ private void UserControl_Loaded(object sender, RoutedEventArgs e)
+ {
+ ShowMask(() =>
+ {
+ try
+ {
+ if (!File.Exists(configFilePath))
+ {
+ config = new ConfigData
+ {
+ Type = ConfigType.ModBusTcp,
+ IP = "127.0.0.1",
+ Port = "502"
+ };
+ }
+ else
+ {
+ config = XmlHelper.DeserializeFromXml<ConfigData>(configFilePath);
+ }
+ }
+ catch (Exception ex)
+ {
+ log.Error("Load Config Error.", ex);
+ config = new ConfigData
+ {
+ Type = ConfigType.ModBusTcp,
+ IP = "127.0.0.1",
+ Port = "502"
+ };
+ }
+ var datasource = new ObservableCollection<DIOData>();
+ var connectStatus = false;
+ try
+ {
+ if (File.Exists(dataFilePath))
+ {
+ datasource = XmlHelper.DeserializeFromXml<ObservableCollection<DIOData>>(dataFilePath);
+ }
+ }
+ catch (Exception ex)
+ {
+ log.Error("Load Data Error.", ex);
+ }
+
+ try
+ {
+ controller = CreateController();
+ var res = controller.Start(out string msg);
+ if (!res)
+ {
+ throw new Exception($"鎺у埗鍣ㄨ繛鎺ュけ璐�: {msg}");
+ }
+ connectStatus = true;
+ }
+ catch (Exception ex)
+ {
+ controller = null;
+ log.Error("Modbus Connect Error.", ex);
+ connectStatus = false;
+ }
+
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ ConnectStatus = connectStatus;
+ Datasource = datasource;
+ RefreshDOControl();
+ refreshTimer.Enabled = true;
+ this.Loaded -= UserControl_Loaded;
+ }));
+ }, null, "璇风◢鍚�", "鍔犺浇涓�...");
+ }
+
+ public IDIOController CreateController()
+ {
+ switch (config.Type)
+ {
+ case ConfigType.ModBusTcp:
+ return new ModBusTcpController
+ {
+ Ip = config.IP,
+ Port = int.Parse(config.Port),
+ };
+ case ConfigType.PCI_7432:
+ return new PCIController
+ {
+ Name = "PCI_7432",
+ };
+ default:
+ return null;
+ }
+ }
+
+ private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ bool isConnected = ConnectStatus;
+ if (!isConnected) return;
+ try
+ {
+ refreshTimer.Enabled = false;
+ if (cts.IsCancellationRequested)
+ {
+ isConnected = false;
+ return;
+ }
+
+ for (int i = doControls.Count - 1; i >= 0; i--)
+ {
+ var ui = doControls[i];
+ ui.Dispatcher.Invoke(() =>
+ {
+ DIOData data = ui.DataContext as DIOData;
+ if (data == null) return;
+ isConnected = GetValueByModbusTcp(data, out string value);
+ if (!isConnected) return;
+ switch (data.ControlType)
+ {
+ case ControlType.BOOL:
+ case ControlType.PULSE:
+ if (data.Dio == DIOType.DI)
+ {
+ ((LightBall)ui).Value = value == data.PressDownValue;
+ ((LightBall)ui).Content = value == data.PressDownValue ? data.PressDownText : data.PressUpText;
+ }
+ else
+ {
+ var color = value == data.PressDownValue ? successColor : errorColor;
+ ((Button)ui).Background = color;
+ ((Button)ui).Content = value == data.PressDownValue ? data.PressDownText : data.PressUpText;
+ }
+ break;
+ case ControlType.INT:
+ case ControlType.WORD:
+ case ControlType.REAL:
+ case ControlType.LREAL:
+ case ControlType.STRING:
+ case ControlType.WSTRING:
+ ((TextBox)ui).Text = value;
+ break;
+ default:
+ ((TextBox)ui).Text = value;
+ break;
+ }
+ });
+ }
+ }
+ catch (TaskCanceledException ex)
+ {
+ isConnected = false;
+ }
+ finally
+ {
+ if (isConnected && ConnectStatus) refreshTimer.Enabled = true;
+ else ConnectStatus = false;
+ }
+ }
+
+ private bool GetValueByModbusTcp(DIOData data, out string value)
+ {
+ if (controller == null)
+ {
+ value = null;
+ return false;
+ }
+ string msg;
+ switch (data.ControlType)
+ {
+ case ControlType.BOOL:
+ bool val = false;
+ bool res = false;
+ if (data.Dio == DIOType.DI)
+ {
+ res = controller.ReadDIBool(data.Address, out val, out msg);
+ }
+ else
+ {
+ res = controller.ReadDOBool(data.Address, out val, out msg);
+ }
+ if (res == true)
+ {
+ value = val.ToString();
+ return true;
+ }
+ value = null;
+ return false;
+ default:
+ if (controller.ReadDIDouble(data.Address, out double? dVal, out msg))
+ {
+ value = dVal.ToString();
+ }
+ value = null;
+ return true;
+ }
+ }
+ }
+}
--
Gitblit v1.9.1