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