using JinYuan.Helper; using JinYuan.Models; using JinYuan.VirtualDataLibrary; using Language; using MiniExcelLibs; using SqlSugar; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; namespace LargeSquareOne { public partial class VariableEditForm : MultiLanguageForm { private string variableConfigPath = CommonMethods.variableConfigPath; private string groupConfigPath = CommonMethods.groupConfigPath; private List groupNames = new List(); private List Variables = new List(); private VariableConfig editingConfig; private int PlcNum; public VariableEditForm(int plcNum) { PlcNum = plcNum; InitializeComponent(); groupNames = GetGroupNames(PlcNum); InitializeDataGridView(); Variables = GetALLVariables(); LoadVariables(); } private void InitializeDataGridView() { variableTable.DataError += VariableTable_DataError; variableTable.AutoGenerateColumns = false; variableTable.AllowUserToAddRows = false; variableTable.SelectionMode = DataGridViewSelectionMode.FullRowSelect; variableTable.MultiSelect = false; variableTable.ReadOnly = false; variableTable.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; variableTable.RowHeadersVisible = false; variableTable.Columns.Clear(); // 添加列 variableTable.Columns.AddRange(new DataGridViewColumn[] { new DataGridViewTextBoxColumn { Name = "PlcNum", HeaderText = "PLC编号", Width = 80, ReadOnly = true, DataPropertyName = "PlcNum" }, new DataGridViewComboBoxColumn { Name = "GroupName", HeaderText = "组名", Width = 100, DataPropertyName = "GroupName", ValueType = typeof(string), ReadOnly = false, DefaultCellStyle = new DataGridViewCellStyle { BackColor = Color.FromArgb(14, 23, 38), ForeColor = Color.White, SelectionBackColor = Color.FromArgb(0, 122, 204), SelectionForeColor = Color.White, Font = new Font("微软雅黑", 9F) } }, new DataGridViewTextBoxColumn { Name = "VarName", HeaderText = "变量名称", Width = 200, DataPropertyName = "VarName" }, new DataGridViewTextBoxColumn { Name = "VarAddress", HeaderText = "变量地址", Width = 100, DataPropertyName = "VarAddress" }, new DataGridViewTextBoxColumn { Name = "Start", HeaderText = "起始地址", Width = 100, DataPropertyName = "Start", ReadOnly = true // Start列只读,由VarAddress自动生成 }, new DataGridViewComboBoxColumn { Name = "DataType", HeaderText = "数据类型", Width = 150, DataPropertyName = "DataType", ValueType = typeof(string), DefaultCellStyle = new DataGridViewCellStyle { BackColor = Color.FromArgb(14, 23, 38), ForeColor = Color.White, SelectionBackColor = Color.FromArgb(0, 122, 204), SelectionForeColor = Color.White, Font = new Font("微软雅黑", 9F) } }, new DataGridViewTextBoxColumn { Name = "OffsetOrLength", HeaderText = "长度偏移", Width = 80, DataPropertyName = "OffsetOrLength" }, new DataGridViewTextBoxColumn { Name = "Remark", HeaderText = "备注", Width = 120, DataPropertyName = "Remark", Visible = false }, new DataGridViewButtonColumn { Name = "Edit", HeaderText = "操作", Width = 60, Text = "修改".Translated(), UseColumnTextForButtonValue = true }, new DataGridViewButtonColumn { Name = "Delete", HeaderText = "", Width = 60, Text = "删除".Translated(), UseColumnTextForButtonValue = true } }); // 设置ComboBox列数据源 if (variableTable.Columns["GroupName"] is DataGridViewComboBoxColumn groupColumn) { groupColumn.Items.AddRange(groupNames.ToArray()); } if (variableTable.Columns["DataType"] is DataGridViewComboBoxColumn typeColumn) { typeColumn.Items.AddRange(new string[] { "Bit", "Byte", "ByteArray", "Short", "Arrshort", "Word", "DWord", "Int", "DInt", "Real", "String" }); } // 添加事件处理 variableTable.CellBeginEdit += DataGridView_CellBeginEdit; variableTable.CellEndEdit += DataGridView_CellEndEdit; variableTable.CellContentClick += DataGridView_CellContentClick; variableTable.CellValueChanged += DataGridView_CellValueChanged; variableTable.EditingControlShowing += VariableTable_EditingControlShowing; } /// /// 处理DataGridView数据错误,避免弹出默认错误对话框 /// private void VariableTable_DataError(object sender, DataGridViewDataErrorEventArgs e) { e.ThrowException = false; // 修复ComboBox值无效的问题 if (e.Exception is ArgumentException && variableTable.Columns[e.ColumnIndex] is DataGridViewComboBoxColumn) { var comboColumn = (DataGridViewComboBoxColumn)variableTable.Columns[e.ColumnIndex]; var cell = variableTable.Rows[e.RowIndex].Cells[e.ColumnIndex]; if (cell.Value == null || !comboColumn.Items.Contains(cell.Value)) { if (comboColumn.Items.Count > 0) { cell.Value = comboColumn.Items[0]; } else { cell.Value = null; } } } } /// /// 编辑控件显示时确保数据源正确 /// private void VariableTable_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { if (variableTable.CurrentCell == null) return; // 组名ComboBox if (variableTable.Columns[variableTable.CurrentCell.ColumnIndex].Name == "GroupName") { if (e.Control is ComboBox comboBox) { comboBox.Items.Clear(); comboBox.Items.AddRange(groupNames.ToArray()); comboBox.DropDownStyle = ComboBoxStyle.DropDownList; } } // 数据类型ComboBox else if (variableTable.Columns[variableTable.CurrentCell.ColumnIndex].Name == "DataType") { if (e.Control is ComboBox comboBox) { comboBox.DropDownStyle = ComboBoxStyle.DropDownList; } } } /// /// 单元格值变更事件(自动更新Start列) /// private void DataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex < 0 || e.ColumnIndex < 0) return; // 当VarAddress列值变更时,自动更新Start列 if (variableTable.Columns[e.ColumnIndex].Name == "VarAddress") { var cellValue = variableTable["VarAddress", e.RowIndex].Value?.ToString(); if (!string.IsNullOrEmpty(cellValue) && cellValue.StartsWith("D", StringComparison.OrdinalIgnoreCase) && int.TryParse(cellValue.Substring(1), out int address)) { variableTable["Start", e.RowIndex].Value = address.ToString(); variableTable.InvalidateRow(e.RowIndex); variableTable.Update(); } } } // 获取组名称(按PLC编号筛选) private List GetGroupNames(int plcNum) { try { if (!File.Exists(groupConfigPath)) { // 创建默认组配置文件 Directory.CreateDirectory(Path.GetDirectoryName(groupConfigPath)); MiniExcel.SaveAs(groupConfigPath, new List { new PlcGroupConfig { PlcNum = plcNum, GroupName = "默认组".Translated() } }); } var groups = MiniExcel.Query(groupConfigPath).ToList(); var result = groups.Where(g => g.PlcNum == plcNum) .Select(g => g.GroupName?.Trim() ?? "") .Where(g => !string.IsNullOrEmpty(g)) .Distinct() .ToList(); if (result.Count == 0) { result.Add("默认组".Translated()); } return result; } catch (Exception) { return new List { "默认组".Translated() }; } } /// /// 获取所有变量 /// private List GetALLVariables() { try { if (!File.Exists(variableConfigPath)) { return new List(); } var variables = MiniExcel.Query(variableConfigPath).ToList(); foreach (var variable in variables) { // 确保组名有效 if (string.IsNullOrEmpty(variable.GroupName) || !groupNames.Contains(variable.GroupName)) { variable.GroupName = groupNames.Count > 0 ? groupNames[0] : "默认组".Translated(); } // 确保数据类型有效 if (string.IsNullOrEmpty(variable.DataType) || !ValidateDataType(variable.DataType)) { variable.DataType = "Short"; } // 确保长度有效 if (variable.OffsetOrLength <= 0) { variable.OffsetOrLength = 1; } // Start地址 if (string.IsNullOrEmpty(variable.Start) && !string.IsNullOrEmpty(variable.VarAddress) && variable.VarAddress.StartsWith("D") && int.TryParse(variable.VarAddress.Substring(1), out int startAddr)) { variable.Start = startAddr.ToString(); } } return variables; } catch (Exception) { return new List(); } } /// /// 加载当前PLC的变量到表格 /// private void LoadVariables() { try { variableTable.DataSource = null; var plcVariables = Variables.Where(g => g.PlcNum == PlcNum).ToList(); variableTable.DataSource = plcVariables; // 刷新表格 variableTable.Invalidate(); } catch (Exception ex) { ShowError($"加载变量失败: {ex.Message}"); } } /// /// 开始编辑单元格 /// private void DataGridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { if (e.RowIndex < 0) return; // 获取当前编辑的配置 if (variableTable.Rows[e.RowIndex].DataBoundItem is VariableConfig config) { editingConfig = config; } // 禁止编辑PLC编号和Start列 string colName = variableTable.Columns[e.ColumnIndex].Name; if (colName == "PlcNum" || colName == "Start") { e.Cancel = true; return; } } /// /// 结束编辑单元格 /// private void DataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex < 0 || e.RowIndex >= variableTable.Rows.Count) return; var row = variableTable.Rows[e.RowIndex]; if (row.DataBoundItem is not VariableConfig config) return; var columnName = variableTable.Columns[e.ColumnIndex].Name; var cellValue = row.Cells[e.ColumnIndex].Value; try { UpdateConfigValue(config, columnName, cellValue); variableTable.InvalidateRow(e.RowIndex); variableTable.Update(); } catch (Exception ex) { ShowError($"更新数据失败: {ex.Message}"); // 回滚单元格值 switch (columnName) { case "GroupName": row.Cells[e.ColumnIndex].Value = config.GroupName; break; case "VarName": row.Cells[e.ColumnIndex].Value = config.VarName; break; case "VarAddress": row.Cells[e.ColumnIndex].Value = config.VarAddress; break; case "DataType": row.Cells[e.ColumnIndex].Value = config.DataType; break; case "OffsetOrLength": row.Cells[e.ColumnIndex].Value = config.OffsetOrLength; break; case "Remark": row.Cells[e.ColumnIndex].Value = config.Remark; break; } // 刷新行 variableTable.InvalidateRow(e.RowIndex); variableTable.Update(); } } /// /// 更新配置值 /// private void UpdateConfigValue(VariableConfig config, string columnName, object value) { if (value == null && columnName != "Remark") { throw new Exception("值不能为空".Translated()); } string strValue = value?.ToString().Trim() ?? ""; switch (columnName) { case "GroupName": if (!groupNames.Contains(strValue)) { throw new Exception($"无效的组名,可选值:{string.Join(",", groupNames)}".Translated()); } config.GroupName = strValue; break; case "VarName": if (string.IsNullOrEmpty(strValue)) { throw new Exception("变量名不能为空".Translated()); } if (Variables.Any(v => v != config && v.PlcNum == config.PlcNum && v.VarName.Equals(strValue, StringComparison.OrdinalIgnoreCase))) { throw new Exception("变量名已存在".Translated()); } config.VarName = strValue; break; case "VarAddress": if (!ValidateVariableAddress(strValue)) { throw new Exception("地址格式不正确,应以'D'开头后跟数字".Translated()); } config.VarAddress = strValue; // 自动更新Start列 if (strValue.StartsWith("D") && int.TryParse(strValue.Substring(1), out int startAddr)) { config.Start = startAddr.ToString(); } break; case "DataType": if (!ValidateDataType(strValue)) { throw new Exception("不支持的数据类型".Translated()); } config.DataType = strValue; break; case "OffsetOrLength": if (!int.TryParse(strValue, out int length) || length <= 0) { throw new Exception("长度必须是大于0的数字".Translated()); } config.OffsetOrLength = length; break; case "Remark": config.Remark = strValue; break; } } /// /// 按钮点击事件 /// private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex < 0 || e.RowIndex >= variableTable.Rows.Count) return; var row = variableTable.Rows[e.RowIndex]; if (row.DataBoundItem is not VariableConfig config) return; // 编辑按钮 if (variableTable.Columns[e.ColumnIndex].Name == "Edit") { HandleEdit(config); } // 删除按钮 else if (variableTable.Columns[e.ColumnIndex].Name == "Delete") { HandleDelete(config); } } /// /// 处理单行编辑保存 /// private void HandleEdit(VariableConfig config) { try { var result = MessageBox.Show("是否保存修改的配置?".Translated(), "确认保存".Translated(), MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result != DialogResult.Yes) return; if (!ValidateConfigAll(config)) return; // 找到原始数据并更新 var originalIndex = Variables.FindIndex(v => v.PlcNum == config.PlcNum && v.VarAddress == config.VarAddress && v.VarName == config.VarName); if (originalIndex >= 0) { Variables[originalIndex] = config; } else { Variables.Add(config); } SaveConfigToExcel(); LoadVariables(); ShowSuccess("配置保存成功".Translated()); } catch (Exception ex) { ShowError($"保存失败: {ex.Message}"); } } /// /// 处理单行删除 /// private void HandleDelete(VariableConfig config) { var result = MessageBox.Show( $"确定要删除该变量吗?\n PLC编号: [{config.PlcNum}],通信组: [{config.GroupName}],变量名: [{config.VarName}]", "确认删除", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { try { // 从列表中移除 Variables.RemoveAll(v => v.PlcNum == config.PlcNum && v.VarAddress == config.VarAddress && v.VarName == config.VarName); SaveConfigToExcel(); LoadVariables(); ShowSuccess("删除成功".Translated()); } catch (Exception ex) { ShowError($"删除失败: {ex.Message}"); } } } /// /// 保存配置到Excel /// private void SaveConfigToExcel() { try { // 确保目录存在 string configDir = Path.GetDirectoryName(variableConfigPath); if (!Directory.Exists(configDir)) { Directory.CreateDirectory(configDir); } string tempFilePath = Path.Combine(configDir, $"temp_{Guid.NewGuid()}_{Path.GetFileName(variableConfigPath)}"); // 保存到临时文件 MiniExcel.SaveAs(tempFilePath, Variables); if (!File.Exists(tempFilePath)) { throw new Exception("创建临时文件失败".Translated()); } // 替换原文件(处理文件占用) File.Copy(tempFilePath, variableConfigPath, true); // 删除临时文件 if (File.Exists(tempFilePath)) { File.Delete(tempFilePath); } } catch (Exception ex) { throw new Exception($"保存Excel文件时发生错误: {ex.Message}", ex); } } /// /// 添加新变量 /// private void BtnAdd_Click(object sender, EventArgs e) { try { if (groupNames.Count == 0) { ShowError("没有可用的组名,请先在Group.xlsx中添加组".Translated()); return; } string varName = GenerateNewVarName(); string varAddress = FindNextAvailableAddress(); int startAddr = int.Parse(varAddress.Substring(1)); var newVariable = new VariableConfig { VarName = varName, VarAddress = varAddress, Start = startAddr.ToString(), DataType = "Short", OffsetOrLength = 1, PlcNum = PlcNum, GroupName = groupNames[0], Remark = "" }; if (!ValidateConfigAll(newVariable)) { return; } Variables.Add(newVariable); SaveConfigToExcel(); LoadVariables(); ShowSuccess("新建变量成功".Translated()); } catch (Exception ex) { ShowError($"添加变量失败: {ex.Message}"); } } /// /// 生成新变量名 /// private string GenerateNewVarName() { int index = 1; string baseName = "新变量".Translated(); string newName = baseName; while (Variables.Any(v => v.PlcNum == PlcNum && v.VarName.Equals(newName, StringComparison.OrdinalIgnoreCase))) { index++; newName = $"{baseName}{index}"; } return newName; } /// /// 获取下一个可用地址 /// private string FindNextAvailableAddress() { var plcVariables = Variables.Where(v => v.PlcNum == PlcNum).ToList(); if (!plcVariables.Any()) { return "D0"; } int maxAddress = plcVariables.Max(v => { if (v.VarAddress.StartsWith("D") && int.TryParse(v.VarAddress.Substring(1), out int addr)) { return addr + v.OffsetOrLength; } return 0; }); if (maxAddress >= 32675) { throw new Exception("没有可用的地址空间".Translated()); } return $"D{maxAddress}"; } /// /// 验证单个变量配置 /// private bool ValidateConfigAll(VariableConfig config) { try { if (string.IsNullOrWhiteSpace(config.VarName)) { ShowError("变量名不能为空".Translated()); return false; } if (!ValidateVariableAddress(config.VarAddress)) { ShowError("变量地址格式不正确".Translated()); return false; } if (!ValidateStartAddress(config.Start)) { ShowError("起始地址格式不正确".Translated()); return false; } if (!ValidateDataType(config.DataType)) { ShowError("不支持的数据类型".Translated()); return false; } if (config.OffsetOrLength <= 0) { ShowError("长度必须大于0".Translated()); return false; } // 检查重复 if (Variables.Any(v => v != config && v.PlcNum == config.PlcNum && v.VarName.Equals(config.VarName, StringComparison.OrdinalIgnoreCase))) { ShowError($"变量名 {config.VarName} 已存在"); return false; } if (Variables.Any(v => v != config && v.PlcNum == config.PlcNum && v.VarAddress.Equals(config.VarAddress, StringComparison.OrdinalIgnoreCase))) { ShowError($"变量地址 {config.VarAddress} 已存在"); return false; } // 地址范围验证 if (!int.TryParse(config.Start, out int startAddress)) { ShowError("无效的起始地址".Translated()); return false; } int endAddress = startAddress + config.OffsetOrLength/2 - 1; if (endAddress > 32767) { ShowError($"地址范围超出限制,结束地址不能超过32767".Translated()); return false; } // 检查地址冲突 foreach (var other in Variables.Where(v => v != config && v.PlcNum == config.PlcNum)) { if (int.TryParse(other.Start, out int otherStart)) { int otherEnd = otherStart + other.OffsetOrLength/2 - 1; if (!(endAddress < otherStart || startAddress > otherEnd)) { ShowError($"地址范围与变量 \"{other.VarName}\" 冲突"); return false; } } } return true; } catch (Exception ex) { LogHelper.Instance?.WriteError($"验证变量配置时发生错误: {ex}"); ShowError($"验证配置时发生错误: {ex.Message}".Translated()); return false; } } /// /// 批量验证表格中所有变量配置 /// private bool ValidateAllVariablesInGrid() { try { if (variableTable.DataSource is not List gridVariables) { ShowError("表格中无数据可保存".Translated()); return false; } // 逐个验证表格中的变量 foreach (var config in gridVariables) { if (!ValidateConfigAll(config)) { return false; } } return true; } catch (Exception ex) { ShowError($"批量验证失败: {ex.Message}"); return false; } } /// /// 验证变量地址 /// private bool ValidateVariableAddress(string address) { if (string.IsNullOrWhiteSpace(address)) return false; if (!address.StartsWith("D", StringComparison.OrdinalIgnoreCase)) { return false; } string numberPart = address.Substring(1); if (!int.TryParse(numberPart, out int dAddress) || dAddress < 0 || dAddress > 32767) { return false; } return true; } /// /// 验证起始地址 /// private bool ValidateStartAddress(string startAddress) { if (string.IsNullOrWhiteSpace(startAddress)) return false; if (!int.TryParse(startAddress, out int addressNum)) return false; return addressNum >= 0 && addressNum <= 32767; } /// /// 验证数据类型 /// private bool ValidateDataType(string dataType) { if (string.IsNullOrWhiteSpace(dataType)) return false; string[] validTypes = new[] { "Bit", "Byte", "ByteArray", "Short", "Arrshort", "Word", "DWord", "Int", "DInt", "Real", "String" }; return validTypes.Contains(dataType, StringComparer.OrdinalIgnoreCase); } /// /// 查找地址间隙 /// private string FindAddressGap(int requiredLength = 1) { var plcVariables = Variables .Where(v => v.PlcNum == PlcNum) .Select(v => new { Start = int.Parse(v.VarAddress.Substring(1)), End = int.Parse(v.VarAddress.Substring(1)) + v.OffsetOrLength - 1 }) .OrderBy(v => v.Start) .ToList(); if (!plcVariables.Any()) { return "D0"; } if (plcVariables.First().Start >= requiredLength) { return "D0"; } for (int i = 0; i < plcVariables.Count - 1; i++) { int gapStart = plcVariables[i].End + 1; int gapEnd = plcVariables[i + 1].Start - 1; if (gapEnd - gapStart + 1 >= requiredLength) { return $"D{gapStart}"; } } int lastEnd = plcVariables.Last().End; if (lastEnd + requiredLength <= 32675) { return $"D{lastEnd + 1}"; } throw new Exception("没有找到足够大的地址空间".Translated()); } // 错误提示 private void ShowError(string message) { MessageBox.Show(message, "验证错误".Translated(), MessageBoxButtons.OK, MessageBoxIcon.Error); } // 成功提示 private void ShowSuccess(string message) { MessageBox.Show(message, "成功".Translated(), MessageBoxButtons.OK, MessageBoxIcon.Information); } private void btn_Close_Click(object sender, EventArgs e) { this.Close(); } #region 减少闪烁 protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; return cp; } } #endregion #region 无边框拖动 private Point mPoint; private void Panel_MouseDown(object sender, MouseEventArgs e) { mPoint = new Point(e.X, e.Y); } private void Panel_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { this.Location = new Point(this.Location.X + e.X - mPoint.X, this.Location.Y + e.Y - mPoint.Y); } } #endregion /// /// 批量保存所有修改的变量配置 /// private void BtnSave_Click(object sender, EventArgs e) { try { var result = MessageBox.Show("是否保存所有修改的配置?".Translated(), "确认保存".Translated(), MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result != DialogResult.Yes) return; // 验证表格中所有变量 if (!ValidateAllVariablesInGrid()) { return; } // 同步表格数据到Variables列表 if (variableTable.DataSource is List gridVariables) { // 清空原有列表,替换为表格中的最新数据 Variables.Clear(); Variables.AddRange(gridVariables); } SaveConfigToExcel(); LoadVariables(); ShowSuccess("所有配置保存成功".Translated()); } catch (Exception ex) { ShowError($"批量保存失败: {ex.Message}"); } } } }