using 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
{
///
/// SingleControl.xaml 的交互逻辑
///
[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 datasource = new ObservableCollection();
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 doControls = new List();
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 datas = ExcelHelper.ReadFromExcel(fileName);
//foreach (var data in datas)
//{
// Datasource.Add(data);
//}
Datasource = new ObservableCollection(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}");
}));
}, "请稍候", "正在重连...");
}
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)
{
return new LightBall
{
Size = 40,
DataContext = data
};
}
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();
}
else
{
Datasource = XmlHelper.DeserializeFromXml>(dataFilePath);
}
}
catch (Exception ex)
{
log.Error("Load Data Error.", ex);
Datasource = new ObservableCollection();
}
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(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();
var connectStatus = false;
try
{
if (File.Exists(dataFilePath))
{
datasource = XmlHelper.DeserializeFromXml>(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:
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:
((Button)ui).Content = value;
break;
default:
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;
}
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 string msg);
}
else
{
res = controller.ReadDOBool(data.Address, out val, out string msg);
}
if (res == true)
{
value = val.ToString();
return true;
}
value = null;
return false;
default:
value = null;
return true;
}
}
}
}