using System;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using CCDCount.DLL.CanBus;
using CanTest;
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 GroupBox grpPdo1Send;
private Label lblPdo1Data0;
private Label lblPdo1Data1;
private Label lblPdo1Data2;
private Label lblPdo1Data3;
private Label lblPdo1Data4;
private Label lblPdo1Data5;
private Label lblPdo1Data6;
private Label lblPdo1Data7;
private TextBox txtPdo1Data0;
private TextBox txtPdo1Data1;
private TextBox txtPdo1Data2;
private TextBox txtPdo1Data3;
private TextBox txtPdo1Data4;
private TextBox txtPdo1Data5;
private TextBox txtPdo1Data6;
private TextBox txtPdo1Data7;
private Button btnSendTpdo1;
private Button btnRuleSend;
private Button btnContinuousSend;
// 连续发送相关控件
private Label lblPeriodTime;
private TextBox txtPeriodTime;
private Label lblPeriodUnit;
// 连续发送运行时长相关控件
private Label lblContinuousDuration;
private TextBox txtContinuousDuration;
private Label lblContinuousDurationUnit;
// 规则发送运行时长相关控件
private Label lblRuleDuration;
private TextBox txtRuleDuration;
private Label lblRuleDurationUnit;
private TextBox txtLog;
private System.ComponentModel.IContainer components;
private System.Windows.Forms.Timer m_updateTimer;
// 连续发送状态
private bool m_isContinuousSending = false;
private CancellationTokenSource m_continuousSendCts;
private long m_continuousSendCounter = 0;
// 连续发送运行时长相关
private DateTime m_continuousSendStartTime;
private int m_continuousSendDurationMs = 0;
// 规则发送状态
private bool m_isRuleSending = false;
private long m_ruleSendCounter = 0;
// 规则发送运行时长相关
private DateTime m_ruleSendStartTime;
private int m_ruleSendDurationMs = 0;
private CancellationTokenSource m_ruleSendCts;
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("心跳周期: 1000ms (固定值)");
AppendLog("点击'启动从站'按钮开始测试\n");
}
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.grpDeviceInfo = new System.Windows.Forms.GroupBox();
this.lblNodeId = new System.Windows.Forms.Label();
this.lblState = new System.Windows.Forms.Label();
this.lblStatus = new System.Windows.Forms.Label();
this.grpControl = new System.Windows.Forms.GroupBox();
this.btnStart = new System.Windows.Forms.Button();
this.btnStop = new System.Windows.Forms.Button();
this.grpPdo1Send = new System.Windows.Forms.GroupBox();
this.lblPdo1Data0 = new System.Windows.Forms.Label();
this.lblPdo1Data1 = new System.Windows.Forms.Label();
this.lblPdo1Data2 = new System.Windows.Forms.Label();
this.lblPdo1Data3 = new System.Windows.Forms.Label();
this.lblPdo1Data4 = new System.Windows.Forms.Label();
this.lblPdo1Data5 = new System.Windows.Forms.Label();
this.lblPdo1Data6 = new System.Windows.Forms.Label();
this.lblPdo1Data7 = new System.Windows.Forms.Label();
this.txtPdo1Data0 = new System.Windows.Forms.TextBox();
this.txtPdo1Data1 = new System.Windows.Forms.TextBox();
this.txtPdo1Data2 = new System.Windows.Forms.TextBox();
this.txtPdo1Data3 = new System.Windows.Forms.TextBox();
this.txtPdo1Data4 = new System.Windows.Forms.TextBox();
this.txtPdo1Data5 = new System.Windows.Forms.TextBox();
this.txtPdo1Data6 = new System.Windows.Forms.TextBox();
this.txtPdo1Data7 = new System.Windows.Forms.TextBox();
this.btnSendTpdo1 = new System.Windows.Forms.Button();
this.btnRuleSend = new System.Windows.Forms.Button();
this.btnContinuousSend = new System.Windows.Forms.Button();
this.lblPeriodTime = new System.Windows.Forms.Label();
this.txtPeriodTime = new System.Windows.Forms.TextBox();
this.lblPeriodUnit = new System.Windows.Forms.Label();
this.lblContinuousDuration = new System.Windows.Forms.Label();
this.txtContinuousDuration = new System.Windows.Forms.TextBox();
this.lblContinuousDurationUnit = new System.Windows.Forms.Label();
this.lblRuleDuration = new System.Windows.Forms.Label();
this.txtRuleDuration = new System.Windows.Forms.TextBox();
this.lblRuleDurationUnit = new System.Windows.Forms.Label();
this.txtLog = new System.Windows.Forms.TextBox();
this.m_updateTimer = new System.Windows.Forms.Timer(this.components);
this.grpDeviceInfo.SuspendLayout();
this.grpControl.SuspendLayout();
this.grpPdo1Send.SuspendLayout();
this.SuspendLayout();
//
// grpDeviceInfo
//
this.grpDeviceInfo.Controls.Add(this.lblNodeId);
this.grpDeviceInfo.Controls.Add(this.lblState);
this.grpDeviceInfo.Controls.Add(this.lblStatus);
this.grpDeviceInfo.Location = new System.Drawing.Point(10, 10);
this.grpDeviceInfo.Name = "grpDeviceInfo";
this.grpDeviceInfo.Size = new System.Drawing.Size(380, 100);
this.grpDeviceInfo.TabIndex = 0;
this.grpDeviceInfo.TabStop = false;
this.grpDeviceInfo.Text = "设备信息";
//
// lblNodeId
//
this.lblNodeId.AutoSize = true;
this.lblNodeId.Location = new System.Drawing.Point(10, 25);
this.lblNodeId.Name = "lblNodeId";
this.lblNodeId.Size = new System.Drawing.Size(77, 15);
this.lblNodeId.TabIndex = 0;
this.lblNodeId.Text = "节点ID: -";
//
// lblState
//
this.lblState.AutoSize = true;
this.lblState.Location = new System.Drawing.Point(10, 50);
this.lblState.Name = "lblState";
this.lblState.Size = new System.Drawing.Size(61, 15);
this.lblState.TabIndex = 1;
this.lblState.Text = "状态: -";
//
// lblStatus
//
this.lblStatus.AutoSize = true;
this.lblStatus.Location = new System.Drawing.Point(10, 75);
this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(128, 15);
this.lblStatus.TabIndex = 2;
this.lblStatus.Text = "运行状态: 未启动";
//
// grpControl
//
this.grpControl.Controls.Add(this.btnStart);
this.grpControl.Controls.Add(this.btnStop);
this.grpControl.Location = new System.Drawing.Point(400, 10);
this.grpControl.Name = "grpControl";
this.grpControl.Size = new System.Drawing.Size(380, 100);
this.grpControl.TabIndex = 1;
this.grpControl.TabStop = false;
this.grpControl.Text = "控制";
//
// btnStart
//
this.btnStart.Location = new System.Drawing.Point(10, 25);
this.btnStart.Name = "btnStart";
this.btnStart.Size = new System.Drawing.Size(100, 30);
this.btnStart.TabIndex = 0;
this.btnStart.Text = "启动从站";
this.btnStart.Click += new System.EventHandler(this.BtnStart_Click);
//
// btnStop
//
this.btnStop.Enabled = false;
this.btnStop.Location = new System.Drawing.Point(120, 25);
this.btnStop.Name = "btnStop";
this.btnStop.Size = new System.Drawing.Size(100, 30);
this.btnStop.TabIndex = 1;
this.btnStop.Text = "停止从站";
this.btnStop.Click += new System.EventHandler(this.BtnStop_Click);
//
// grpPdo1Send
//
this.grpPdo1Send.Controls.Add(this.lblPdo1Data0);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data1);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data2);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data3);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data4);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data5);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data6);
this.grpPdo1Send.Controls.Add(this.lblPdo1Data7);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data0);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data1);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data2);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data3);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data4);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data5);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data6);
this.grpPdo1Send.Controls.Add(this.txtPdo1Data7);
this.grpPdo1Send.Controls.Add(this.btnSendTpdo1);
this.grpPdo1Send.Controls.Add(this.btnRuleSend);
this.grpPdo1Send.Controls.Add(this.btnContinuousSend);
this.grpPdo1Send.Controls.Add(this.lblPeriodTime);
this.grpPdo1Send.Controls.Add(this.txtPeriodTime);
this.grpPdo1Send.Controls.Add(this.lblPeriodUnit);
this.grpPdo1Send.Controls.Add(this.lblContinuousDuration);
this.grpPdo1Send.Controls.Add(this.txtContinuousDuration);
this.grpPdo1Send.Controls.Add(this.lblContinuousDurationUnit);
this.grpPdo1Send.Controls.Add(this.lblRuleDuration);
this.grpPdo1Send.Controls.Add(this.txtRuleDuration);
this.grpPdo1Send.Controls.Add(this.lblRuleDurationUnit);
this.grpPdo1Send.Location = new System.Drawing.Point(10, 120);
this.grpPdo1Send.Name = "grpPdo1Send";
this.grpPdo1Send.Size = new System.Drawing.Size(770, 190);
this.grpPdo1Send.TabIndex = 2;
this.grpPdo1Send.TabStop = false;
this.grpPdo1Send.Text = "TPDO1发送配置";
//
// lblPdo1Data0
//
this.lblPdo1Data0.AutoSize = true;
this.lblPdo1Data0.Location = new System.Drawing.Point(10, 25);
this.lblPdo1Data0.Name = "lblPdo1Data0";
this.lblPdo1Data0.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data0.TabIndex = 0;
this.lblPdo1Data0.Text = "Byte0:";
//
// lblPdo1Data1
//
this.lblPdo1Data1.AutoSize = true;
this.lblPdo1Data1.Location = new System.Drawing.Point(90, 25);
this.lblPdo1Data1.Name = "lblPdo1Data1";
this.lblPdo1Data1.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data1.TabIndex = 1;
this.lblPdo1Data1.Text = "Byte1:";
//
// lblPdo1Data2
//
this.lblPdo1Data2.AutoSize = true;
this.lblPdo1Data2.Location = new System.Drawing.Point(170, 25);
this.lblPdo1Data2.Name = "lblPdo1Data2";
this.lblPdo1Data2.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data2.TabIndex = 2;
this.lblPdo1Data2.Text = "Byte2:";
//
// lblPdo1Data3
//
this.lblPdo1Data3.AutoSize = true;
this.lblPdo1Data3.Location = new System.Drawing.Point(250, 25);
this.lblPdo1Data3.Name = "lblPdo1Data3";
this.lblPdo1Data3.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data3.TabIndex = 3;
this.lblPdo1Data3.Text = "Byte3:";
//
// lblPdo1Data4
//
this.lblPdo1Data4.AutoSize = true;
this.lblPdo1Data4.Location = new System.Drawing.Point(330, 25);
this.lblPdo1Data4.Name = "lblPdo1Data4";
this.lblPdo1Data4.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data4.TabIndex = 4;
this.lblPdo1Data4.Text = "Byte4:";
//
// lblPdo1Data5
//
this.lblPdo1Data5.AutoSize = true;
this.lblPdo1Data5.Location = new System.Drawing.Point(410, 25);
this.lblPdo1Data5.Name = "lblPdo1Data5";
this.lblPdo1Data5.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data5.TabIndex = 5;
this.lblPdo1Data5.Text = "Byte5:";
//
// lblPdo1Data6
//
this.lblPdo1Data6.AutoSize = true;
this.lblPdo1Data6.Location = new System.Drawing.Point(490, 25);
this.lblPdo1Data6.Name = "lblPdo1Data6";
this.lblPdo1Data6.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data6.TabIndex = 6;
this.lblPdo1Data6.Text = "Byte6:";
//
// lblPdo1Data7
//
this.lblPdo1Data7.AutoSize = true;
this.lblPdo1Data7.Location = new System.Drawing.Point(570, 25);
this.lblPdo1Data7.Name = "lblPdo1Data7";
this.lblPdo1Data7.Size = new System.Drawing.Size(55, 15);
this.lblPdo1Data7.TabIndex = 7;
this.lblPdo1Data7.Text = "Byte7:";
//
// txtPdo1Data0
//
this.txtPdo1Data0.Location = new System.Drawing.Point(10, 45);
this.txtPdo1Data0.MaxLength = 3;
this.txtPdo1Data0.Name = "txtPdo1Data0";
this.txtPdo1Data0.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data0.TabIndex = 8;
this.txtPdo1Data0.Text = "0";
this.txtPdo1Data0.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data1
//
this.txtPdo1Data1.Location = new System.Drawing.Point(90, 45);
this.txtPdo1Data1.MaxLength = 3;
this.txtPdo1Data1.Name = "txtPdo1Data1";
this.txtPdo1Data1.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data1.TabIndex = 9;
this.txtPdo1Data1.Text = "0";
this.txtPdo1Data1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data2
//
this.txtPdo1Data2.Location = new System.Drawing.Point(170, 45);
this.txtPdo1Data2.MaxLength = 3;
this.txtPdo1Data2.Name = "txtPdo1Data2";
this.txtPdo1Data2.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data2.TabIndex = 10;
this.txtPdo1Data2.Text = "0";
this.txtPdo1Data2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data3
//
this.txtPdo1Data3.Location = new System.Drawing.Point(250, 45);
this.txtPdo1Data3.MaxLength = 3;
this.txtPdo1Data3.Name = "txtPdo1Data3";
this.txtPdo1Data3.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data3.TabIndex = 11;
this.txtPdo1Data3.Text = "0";
this.txtPdo1Data3.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data4
//
this.txtPdo1Data4.Location = new System.Drawing.Point(330, 45);
this.txtPdo1Data4.MaxLength = 3;
this.txtPdo1Data4.Name = "txtPdo1Data4";
this.txtPdo1Data4.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data4.TabIndex = 12;
this.txtPdo1Data4.Text = "0";
this.txtPdo1Data4.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data5
//
this.txtPdo1Data5.Location = new System.Drawing.Point(410, 45);
this.txtPdo1Data5.MaxLength = 3;
this.txtPdo1Data5.Name = "txtPdo1Data5";
this.txtPdo1Data5.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data5.TabIndex = 13;
this.txtPdo1Data5.Text = "0";
this.txtPdo1Data5.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data6
//
this.txtPdo1Data6.Location = new System.Drawing.Point(490, 45);
this.txtPdo1Data6.MaxLength = 3;
this.txtPdo1Data6.Name = "txtPdo1Data6";
this.txtPdo1Data6.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data6.TabIndex = 14;
this.txtPdo1Data6.Text = "0";
this.txtPdo1Data6.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// txtPdo1Data7
//
this.txtPdo1Data7.Location = new System.Drawing.Point(570, 45);
this.txtPdo1Data7.MaxLength = 3;
this.txtPdo1Data7.Name = "txtPdo1Data7";
this.txtPdo1Data7.Size = new System.Drawing.Size(60, 25);
this.txtPdo1Data7.TabIndex = 15;
this.txtPdo1Data7.Text = "0";
this.txtPdo1Data7.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// btnSendTpdo1
//
this.btnSendTpdo1.Enabled = false;
this.btnSendTpdo1.Location = new System.Drawing.Point(10, 95);
this.btnSendTpdo1.Name = "btnSendTpdo1";
this.btnSendTpdo1.Size = new System.Drawing.Size(100, 25);
this.btnSendTpdo1.TabIndex = 16;
this.btnSendTpdo1.Text = "立即发送";
this.btnSendTpdo1.Click += new System.EventHandler(this.BtnSendTpdo1_Click);
//
// btnRuleSend
//
this.btnRuleSend.Enabled = false;
this.btnRuleSend.Location = new System.Drawing.Point(120, 95);
this.btnRuleSend.Name = "btnRuleSend";
this.btnRuleSend.Size = new System.Drawing.Size(100, 25);
this.btnRuleSend.TabIndex = 17;
this.btnRuleSend.Text = "规则发送";
this.btnRuleSend.Click += new System.EventHandler(this.BtnRuleSend_Click);
//
// btnContinuousSend
//
this.btnContinuousSend.Enabled = false;
this.btnContinuousSend.Location = new System.Drawing.Point(230, 95);
this.btnContinuousSend.Name = "btnContinuousSend";
this.btnContinuousSend.Size = new System.Drawing.Size(100, 25);
this.btnContinuousSend.TabIndex = 18;
this.btnContinuousSend.Text = "连续发送";
this.btnContinuousSend.Click += new System.EventHandler(this.BtnContinuousSend_Click);
//
// lblPeriodTime
//
this.lblPeriodTime.AutoSize = true;
this.lblPeriodTime.Location = new System.Drawing.Point(350, 98);
this.lblPeriodTime.Name = "lblPeriodTime";
this.lblPeriodTime.Size = new System.Drawing.Size(75, 15);
this.lblPeriodTime.TabIndex = 19;
this.lblPeriodTime.Text = "周期时间:";
//
// txtPeriodTime
//
this.txtPeriodTime.Location = new System.Drawing.Point(430, 95);
this.txtPeriodTime.Name = "txtPeriodTime";
this.txtPeriodTime.Size = new System.Drawing.Size(80, 25);
this.txtPeriodTime.TabIndex = 20;
this.txtPeriodTime.Text = "1000";
this.txtPeriodTime.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// lblPeriodUnit
//
this.lblPeriodUnit.AutoSize = true;
this.lblPeriodUnit.Location = new System.Drawing.Point(520, 98);
this.lblPeriodUnit.Name = "lblPeriodUnit";
this.lblPeriodUnit.Size = new System.Drawing.Size(23, 15);
this.lblPeriodUnit.TabIndex = 21;
this.lblPeriodUnit.Text = "us";
//
// lblContinuousDuration
//
this.lblContinuousDuration.AutoSize = true;
this.lblContinuousDuration.Location = new System.Drawing.Point(10, 128);
this.lblContinuousDuration.Name = "lblContinuousDuration";
this.lblContinuousDuration.Size = new System.Drawing.Size(75, 15);
this.lblContinuousDuration.TabIndex = 22;
this.lblContinuousDuration.Text = "持续时间:";
//
// txtContinuousDuration
//
this.txtContinuousDuration.Location = new System.Drawing.Point(90, 125);
this.txtContinuousDuration.Name = "txtContinuousDuration";
this.txtContinuousDuration.Size = new System.Drawing.Size(80, 25);
this.txtContinuousDuration.TabIndex = 23;
this.txtContinuousDuration.Text = "0";
this.txtContinuousDuration.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// lblContinuousDurationUnit
//
this.lblContinuousDurationUnit.AutoSize = true;
this.lblContinuousDurationUnit.Location = new System.Drawing.Point(180, 128);
this.lblContinuousDurationUnit.Name = "lblContinuousDurationUnit";
this.lblContinuousDurationUnit.Size = new System.Drawing.Size(99, 15);
this.lblContinuousDurationUnit.TabIndex = 24;
this.lblContinuousDurationUnit.Text = "秒(0=无限制)";
//
// lblRuleDuration
//
this.lblRuleDuration.AutoSize = true;
this.lblRuleDuration.Location = new System.Drawing.Point(350, 128);
this.lblRuleDuration.Name = "lblRuleDuration";
this.lblRuleDuration.Size = new System.Drawing.Size(75, 15);
this.lblRuleDuration.TabIndex = 25;
this.lblRuleDuration.Text = "规则时长:";
//
// txtRuleDuration
//
this.txtRuleDuration.Location = new System.Drawing.Point(430, 125);
this.txtRuleDuration.Name = "txtRuleDuration";
this.txtRuleDuration.Size = new System.Drawing.Size(80, 25);
this.txtRuleDuration.TabIndex = 26;
this.txtRuleDuration.Text = "0";
this.txtRuleDuration.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// lblRuleDurationUnit
//
this.lblRuleDurationUnit.AutoSize = true;
this.lblRuleDurationUnit.Location = new System.Drawing.Point(520, 128);
this.lblRuleDurationUnit.Name = "lblRuleDurationUnit";
this.lblRuleDurationUnit.Size = new System.Drawing.Size(99, 15);
this.lblRuleDurationUnit.TabIndex = 27;
this.lblRuleDurationUnit.Text = "秒(0=无限制)";
//
// txtLog
//
this.txtLog.BackColor = System.Drawing.Color.White;
this.txtLog.Font = new System.Drawing.Font("微软雅黑", 9F);
this.txtLog.ForeColor = System.Drawing.Color.Black;
this.txtLog.Location = new System.Drawing.Point(10, 320);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtLog.Size = new System.Drawing.Size(770, 210);
this.txtLog.TabIndex = 3;
//
// m_updateTimer
//
this.m_updateTimer.Enabled = true;
this.m_updateTimer.Interval = 500;
//
// SlaveTestForm
//
this.ClientSize = new System.Drawing.Size(782, 543);
this.Controls.Add(this.grpDeviceInfo);
this.Controls.Add(this.grpControl);
this.Controls.Add(this.grpPdo1Send);
this.Controls.Add(this.txtLog);
this.Name = "SlaveTestForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "CANopen从站设备测试";
this.grpDeviceInfo.ResumeLayout(false);
this.grpDeviceInfo.PerformLayout();
this.grpControl.ResumeLayout(false);
this.grpPdo1Send.ResumeLayout(false);
this.grpPdo1Send.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
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;
btnRuleSend.Enabled = true;
btnContinuousSend.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
{
// 如果正在连续发送,先停止
if (m_isContinuousSending)
{
StopContinuousSend();
}
// 如果正在规则发送,先停止
if (m_isRuleSending)
{
StopRuleSend();
}
AppendLog("正在停止从站...");
m_slaveDevice.Stop();
AppendLog("✓ 从站已停止");
btnStart.Enabled = true;
btnStop.Enabled = false;
btnSendTpdo1.Enabled = false;
btnRuleSend.Enabled = false;
btnContinuousSend.Enabled = false;
btnContinuousSend.Text = "连续发送";
btnRuleSend.Text = "规则发送";
UpdateDeviceInfo();
}
catch (Exception ex)
{
AppendLog("✗ 停止错误: " + ex.Message);
}
}
private void BtnSendTpdo1_Click(object sender, EventArgs e)
{
try
{
// 从界面读取PDO1数据
byte[] pdoData = new byte[8];
pdoData[0] = ParseByteValue(txtPdo1Data0.Text, 0);
pdoData[1] = ParseByteValue(txtPdo1Data1.Text, 1);
pdoData[2] = ParseByteValue(txtPdo1Data2.Text, 2);
pdoData[3] = ParseByteValue(txtPdo1Data3.Text, 3);
pdoData[4] = ParseByteValue(txtPdo1Data4.Text, 4);
pdoData[5] = ParseByteValue(txtPdo1Data5.Text, 5);
pdoData[6] = ParseByteValue(txtPdo1Data6.Text, 6);
pdoData[7] = ParseByteValue(txtPdo1Data7.Text, 7);
// 构建数据字符串用于日志
string dataStr = BitConverter.ToString(pdoData).Replace("-", " ");
AppendLog("→ 发送TPDO1: [" + dataStr + "]");
// TODO: 这里需要修改CanOpenSlaveDevice以支持自定义数据发送
// 暂时使用默认发送方法
m_slaveDevice.SendTpdo(1, pdoData);
}
catch (Exception ex)
{
AppendLog("✗ 发送TPDO1失败: " + ex.Message);
}
}
private void BtnContinuousSend_Click(object sender, EventArgs e)
{
if (m_isContinuousSending)
{
// 停止连续发送
StopContinuousSend();
}
else
{
// 启动连续发送
StartContinuousSend();
}
}
///
/// 规则发送按钮点击事件
///
private void BtnRuleSend_Click(object sender, EventArgs e)
{
if (m_isRuleSending)
{
// 停止规则发送
StopRuleSend();
}
else
{
// 启动规则发送
StartRuleSend();
}
}
///
/// 启动连续发送
///
private void StartContinuousSend()
{
try
{
// 解析周期时间(微秒)
if (!double.TryParse(txtPeriodTime.Text.Trim(), out double periodUs) || periodUs <= 0)
{
AppendLog("✗ 周期时间无效,请输入大于0的数值(单位:微秒)");
return;
}
// 解析运行时长(秒转毫秒),如果输入为空或无效,默认为0(无限制)
int durationSeconds = 0;
if (int.TryParse(txtContinuousDuration.Text.Trim(), out int parsedSeconds))
{
durationSeconds = Math.Max(0, parsedSeconds); // 确保非负数
}
m_continuousSendDurationMs = durationSeconds * 1000; // 转换为毫秒
// 重置计数器
m_continuousSendCounter = 0;
m_continuousSendStartTime = DateTime.Now;
// 创建取消令牌
m_continuousSendCts = new CancellationTokenSource();
m_isContinuousSending = true;
// 更新按钮文本
btnContinuousSend.Text = "停止发送";
string durationText = durationSeconds > 0 ? durationSeconds.ToString() + "秒" : "无限制";
AppendLog($"▶ 开始连续发送 - 周期: {periodUs:F0}us, 时长: {durationText}");
// 启动异步发送任务
Task.Run(() => ContinuousSendLoop(periodUs, m_continuousSendCts.Token));
// 如果设置了运行时长,则启动定时器来检查时间
if (m_continuousSendDurationMs > 0)
{
Task.Delay(m_continuousSendDurationMs, m_continuousSendCts.Token)
.ContinueWith(t =>
{
if (!t.IsCanceled)
{
// 时间到了,自动停止连续发送
if (this.InvokeRequired)
{
this.Invoke(new Action(StopContinuousSend));
}
else
{
StopContinuousSend();
}
}
});
}
}
catch (Exception ex)
{
AppendLog("✗ 启动连续发送失败: " + ex.Message);
m_isContinuousSending = false;
btnContinuousSend.Text = "连续发送";
}
}
///
/// 停止连续发送
///
private void StopContinuousSend()
{
try
{
m_isContinuousSending = false;
m_continuousSendCts?.Cancel();
// 等待任务结束
Thread.Sleep(100);
btnContinuousSend.Text = "连续发送";
AppendLog("⏹ 连续发送已停止 - 共发送 " + m_continuousSendCounter.ToString() + " 次");
}
catch (Exception ex)
{
AppendLog("✗ 停止连续发送失败: " + ex.Message);
}
}
///
/// 连续发送循环(高精度)
///
private async Task ContinuousSendLoop(double periodUs, CancellationToken cancellationToken)
{
Stopwatch stopwatch = new Stopwatch();
long sendCount = 0;
// 计算每次循环的目标时间间隔(纳秒)
long periodNs = (long)(periodUs * 1000.0);
AppendLog("[调试] 目标周期: " + periodUs.ToString("F0") + "us = " + periodNs.ToString() + "ns");
while (!cancellationToken.IsCancellationRequested)
{
// 检查是否达到运行时长限制
if (m_continuousSendDurationMs > 0)
{
double elapsedMs = (DateTime.Now - m_continuousSendStartTime).TotalMilliseconds;
if (elapsedMs >= m_continuousSendDurationMs)
{
StopContinuousSend();
return;
}
}
try
{
// 记录开始时间
stopwatch.Restart();
// 构建PDO数据 - 第一个字节自增
byte[] pdoData = new byte[8];
pdoData[0] = (byte)(m_continuousSendCounter % 256); // 第一个字节自增
pdoData[1] = ParseByteValue(txtPdo1Data1.Text, 1);
pdoData[2] = ParseByteValue(txtPdo1Data2.Text, 2);
pdoData[3] = ParseByteValue(txtPdo1Data3.Text, 3);
pdoData[4] = ParseByteValue(txtPdo1Data4.Text, 4);
pdoData[5] = ParseByteValue(txtPdo1Data5.Text, 5);
pdoData[6] = ParseByteValue(txtPdo1Data6.Text, 6);
pdoData[7] = ParseByteValue(txtPdo1Data7.Text, 7);
// 发送PDO(注意:这里需要修改CanOpenSlaveDevice支持自定义数据)
// 暂时使用默认方法,实际应该传入pdoData
m_slaveDevice.SendTpdo(1, pdoData);
sendCount++;
m_continuousSendCounter++; // 计数器自增
// 每100次输出一次日志,避免日志过多
if (sendCount % 100 == 0)
{
AppendLog("→ 连续发送 #" + sendCount.ToString() + ", Byte0=" + m_continuousSendCounter.ToString());
}
// 计算已经消耗的时间
long elapsedNs = stopwatch.ElapsedTicks * 1000000000L / Stopwatch.Frequency;
long remainingNs = periodNs - elapsedNs;
// 如果还有剩余时间,进行精确延迟
if (remainingNs > 0)
{
// 对于较大的延迟,使用Task.Delay
if (remainingNs > 100000) // 大于100微秒
{
int delayMs = (int)(remainingNs / 1000000);
await Task.Delay(delayMs, cancellationToken).ConfigureAwait(false);
}
else
{
// 对于较小的延迟,使用SpinWait
SpinWait.SpinUntil(() => cancellationToken.IsCancellationRequested,
TimeSpan.FromTicks(remainingNs * Stopwatch.Frequency / 1000000000L));
}
}
}
catch (OperationCanceledException)
{
// 正常取消
break;
}
catch (Exception ex)
{
AppendLog("✗ 连续发送错误: " + ex.Message);
break;
}
}
}
private byte ParseByteValue(string text, int index)
{
if (string.IsNullOrWhiteSpace(text))
return 0;
if (byte.TryParse(text.Trim(), out byte value))
return value;
AppendLog("⚠ Byte" + index.ToString() + " 值无效: '" + text + "',使用默认值0");
return 0;
}
bool NextValue = false;
byte[] pdoData = new byte[8];
///
/// 根据规则发送TPDO1(第一字节自增)
///
private void SendRuleBasedTpdo1()
{
if(m_ruleSendCounter==0)
{
m_ruleSendStartTime = DateTime.Now;
}
// 检查是否达到运行时长限制
if (m_ruleSendDurationMs > 0)
{
double elapsedMs = (DateTime.Now - m_ruleSendStartTime).TotalMilliseconds;
if (elapsedMs >= m_ruleSendDurationMs)
{
StopRuleSend();
return;
}
}
try
{
// 构建PDO数据 - 第一个字节自增
pdoData[0] = (byte)(m_ruleSendCounter % 256); // 第一个字节自增
pdoData[1] = ParseByteValue(txtPdo1Data1.Text, 1);
pdoData[2] = ParseByteValue(txtPdo1Data2.Text, 2);
pdoData[3] = ParseByteValue(txtPdo1Data3.Text, 3);
pdoData[4] = ParseByteValue(txtPdo1Data4.Text, 4);
pdoData[5] = ParseByteValue(txtPdo1Data5.Text, 5);
pdoData[6] = ParseByteValue(txtPdo1Data6.Text, 6);
//pdoData[7] = ParseByteValue(txtPdo1Data7.Text, 7);
pdoData[7] = NextValue ? (byte)0 : (byte)1;
// 发送PDO
m_slaveDevice.SendTpdo(1, pdoData);
NextValue = !NextValue;
// 计数器自增
m_ruleSendCounter++;
// 输出日志
string dataStr = BitConverter.ToString(pdoData).Replace("-", " ");
AppendLog("→ 规则发送 #" + m_ruleSendCounter.ToString() + ", Byte0=" + ((m_ruleSendCounter - 1) % 256).ToString() + ", Data: [" + dataStr + "]");
}
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.ToString() + " [节点" + nodeId.ToString() + "]: " + dataStr);
// 如果启用了规则发送,则发送TPDO1
if (m_isRuleSending)
{
SendRuleBasedTpdo1();
}
}
private void SlaveDevice_OnNmtStateChanged(NmtState oldState, NmtState newState)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
SlaveDevice_OnNmtStateChanged(oldState, newState)));
return;
}
AppendLog("⟳ NMT状态改变: " + oldState.ToString() + " → " + newState.ToString());
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.ToString("X4") + ", 子索引" + subIndex.ToString());
}
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.ToString("X4") + ", 子索引" + subIndex.ToString() + ", 值0x" + value.ToString("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.ToString();
lblState.Text = "状态: " + m_slaveDevice.CurrentState.ToString();
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");
Console.WriteLine("[" + timestamp + "] " + message + "\r\n");
// 使用字符串拼接而非插值,确保兼容性
//txtLog.AppendText("[" + timestamp + "] " + message + "\r\n");
// 自动滚动到底部
//txtLog.SelectionStart = txtLog.Text.Length;
//txtLog.ScrollToCaret();
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
// 如果正在连续发送,先停止
if (m_isContinuousSending)
{
StopContinuousSend();
}
// 如果正在规则发送,先停止
if (m_isRuleSending)
{
StopRuleSend();
}
if (m_slaveDevice != null)
{
m_slaveDevice.Stop();
m_slaveDevice.Dispose();
}
m_continuousSendCts?.Dispose();
m_updateTimer?.Stop();
m_updateTimer?.Dispose();
base.OnFormClosing(e);
}
///
/// 启动规则发送
///
private void StartRuleSend()
{
try
{
// 解析运行时长(秒转毫秒),如果输入为空或无效,默认为0(无限制)
int durationSeconds = 0;
if (int.TryParse(txtRuleDuration.Text.Trim(), out int parsedSeconds))
{
durationSeconds = Math.Max(0, parsedSeconds); // 确保非负数
}
m_ruleSendDurationMs = durationSeconds * 1000; // 转换为毫秒
// 重置计数器
m_ruleSendCounter = 0;
m_ruleSendStartTime = DateTime.Now;
m_isRuleSending = true;
m_ruleSendCts = new CancellationTokenSource();
// 更新按钮文本
btnRuleSend.Text = "停止规则发送";
string durationText = durationSeconds > 0 ? durationSeconds.ToString() + "秒" : "无限制";
AppendLog("▶ 开始规则发送 - 时长: " + durationText);
// 如果设置了运行时长,则启动定时器来检查时间
if (m_ruleSendDurationMs > 0)
{
Task.Delay(m_ruleSendDurationMs, m_ruleSendCts.Token)
.ContinueWith(t =>
{
if (!t.IsCanceled)
{
// 时间到了,自动停止规则发送
if (this.InvokeRequired)
{
this.Invoke(new Action(StopRuleSend));
}
else
{
StopRuleSend();
}
}
});
}
}
catch (Exception ex)
{
AppendLog("✗ 启动规则发送失败: " + ex.Message);
m_isRuleSending = false;
btnRuleSend.Text = "规则发送";
}
}
///
/// 停止规则发送
///
private void StopRuleSend()
{
try
{
m_isRuleSending = false;
m_ruleSendCts?.Cancel();
btnRuleSend.Text = "规则发送";
AppendLog("⏹ 规则发送已停止 - 共发送 " + m_ruleSendCounter.ToString() + " 次");
}
catch (Exception ex)
{
AppendLog("✗ 停止规则发送失败: " + ex.Message);
}
}
}
}