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);
}
}
}