using LB_SmartVisionCommon; using LB_VisionProcesses.Communicators; using LB_VisionProcesses.Communicators.SiemensS7; using LB_VisionProcesses.Communicators.Tcom; using Microsoft.VisualBasic; using OpenVinoSharp.Extensions.model; using ReaLTaiizor.Forms; using RJCP.IO.Ports; using S7.Net; using System.Data; using System.Diagnostics; using System.Linq; using System.Windows.Forms; using YamlDotNet.Core.Tokens; namespace LB_SmartVision.Forms.Pages.CommunicatorPage { public delegate void CommunicatorS7Change(BaseCommunicator communicator); public partial class CommunicatorS7Form : Form { BaseCommunicator communicator { get; set; } public event CommunicatorS7Change CommunicatorChanged; private PlcConfig config; private DataTable signalTable; private BindingSource bindingSource; private string Name; private void communicatorChanged(BaseCommunicator communicator) { if (CommunicatorChanged != null) { CommunicatorChanged(communicator); } } public CommunicatorS7Form() { InitializeComponent(); cmbType.Items.Clear(); if (communicator != null && !string.IsNullOrEmpty(communicator.CommunicatorConnections["型号"]?.ToString())) { cmbType.Items.Add(communicator.CommunicatorConnections["型号"]?.ToString()); } } public CommunicatorS7Form(BaseCommunicator communicator, string name) { InitializeComponent(); if (communicator == null) { return; } cmbIP.Enabled = false; txtIP.Enabled = false; txtPort.Enabled = false; lblType.Visible = false; cmbType.Visible = false; Name = name; this.Text = name; if (communicator is SiemensLBS7) { txtIP.Text = communicator.CommunicatorConnections["地址"]?.ToString(); txtPort.Text = communicator.CommunicatorConnections["端口"]?.ToString(); cmbType.Items.Clear(); if (!string.IsNullOrEmpty(communicator.CommunicatorConnections["型号"]?.ToString())) { cmbType.Items.Add(communicator.CommunicatorConnections["型号"]?.ToString()); cmbType.Text = communicator.CommunicatorConnections["型号"]?.ToString(); } txtIP.Visible = true; cmbIP.Visible = false; lblType.Visible = true; cmbType.Visible = true; lblIP.Text = "IP"; lblPort.Text = "槽"; } this.communicator = communicator; //加载回调函数 Subscribe(); LoadConfigAndSetupGrid(); } private void CommunicatorForm_FormClosing(object sender, FormClosingEventArgs e) => Unsubscribe(); public void Unsubscribe() { try { if (communicator == null) { return; } //取消回调函数 communicator.MessageReceived -= ShowReceiveMsg; if (communicator != null) { communicatorChanged(communicator); if (GlobalVar.dicCommunicators != null && GlobalVar.dicCommunicators.Keys.Count > 0 && GlobalVar.dicCommunicators.Keys.Contains(communicator.CommunicatorName)) { GlobalVar.dicCommunicators[communicator.CommunicatorName] = communicator; } } } catch (Exception ex) { // 记录错误信息 Debug.WriteLine($"错误: {ex.Message}"); } } public void Subscribe() { try { if (communicator == null) { return; } //取消回调函数 communicator.MessageReceived -= ShowReceiveMsg; //加载回调函数 communicator.MessageReceived += ShowReceiveMsg; } catch (Exception ex) { // 记录错误信息 Debug.WriteLine($"错误: {ex.Message}"); } } /// /// 通讯回调运行 /// /// /// private void ShowReceiveMsg(string strReceiveMsg) { // 如果当前不是 UI 线程,则通过 Invoke 将操作调度到 UI 线程 if (this.InvokeRequired) { this.Invoke(new Action((msg) => { // 更新 UI 控件,比如显示接收到的消息 this.txtReceiveMsg.AppendText(("[接收]" + DateTime.Now.ToString() + ":" + msg)); this.txtReceiveMsg.AppendText("\r\n"); this.txtReceiveMsg.SelectionStart = this.txtReceiveMsg.Text.Length; this.txtReceiveMsg.ScrollToCaret(); }), strReceiveMsg); } else { // 如果已经在 UI 线程上,直接更新 UI this.txtReceiveMsg.AppendText(("[接收]" + DateTime.Now.ToString() + ":" + strReceiveMsg)); this.txtReceiveMsg.AppendText("\r\n"); this.txtReceiveMsg.SelectionStart = this.txtReceiveMsg.Text.Length; this.txtReceiveMsg.ScrollToCaret(); } } private void ShowSendMsg(string strSendMsg) { // 如果当前不是 UI 线程,则通过 Invoke 将操作调度到 UI 线程 if (this.InvokeRequired) { this.Invoke(new Action((msg) => { // 更新 UI 控件,比如显示接收到的消息 this.txtReceiveMsg.AppendText(("[发送]" + DateTime.Now.ToString() + ":" + msg)); this.txtReceiveMsg.AppendText("\r\n"); this.txtReceiveMsg.SelectionStart = this.txtReceiveMsg.Text.Length; this.txtReceiveMsg.ScrollToCaret(); }), strSendMsg); } else { // 如果已经在 UI 线程上,直接更新 UI this.txtReceiveMsg.AppendText(("[发送]" + DateTime.Now.ToString() + ":" + strSendMsg)); this.txtReceiveMsg.AppendText("\r\n"); this.txtReceiveMsg.SelectionStart = this.txtReceiveMsg.Text.Length; this.txtReceiveMsg.ScrollToCaret(); } } private void ShowLogMsg(string msg) { // 如果当前不是 UI 线程,则通过 Invoke 将操作调度到 UI 线程 if (this.InvokeRequired) { this.Invoke(new Action((message) => { this.txtReceiveMsg.AppendText("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + message + "\r\n"); this.txtReceiveMsg.ScrollToCaret(); }), msg); } else { this.txtReceiveMsg.AppendText("[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + msg + "\r\n"); this.txtReceiveMsg.ScrollToCaret(); } } // 批量读取所有信号 private void btnReadAll_Click(object sender, EventArgs e) { if (communicator == null || !communicator.bConnected) { MessageBox.Show("请先连接 PLC", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } try { for (int i = 0; i < config.Signals.Count; i++) { var signal = config.Signals[i]; object value = ((SiemensLBS7)communicator).ReadSignal(signal); config.Signals[i].Value= value?.ToString() ?? ""; signalTable.Rows[i]["Value"] = config.Signals[i].Value; ShowLogMsg(signalTable.Rows[i]["Value"] + ":" + value?.ToString() ?? ""); } } catch (Exception ex) { MessageBox.Show($"批量读取失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // 批量写入:将表格中修改过的值写入 PLC private void btnWriteAll_Click(object sender, EventArgs e) { if (communicator == null || !communicator.bConnected) { MessageBox.Show("请先连接 PLC", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } try { int successCount = 0; for (int i = 0; i < config.Signals.Count; i++) { var signal = config.Signals[i]; string newValueStr = signalTable.Rows[i]["Value"]?.ToString(); if (!string.IsNullOrEmpty(newValueStr)) { object convertedValue = ConvertValue(signal.DataType, newValueStr); ((SiemensLBS7)communicator).WriteSignal(signal, convertedValue); successCount++; } ShowSendMsg(signalTable.Rows[i]["Value"] + ":" + newValueStr?.ToString() ?? ""); } MessageBox.Show($"批量写入完成,成功写入 {successCount} 个信号。", "写入结果", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"批量写入失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void btnConfig_Click(object sender, EventArgs e) { using (var configForm = new ConfigForm(config, GlobalVar.allPlcSettingPath + "\\" + this.Name + ".json")) { if (configForm.ShowDialog() == DialogResult.OK) { config = S7ConfigService.LoadConfig(GlobalVar.allPlcSettingPath); LoadConfigAndSetupGrid(); UpdateConnectionStatus(); } } } // 数据类型转换辅助方法 private object ConvertValue(string dataType, string valueStr) { switch (dataType) { case "Bool": { if (bool.TryParse(valueStr, out bool b)) return b; if (valueStr == "1") return true; if (valueStr == "0") return false; throw new Exception("无效的布尔值"); } case "Byte": { return byte.Parse(valueStr); } case "Int": { return short.Parse(valueStr); } case "DInt": { return int.Parse(valueStr); } case "Real": { return float.Parse(valueStr); } case "String": { return valueStr; } default: { throw new NotSupportedException($"不支持的类型: {dataType}"); } } } private void LoadConfigAndSetupGrid() { config = S7ConfigService.LoadConfig(GlobalVar.allPlcSettingPath + "\\" + this.Name + ".json"); if (GlobalVar.allPlcSettings.Keys.Contains(Name)) { GlobalVar.allPlcSettings[Name] = config; } else { GlobalVar.allPlcSettings.TryAdd(Name, config); } ConfigManager>.SaveConfig>(GlobalVar.allPlcSettings, GlobalVar.allPlcSettingsPath); // 构建 DataTable 用于展示信号(支持编辑值列) signalTable = new DataTable(); signalTable.Columns.Add("SignalName", typeof(string)); signalTable.Columns.Add("Address", typeof(string)); signalTable.Columns.Add("Value", typeof(string)); foreach (var signal in config.Signals) { string address = $"{signal.Area}"; if (signal.Area == "DB") address += $"{signal.DbNumber}.DB"; address += $"{signal.ByteOffset}.{signal.BitOffset}"; signalTable.Rows.Add(signal.SignalName, address, ""); } bindingSource = new BindingSource(); bindingSource.DataSource = signalTable; dgvSignals.DataSource = bindingSource; dgvSignals.Columns["SignalName"].HeaderText = "信号名称"; dgvSignals.Columns["SignalName"].ReadOnly = true; dgvSignals.Columns["Address"].HeaderText = "PLC 地址"; dgvSignals.Columns["Address"].ReadOnly = true; dgvSignals.Columns["Value"].HeaderText = "当前值"; dgvSignals.Columns["Value"].ReadOnly = false; // 允许用户编辑 UpdateConnectionStatus(); dgvSignals.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; } private void UpdateConnectionStatus() { if (communicator != null && communicator.bConnected) { lblStatus.Text = $"已连接: {config.PlcIpAddress}"; lblStatus.ForeColor = Color.Green; } else { lblStatus.Text = "未连接"; lblStatus.ForeColor = Color.Red; } } // ========== 右键菜单:单信号操作 ========== private SignalConfig GetSelectedSignal() { if (dgvSignals.CurrentRow == null) return null; int index = dgvSignals.CurrentRow.Index; if (index >= 0 && index < config.Signals.Count) return config.Signals[index]; return null; } private void menuReadSignal_Click(object sender, EventArgs e) { var signal = GetSelectedSignal(); if (signal == null) return; if (communicator != null && !communicator.bConnected) { MessageBox.Show("请先连接 PLC"); return; } try { object value = ((SiemensLBS7)communicator).ReadSignal(signal); MessageBox.Show($"信号 [{signal.SignalName}] 当前值: {value}", "读取结果"); signalTable.Rows[dgvSignals.CurrentRow.Index]["Value"] = value?.ToString(); } catch (Exception ex) { MessageBox.Show($"读取失败: {ex.Message}"); } } private void menuWriteSignal_Click(object sender, EventArgs e) { var signal = GetSelectedSignal(); if (signal == null) return; if (communicator != null && !communicator.bConnected) { MessageBox.Show("请先连接 PLC"); return; } string input = Interaction.InputBox( $"请输入要写入 {signal.SignalName} 的值 ({signal.DataType})", "写入信号", ""); if (string.IsNullOrEmpty(input)) return; try { object value = ConvertValue(signal.DataType, input); ((SiemensLBS7)communicator).WriteSignal(signal, value); MessageBox.Show("写入成功"); } catch (Exception ex) { MessageBox.Show($"写入失败: {ex.Message}"); } } } }