using GSModbus.Config;
|
using GSModbus.Database;
|
|
namespace GSModbus
|
{
|
/// <summary>
|
/// 通用MES系统主窗体 - 基于配置的Modbus通信界面
|
/// </summary>
|
public partial class Form1 : Form
|
{
|
#region 私有字段
|
|
/// <summary>
|
/// 配置管理器
|
/// </summary>
|
private ConfigurationManager? _configManager;
|
|
/// <summary>
|
/// 通用Modbus通信管理器实例
|
/// </summary>
|
private UniversalModbusManager? _universalModbusManager;
|
|
/// <summary>
|
/// 数据接收计数器
|
/// </summary>
|
private int _dataReceiveCount = 0;
|
|
#endregion
|
|
#region 构造函数
|
|
/// <summary>
|
/// 初始化主窗体
|
/// </summary>
|
public Form1()
|
{
|
InitializeComponent();
|
InitializeConfigManager();
|
SetupEventHandlers();
|
UpdateConnectionStatus(false);
|
|
// 初始化数据库状态
|
lblDatabaseStatus.Text = "未检测";
|
lblDatabaseStatus.ForeColor = Color.Gray;
|
btnTestDatabase.Enabled = false;
|
|
// 尝试加载默认配置
|
LoadDefaultConfigurationAsync();
|
}
|
|
#endregion
|
|
#region 初始化方法
|
|
/// <summary>
|
/// 初始化配置管理器
|
/// </summary>
|
private void InitializeConfigManager()
|
{
|
try
|
{
|
_configManager = new ConfigurationManager();
|
|
// 订阅配置管理器的事件
|
_configManager.ConfigurationLoaded += OnConfigurationLoaded;
|
_configManager.ConfigurationError += OnConfigurationError;
|
|
LogMessage("配置管理器初始化完成");
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"初始化配置管理器失败: {ex.Message}");
|
MessageBox.Show($"初始化失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
}
|
}
|
|
/// <summary>
|
/// 设置控件事件处理程序
|
/// </summary>
|
private void SetupEventHandlers()
|
{
|
// 窗体关闭事件
|
this.FormClosing += Form1_FormClosing;
|
|
// 按钮事件
|
btnLoadConfig.Click += BtnLoadConfig_Click;
|
btnConnect.Click += BtnConnect_Click;
|
btnDisconnect.Click += BtnDisconnect_Click;
|
btnClearLog.Click += BtnClearLog_Click;
|
btnTestDatabase.Click += BtnTestDatabase_Click;
|
}
|
|
#endregion
|
|
#region 配置管理方法
|
|
/// <summary>
|
/// 加载默认配置
|
/// </summary>
|
private async void LoadDefaultConfigurationAsync()
|
{
|
if (_configManager == null) return;
|
|
try
|
{
|
LogMessage("正在尝试加载默认配置文件...");
|
var loaded = await _configManager.LoadDefaultConfigurationAsync();
|
|
if (!loaded)
|
{
|
LogMessage("默认配置文件不存在,请手动选择配置文件");
|
lblConfigPath.Text = "未加载配置文件";
|
lblConfigPath.ForeColor = Color.Red;
|
}
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"加载默认配置失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 配置加载成功事件处理
|
/// </summary>
|
private void OnConfigurationLoaded(object? sender, ConfigurationLoadedEventArgs e)
|
{
|
// 确保在UI线程中执行
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => OnConfigurationLoaded(sender, e)));
|
return;
|
}
|
|
try
|
{
|
// 更新配置显示
|
var fileName = Path.GetFileName(e.ConfigPath);
|
lblConfigPath.Text = fileName;
|
lblConfigPath.ForeColor = Color.Green;
|
|
// 更新窗体标题
|
this.Text = $"GSModbus - {e.Configuration.ProjectName}";
|
|
// 如果有现有的连接,先断开
|
if (_universalModbusManager?.IsConnected == true)
|
{
|
_universalModbusManager.Disconnect();
|
}
|
|
// 释放现有的管理器
|
_universalModbusManager?.Dispose();
|
|
// 创建新的通用Modbus管理器
|
_universalModbusManager = new UniversalModbusManager(e.Configuration);
|
SetupModbusManagerEvents();
|
|
LogMessage($"✅ 配置加载成功: {e.Configuration.ProjectName}");
|
LogMessage($" PLC地址: {e.Configuration.Connection.IpAddress}:{e.Configuration.Connection.Port}");
|
LogMessage($" 数据字段: {GetTotalFieldCount(e.Configuration)} 个");
|
|
// 更新数据库状态
|
UpdateDatabaseStatus(e.Configuration.Database);
|
|
// 启用连接按钮
|
btnConnect.Enabled = true;
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"处理配置加载事件时发生错误: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 配置错误事件处理
|
/// </summary>
|
private void OnConfigurationError(object? sender, ConfigurationErrorEventArgs e)
|
{
|
// 确保在UI线程中执行
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => OnConfigurationError(sender, e)));
|
return;
|
}
|
|
lblConfigPath.Text = "配置文件错误";
|
lblConfigPath.ForeColor = Color.Red;
|
btnConnect.Enabled = false;
|
|
// 重置数据库状态
|
lblDatabaseStatus.Text = "未检测";
|
lblDatabaseStatus.ForeColor = Color.Gray;
|
btnTestDatabase.Enabled = false;
|
|
LogMessage($"❌ 配置错误: {e.ErrorMessage}");
|
}
|
|
/// <summary>
|
/// 设置Modbus管理器事件
|
/// </summary>
|
private void SetupModbusManagerEvents()
|
{
|
if (_universalModbusManager == null) return;
|
|
_universalModbusManager.ConnectionStatusChanged += OnConnectionStatusChanged;
|
_universalModbusManager.DataReceived += OnDataReceived;
|
_universalModbusManager.ErrorOccurred += OnErrorOccurred;
|
_universalModbusManager.StatsUpdated += OnStatsUpdated;
|
_universalModbusManager.DatabaseLogOccurred += OnDatabaseLogOccurred;
|
_universalModbusManager.DatabaseErrorOccurred += OnDatabaseErrorOccurred;
|
}
|
|
#endregion
|
|
#region 事件处理方法
|
|
/// <summary>
|
/// 选择配置文件按钮点击事件
|
/// </summary>
|
private async void BtnLoadConfig_Click(object? sender, EventArgs e)
|
{
|
try
|
{
|
var openFileDialog = new OpenFileDialog
|
{
|
Title = "选择Modbus配置文件",
|
Filter = "JSON配置文件 (*.json)|*.json|所有文件 (*.*)|*.*",
|
InitialDirectory = Path.Combine(Application.StartupPath, "config"),
|
RestoreDirectory = true
|
};
|
|
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
{
|
LogMessage($"正在加载配置文件: {openFileDialog.FileName}");
|
|
if (_configManager != null)
|
{
|
await _configManager.LoadConfigurationAsync(openFileDialog.FileName);
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"选择配置文件时发生错误: {ex.Message}");
|
MessageBox.Show($"加载配置文件失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
}
|
}
|
|
/// <summary>
|
/// 连接按钮点击事件
|
/// </summary>
|
private async void BtnConnect_Click(object? sender, EventArgs e)
|
{
|
if (_universalModbusManager == null)
|
{
|
LogMessage("❌ 请先加载配置文件");
|
MessageBox.Show("请先选择并加载配置文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
return;
|
}
|
|
try
|
{
|
btnConnect.Enabled = false;
|
btnConnect.Text = "连接中...";
|
|
LogMessage("🔗 正在连接到PLC...");
|
|
bool connected = await _universalModbusManager.ConnectAsync();
|
|
if (!connected)
|
{
|
LogMessage("❌ 连接PLC失败");
|
MessageBox.Show("无法连接到PLC,请检查网络连接和PLC状态", "连接失败",
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
}
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"❌ 连接过程中发生错误: {ex.Message}");
|
MessageBox.Show($"连接错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
}
|
finally
|
{
|
btnConnect.Text = "连接PLC";
|
btnConnect.Enabled = !(_universalModbusManager?.IsConnected ?? false);
|
}
|
}
|
|
/// <summary>
|
/// 断开连接按钮点击事件
|
/// </summary>
|
private void BtnDisconnect_Click(object? sender, EventArgs e)
|
{
|
try
|
{
|
LogMessage("🔌 正在断开PLC连接...");
|
_universalModbusManager?.Disconnect();
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"❌ 断开连接时发生错误: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 清除日志按钮点击事件
|
/// </summary>
|
private void BtnClearLog_Click(object? sender, EventArgs e)
|
{
|
txtLog.Clear();
|
_dataReceiveCount = 0;
|
lblDataCount.Text = "数据接收次数: 0";
|
}
|
|
/// <summary>
|
/// 数据库测试按钮点击事件
|
/// </summary>
|
private async void BtnTestDatabase_Click(object? sender, EventArgs e)
|
{
|
if (_configManager?.CurrentConfiguration?.Database == null)
|
{
|
LogMessage("❌ 请先加载包含数据库配置的配置文件");
|
MessageBox.Show("请先选择并加载包含数据库配置的配置文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
return;
|
}
|
|
try
|
{
|
btnTestDatabase.Enabled = false;
|
btnTestDatabase.Text = "测试中...";
|
lblDatabaseStatus.Text = "测试中...";
|
lblDatabaseStatus.ForeColor = Color.Orange;
|
|
LogMessage("🔍 正在测试数据库连接...");
|
|
var dbConfig = _configManager.CurrentConfiguration.Database;
|
var (success, message) = await DatabaseManager.TestConnectionAsync(dbConfig);
|
|
if (success)
|
{
|
lblDatabaseStatus.Text = "连接成功";
|
lblDatabaseStatus.ForeColor = Color.Green;
|
LogMessage($"✅ 数据库连接测试成功");
|
LogMessage($" {message}");
|
}
|
else
|
{
|
lblDatabaseStatus.Text = "连接失败";
|
lblDatabaseStatus.ForeColor = Color.Red;
|
LogMessage($"❌ 数据库连接测试失败: {message}");
|
}
|
}
|
catch (Exception ex)
|
{
|
lblDatabaseStatus.Text = "测试异常";
|
lblDatabaseStatus.ForeColor = Color.Red;
|
LogMessage($"❌ 数据库连接测试异常: {ex.Message}");
|
MessageBox.Show($"数据库连接测试异常: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
}
|
finally
|
{
|
btnTestDatabase.Text = "测试数据库";
|
btnTestDatabase.Enabled = true;
|
}
|
}
|
|
/// <summary>
|
/// 窗体关闭事件 - 清理资源
|
/// </summary>
|
private void Form1_FormClosing(object? sender, FormClosingEventArgs e)
|
{
|
try
|
{
|
LogMessage("正在关闭应用程序...");
|
_universalModbusManager?.Dispose();
|
}
|
catch (Exception ex)
|
{
|
// 关闭时的错误不需要显示给用户
|
System.Diagnostics.Debug.WriteLine($"关闭时发生错误: {ex.Message}");
|
}
|
}
|
|
#endregion
|
|
#region Modbus事件处理
|
|
/// <summary>
|
/// Modbus连接状态改变事件处理
|
/// </summary>
|
private void OnConnectionStatusChanged(object? sender, bool isConnected)
|
{
|
// 由于此事件可能在非UI线程中触发,需要使用Invoke来更新UI
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => UpdateConnectionStatus(isConnected)));
|
}
|
else
|
{
|
UpdateConnectionStatus(isConnected);
|
}
|
}
|
|
/// <summary>
|
/// 接收到动态数据事件处理 - 通过日志显示
|
/// </summary>
|
private void OnDataReceived(object? sender, DynamicModbusData data)
|
{
|
// 由于此事件可能在非UI线程中触发,需要使用Invoke来更新UI
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => DisplayDataInLog(data)));
|
}
|
else
|
{
|
DisplayDataInLog(data);
|
}
|
}
|
|
/// <summary>
|
/// Modbus错误发生事件处理
|
/// </summary>
|
private void OnErrorOccurred(object? sender, string errorMessage)
|
{
|
// 由于此事件可能在非UI线程中触发,需要使用Invoke来更新UI
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => LogMessage($"⚠️ 通信错误: {errorMessage}")));
|
}
|
else
|
{
|
LogMessage($"⚠️ 通信错误: {errorMessage}");
|
}
|
}
|
|
/// <summary>
|
/// 统计信息更新事件处理
|
/// </summary>
|
private void OnStatsUpdated(object? sender, CommunicationStats stats)
|
{
|
// 由于此事件可能在非UI线程中触发,需要使用Invoke来更新UI
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => UpdateStatsDisplay(stats)));
|
}
|
else
|
{
|
UpdateStatsDisplay(stats);
|
}
|
}
|
|
/// <summary>
|
/// 数据库日志事件处理
|
/// </summary>
|
private void OnDatabaseLogOccurred(object? sender, string logMessage)
|
{
|
// 由于此事件可能在非UI线程中触发,需要使用Invoke来更新UI
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => LogMessage(logMessage)));
|
}
|
else
|
{
|
LogMessage(logMessage);
|
}
|
}
|
|
/// <summary>
|
/// 数据库错误事件处理
|
/// </summary>
|
private void OnDatabaseErrorOccurred(object? sender, string errorMessage)
|
{
|
// 由于此事件可能在非UI线程中触发,需要使用Invoke来更新UI
|
if (InvokeRequired)
|
{
|
Invoke(new Action(() => LogMessage($"⚠️ {errorMessage}")));
|
}
|
else
|
{
|
LogMessage($"⚠️ {errorMessage}");
|
}
|
}
|
|
#endregion
|
|
#region UI更新方法
|
|
/// <summary>
|
/// 更新连接状态显示
|
/// </summary>
|
/// <param name="isConnected">是否已连接</param>
|
private void UpdateConnectionStatus(bool isConnected)
|
{
|
if (isConnected)
|
{
|
lblConnectionStatus.Text = "已连接";
|
lblConnectionStatus.ForeColor = Color.Green;
|
btnConnect.Enabled = false;
|
btnDisconnect.Enabled = true;
|
LogMessage("✅ 成功连接到PLC,开始数据通信");
|
}
|
else
|
{
|
lblConnectionStatus.Text = "未连接";
|
lblConnectionStatus.ForeColor = Color.Red;
|
btnConnect.Enabled = _configManager?.IsConfigurationLoaded ?? false;
|
btnDisconnect.Enabled = false;
|
|
// 重置计数
|
_dataReceiveCount = 0;
|
lblDataCount.Text = "数据接收次数: 0";
|
}
|
}
|
|
/// <summary>
|
/// 在日志中显示动态数据
|
/// </summary>
|
/// <param name="data">动态数据</param>
|
private void DisplayDataInLog(DynamicModbusData data)
|
{
|
if (data == null) return;
|
|
try
|
{
|
_dataReceiveCount++;
|
lblDataCount.Text = $"数据接收次数: {_dataReceiveCount}";
|
|
// 构建数据日志条目
|
var logEntry = new List<string>
|
{
|
$"📊 [数据 #{_dataReceiveCount}] {data.ReadTime:HH:mm:ss.fff}"
|
};
|
|
// 按类别显示数据
|
var allFields = GetAllFieldsByCategory(data);
|
|
foreach (var category in allFields)
|
{
|
if (category.Value.Any())
|
{
|
logEntry.Add($" {category.Key}:");
|
foreach (var field in category.Value)
|
{
|
var config = data.GetFieldConfiguration(field.Key);
|
var displayValue = config != null ?
|
ModbusDataParser.FormatDisplayValue(field.Value, config) :
|
field.Value?.ToString() ?? "null";
|
|
logEntry.Add($" • {config?.DisplayName ?? field.Key}: {displayValue}");
|
}
|
}
|
}
|
|
// 添加分隔线
|
logEntry.Add(" " + new string('-', 50));
|
|
// 批量添加到日志
|
foreach (var line in logEntry)
|
{
|
AppendLogMessage(line);
|
}
|
}
|
catch (Exception ex)
|
{
|
LogMessage($"❌ 显示数据时发生错误: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 更新统计信息显示
|
/// </summary>
|
/// <param name="stats">统计信息</param>
|
private void UpdateStatsDisplay(CommunicationStats stats)
|
{
|
lblStats.Text = stats.GetSummary();
|
}
|
|
/// <summary>
|
/// 更新数据库状态显示
|
/// </summary>
|
/// <param name="dbConfig">数据库配置</param>
|
private void UpdateDatabaseStatus(DatabaseConfig? dbConfig)
|
{
|
if (dbConfig == null)
|
{
|
lblDatabaseStatus.Text = "未配置";
|
lblDatabaseStatus.ForeColor = Color.Gray;
|
btnTestDatabase.Enabled = false;
|
LogMessage(" 数据库: 未配置");
|
}
|
else if (!dbConfig.Enabled)
|
{
|
lblDatabaseStatus.Text = "已禁用";
|
lblDatabaseStatus.ForeColor = Color.Gray;
|
btnTestDatabase.Enabled = false;
|
LogMessage(" 数据库: 已禁用");
|
}
|
else
|
{
|
lblDatabaseStatus.Text = "未检测";
|
lblDatabaseStatus.ForeColor = Color.Orange;
|
btnTestDatabase.Enabled = true;
|
LogMessage($" 数据库: {dbConfig.Type} - 已配置,可测试连接");
|
}
|
}
|
|
#endregion
|
|
#region 辅助方法
|
|
/// <summary>
|
/// 按类别获取所有字段数据
|
/// </summary>
|
private Dictionary<string, Dictionary<string, object?>> GetAllFieldsByCategory(DynamicModbusData data)
|
{
|
var result = new Dictionary<string, Dictionary<string, object?>>
|
{
|
["控制信号"] = new Dictionary<string, object?>(),
|
["产品数据"] = new Dictionary<string, object?>(),
|
["测量数据"] = new Dictionary<string, object?>()
|
};
|
|
if (data.SourceConfiguration?.InputAddresses == null)
|
return result;
|
|
// 控制信号
|
foreach (var field in data.SourceConfiguration.InputAddresses.ControlSignals)
|
{
|
if (data.HasField(field.Key))
|
{
|
result["控制信号"][field.Key] = data.GetFieldValue(field.Key);
|
}
|
}
|
|
// 产品数据
|
foreach (var field in data.SourceConfiguration.InputAddresses.ProductData)
|
{
|
if (data.HasField(field.Key))
|
{
|
result["产品数据"][field.Key] = data.GetFieldValue(field.Key);
|
}
|
}
|
|
// 测量数据
|
foreach (var field in data.SourceConfiguration.InputAddresses.MeasurementData)
|
{
|
if (data.HasField(field.Key))
|
{
|
result["测量数据"][field.Key] = data.GetFieldValue(field.Key);
|
}
|
}
|
|
return result;
|
}
|
|
/// <summary>
|
/// 获取配置中的总字段数
|
/// </summary>
|
private int GetTotalFieldCount(ModbusConfiguration config)
|
{
|
return config.InputAddresses.ControlSignals.Count +
|
config.InputAddresses.ProductData.Count +
|
config.InputAddresses.MeasurementData.Count;
|
}
|
|
/// <summary>
|
/// 记录日志消息
|
/// </summary>
|
/// <param name="message">日志消息</param>
|
private void LogMessage(string message)
|
{
|
string logEntry = $"[{DateTime.Now:HH:mm:ss}] {message}";
|
|
if (txtLog.InvokeRequired)
|
{
|
txtLog.Invoke(new Action(() => AppendLogMessage(logEntry)));
|
}
|
else
|
{
|
AppendLogMessage(logEntry);
|
}
|
}
|
|
/// <summary>
|
/// 添加日志消息到文本框
|
/// </summary>
|
private void AppendLogMessage(string logEntry)
|
{
|
txtLog.AppendText(logEntry + Environment.NewLine);
|
txtLog.SelectionStart = txtLog.Text.Length;
|
txtLog.ScrollToCaret();
|
|
// 限制日志行数,避免内存占用过大
|
var lines = txtLog.Lines;
|
if (lines.Length > 2000)
|
{
|
var newLines = lines.Skip(200).ToArray();
|
txtLog.Text = string.Join(Environment.NewLine, newLines);
|
}
|
}
|
|
#endregion
|
}
|
}
|