995 lines
34 KiB
C#
995 lines
34 KiB
C#
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<string> groupNames = new List<string>();
|
||
|
|
|
||
|
|
private List<VariableConfig> Variables = new List<VariableConfig>();
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 处理DataGridView数据错误,避免弹出默认错误对话框
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 编辑控件显示时确保数据源正确
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 单元格值变更事件(自动更新Start列)
|
||
|
|
/// </summary>
|
||
|
|
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<string> GetGroupNames(int plcNum)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (!File.Exists(groupConfigPath))
|
||
|
|
{
|
||
|
|
// 创建默认组配置文件
|
||
|
|
Directory.CreateDirectory(Path.GetDirectoryName(groupConfigPath));
|
||
|
|
MiniExcel.SaveAs(groupConfigPath, new List<PlcGroupConfig> { new PlcGroupConfig { PlcNum = plcNum, GroupName = "默认组".Translated() } });
|
||
|
|
}
|
||
|
|
|
||
|
|
var groups = MiniExcel.Query<PlcGroupConfig>(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<string> { "默认组".Translated() };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取所有变量
|
||
|
|
/// </summary>
|
||
|
|
private List<VariableConfig> GetALLVariables()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (!File.Exists(variableConfigPath))
|
||
|
|
{
|
||
|
|
return new List<VariableConfig>();
|
||
|
|
}
|
||
|
|
|
||
|
|
var variables = MiniExcel.Query<VariableConfig>(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<VariableConfig>();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 加载当前PLC的变量到表格
|
||
|
|
/// </summary>
|
||
|
|
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}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 开始编辑单元格
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 结束编辑单元格
|
||
|
|
/// </summary>
|
||
|
|
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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 更新配置值
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 按钮点击事件
|
||
|
|
/// </summary>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 处理单行编辑保存
|
||
|
|
/// </summary>
|
||
|
|
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}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 处理单行删除
|
||
|
|
/// </summary>
|
||
|
|
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}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 保存配置到Excel
|
||
|
|
/// </summary>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 添加新变量
|
||
|
|
/// </summary>
|
||
|
|
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}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 生成新变量名
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取下一个可用地址
|
||
|
|
/// </summary>
|
||
|
|
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}";
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 验证单个变量配置
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 批量验证表格中所有变量配置
|
||
|
|
/// </summary>
|
||
|
|
private bool ValidateAllVariablesInGrid()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (variableTable.DataSource is not List<VariableConfig> 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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 验证变量地址
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 验证起始地址
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 验证数据类型
|
||
|
|
/// </summary>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 查找地址间隙
|
||
|
|
/// </summary>
|
||
|
|
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
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 批量保存所有修改的变量配置
|
||
|
|
/// </summary>
|
||
|
|
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<VariableConfig> gridVariables)
|
||
|
|
{
|
||
|
|
// 清空原有列表,替换为表格中的最新数据
|
||
|
|
Variables.Clear();
|
||
|
|
Variables.AddRange(gridVariables);
|
||
|
|
}
|
||
|
|
|
||
|
|
SaveConfigToExcel();
|
||
|
|
LoadVariables();
|
||
|
|
ShowSuccess("所有配置保存成功".Translated());
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
ShowError($"批量保存失败: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|