using System; using System.Windows.Forms; using System.Drawing; using CCDCount.DLL.CanBus; namespace CanOpenSlaveTest { /// /// CANopen从站设备测试界面 /// public class SlaveTestForm : Form { private CanOpenSlaveDevice m_slaveDevice; // UI控件 private GroupBox grpDeviceInfo; private Label lblNodeId; private Label lblState; private Label lblStatus; private GroupBox grpControl; private Button btnStart; private Button btnStop; private Button btnSendTpdo1; private Button btnSendTpdo2; private Button btnSendAllTpdos; private GroupBox grpHeartbeat; private NumericUpDown nudHeartbeatTime; private Button btnConfigureHeartbeat; private TextBox txtLog; private System.Windows.Forms.Timer m_updateTimer; public SlaveTestForm() { InitializeComponent(); // 在构造函数中创建从站设备(避免设计器错误) if (!DesignMode) { m_slaveDevice = new CanOpenSlaveDevice(nodeId: 1); // 注册事件 m_slaveDevice.OnRpdoReceived += SlaveDevice_OnRpdoReceived; m_slaveDevice.OnNmtStateChanged += SlaveDevice_OnNmtStateChanged; m_slaveDevice.OnSdoReadRequest += SlaveDevice_OnSdoReadRequest; m_slaveDevice.OnSdoWriteRequest += SlaveDevice_OnSdoWriteRequest; // 初始化日志 AppendLog("CANopen从站测试程序已启动"); AppendLog("节点ID: " + m_slaveDevice.NodeId.ToString()); AppendLog("点击'启动从站'按钮开始测试\n"); } } private void InitializeComponent() { this.Text = "CANopen从站设备测试"; this.Size = new Size(800, 600); this.StartPosition = FormStartPosition.CenterScreen; // 设备信息组 grpDeviceInfo = new GroupBox(); grpDeviceInfo.Text = "设备信息"; grpDeviceInfo.Location = new Point(10, 10); grpDeviceInfo.Size = new Size(380, 100); lblNodeId = new Label(); lblNodeId.Text = "节点ID: -"; lblNodeId.Location = new Point(10, 25); lblNodeId.AutoSize = true; lblState = new Label(); lblState.Text = "状态: -"; lblState.Location = new Point(10, 50); lblState.AutoSize = true; lblStatus = new Label(); lblStatus.Text = "运行状态: 未启动"; lblStatus.Location = new Point(10, 75); lblStatus.AutoSize = true; grpDeviceInfo.Controls.AddRange(new Control[] { lblNodeId, lblState, lblStatus }); // 控制组 grpControl = new GroupBox(); grpControl.Text = "控制"; grpControl.Location = new Point(400, 10); grpControl.Size = new Size(380, 100); btnStart = new Button(); btnStart.Text = "启动从站"; btnStart.Location = new Point(10, 25); btnStart.Size = new Size(100, 30); btnStart.Click += BtnStart_Click; btnStop = new Button(); btnStop.Text = "停止从站"; btnStop.Location = new Point(120, 25); btnStop.Size = new Size(100, 30); btnStop.Click += BtnStop_Click; btnStop.Enabled = false; btnSendTpdo1 = new Button(); btnSendTpdo1.Text = "发送TPDO1"; btnSendTpdo1.Location = new Point(10, 60); btnSendTpdo1.Size = new Size(100, 30); btnSendTpdo1.Click += BtnSendTpdo1_Click; btnSendTpdo1.Enabled = false; btnSendTpdo2 = new Button(); btnSendTpdo2.Text = "发送TPDO2"; btnSendTpdo2.Location = new Point(120, 60); btnSendTpdo2.Size = new Size(100, 30); btnSendTpdo2.Click += BtnSendTpdo2_Click; btnSendTpdo2.Enabled = false; btnSendAllTpdos = new Button(); btnSendAllTpdos.Text = "发送所有TPDO"; btnSendAllTpdos.Location = new Point(230, 60); btnSendAllTpdos.Size = new Size(120, 30); btnSendAllTpdos.Click += BtnSendAllTpdos_Click; btnSendAllTpdos.Enabled = false; grpControl.Controls.AddRange(new Control[] { btnStart, btnStop, btnSendTpdo1, btnSendTpdo2, btnSendAllTpdos }); // 心跳配置组 grpHeartbeat = new GroupBox(); grpHeartbeat.Text = "心跳配置"; grpHeartbeat.Location = new Point(10, 120); grpHeartbeat.Size = new Size(770, 60); Label lblHeartbeat = new Label(); lblHeartbeat.Text = "心跳时间(ms):"; lblHeartbeat.Location = new Point(10, 25); lblHeartbeat.AutoSize = true; nudHeartbeatTime = new NumericUpDown(); nudHeartbeatTime.Location = new Point(110, 22); nudHeartbeatTime.Size = new Size(100, 25); nudHeartbeatTime.Minimum = 0; nudHeartbeatTime.Maximum = 10000; nudHeartbeatTime.Value = 100; btnConfigureHeartbeat = new Button(); btnConfigureHeartbeat.Text = "配置心跳"; btnConfigureHeartbeat.Location = new Point(220, 20); btnConfigureHeartbeat.Size = new Size(100, 30); btnConfigureHeartbeat.Click += BtnConfigureHeartbeat_Click; btnConfigureHeartbeat.Enabled = false; grpHeartbeat.Controls.AddRange(new Control[] { lblHeartbeat, nudHeartbeatTime, btnConfigureHeartbeat }); // 日志显示 txtLog = new TextBox(); txtLog.Multiline = true; txtLog.ScrollBars = ScrollBars.Vertical; txtLog.ReadOnly = true; txtLog.Location = new Point(10, 190); txtLog.Size = new Size(770, 360); // 使用支持中文的字体(微软雅黑或宋体) txtLog.Font = new Font("Microsoft YaHei", 9); txtLog.ForeColor = Color.Black; txtLog.BackColor = Color.White; // 添加控件到窗体 this.Controls.AddRange(new Control[] { grpDeviceInfo, grpControl, grpHeartbeat, txtLog }); // 更新定时器 m_updateTimer = new System.Windows.Forms.Timer(); m_updateTimer.Interval = 500; m_updateTimer.Tick += UpdateTimer_Tick; m_updateTimer.Start(); } private void BtnStart_Click(object sender, EventArgs e) { try { AppendLog("正在启动从站..."); if (m_slaveDevice.Start(CanBaudRate.BaudRate_1M)) { AppendLog("✓ 从站启动成功"); btnStart.Enabled = false; btnStop.Enabled = true; btnSendTpdo1.Enabled = true; btnSendTpdo2.Enabled = true; btnSendAllTpdos.Enabled = true; btnConfigureHeartbeat.Enabled = true; UpdateDeviceInfo(); } else { AppendLog("✗ 从站启动失败"); MessageBox.Show("启动失败!请检查CAN设备连接。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception ex) { AppendLog($"✗ 启动错误: {ex.Message}"); MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void BtnStop_Click(object sender, EventArgs e) { try { AppendLog("正在停止从站..."); m_slaveDevice.Stop(); AppendLog("✓ 从站已停止"); btnStart.Enabled = true; btnStop.Enabled = false; btnSendTpdo1.Enabled = false; btnSendTpdo2.Enabled = false; btnSendAllTpdos.Enabled = false; btnConfigureHeartbeat.Enabled = false; UpdateDeviceInfo(); } catch (Exception ex) { AppendLog($"✗ 停止错误: {ex.Message}"); } } private void BtnSendTpdo1_Click(object sender, EventArgs e) { try { m_slaveDevice.SendTpdo(1); AppendLog("→ 已发送TPDO1"); } catch (Exception ex) { AppendLog($"✗ 发送TPDO1失败: {ex.Message}"); } } private void BtnSendTpdo2_Click(object sender, EventArgs e) { try { m_slaveDevice.SendTpdo(2); AppendLog("→ 已发送TPDO2"); } catch (Exception ex) { AppendLog($"✗ 发送TPDO2失败: {ex.Message}"); } } private void BtnSendAllTpdos_Click(object sender, EventArgs e) { try { m_slaveDevice.SendAllTpdos(); AppendLog("→ 已发送所有TPDO"); } catch (Exception ex) { AppendLog($"✗ 发送TPDO失败: {ex.Message}"); } } private void BtnConfigureHeartbeat_Click(object sender, EventArgs e) { try { ushort heartbeatTime = (ushort)nudHeartbeatTime.Value; m_slaveDevice.ConfigureHeartbeat(heartbeatTime); AppendLog($"✓ 心跳已配置: {heartbeatTime}ms"); } catch (Exception ex) { AppendLog($"✗ 配置心跳失败: {ex.Message}"); } } private void SlaveDevice_OnRpdoReceived(byte nodeId, byte pdoNumber, byte[] data) { if (this.InvokeRequired) { this.Invoke(new Action(() => SlaveDevice_OnRpdoReceived(nodeId, pdoNumber, data))); return; } string dataStr = BitConverter.ToString(data).Replace("-", " "); AppendLog($"← 收到RPDO{pdoNumber} [节点{nodeId}]: {dataStr}"); } private void SlaveDevice_OnNmtStateChanged(NmtState oldState, NmtState newState) { if (this.InvokeRequired) { this.Invoke(new Action(() => SlaveDevice_OnNmtStateChanged(oldState, newState))); return; } AppendLog($"⟳ NMT状态改变: {oldState} → {newState}"); UpdateDeviceInfo(); } private void SlaveDevice_OnSdoReadRequest(byte nodeId, ushort index, byte subIndex) { if (this.InvokeRequired) { this.Invoke(new Action(() => SlaveDevice_OnSdoReadRequest(nodeId, index, subIndex))); return; } AppendLog($"? SDO读取请求: 索引0x{index:X4}, 子索引{subIndex}"); } private void SlaveDevice_OnSdoWriteRequest(byte nodeId, ushort index, byte subIndex, uint value) { if (this.InvokeRequired) { this.Invoke(new Action(() => SlaveDevice_OnSdoWriteRequest(nodeId, index, subIndex, value))); return; } AppendLog($"↓ SDO写入请求: 索引0x{index:X4}, 子索引{subIndex}, 值0x{value:X8}"); } private void UpdateTimer_Tick(object sender, EventArgs e) { UpdateDeviceInfo(); } private void UpdateDeviceInfo() { if (this.InvokeRequired) { this.Invoke(new Action(UpdateDeviceInfo)); return; } lblNodeId.Text = $"节点ID: {m_slaveDevice.NodeId}"; lblState.Text = $"状态: {m_slaveDevice.CurrentState}"; lblStatus.Text = $"运行状态: {(m_slaveDevice.IsRunning ? "运行中" : "已停止")}"; } private void AppendLog(string message) { if (this.InvokeRequired) { this.Invoke(new Action(AppendLog), message); return; } string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); // 使用字符串拼接而非插值,确保兼容性 txtLog.AppendText("[" + timestamp + "] " + message + "\r\n"); // 自动滚动到底部 txtLog.SelectionStart = txtLog.Text.Length; txtLog.ScrollToCaret(); } protected override void OnFormClosing(FormClosingEventArgs e) { if (m_slaveDevice != null) { m_slaveDevice.Stop(); m_slaveDevice.Dispose(); } m_updateTimer?.Stop(); m_updateTimer?.Dispose(); base.OnFormClosing(e); } } }