SlaveTestForm.cs.backup 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. using System;
  2. using System.Windows.Forms;
  3. using System.Drawing;
  4. using CCDCount.DLL.CanBus;
  5. using CanTest;
  6. namespace CanOpenSlaveTest
  7. {
  8. /// <summary>
  9. /// CANopen从站设备测试界面
  10. /// </summary>
  11. public class SlaveTestForm : Form
  12. {
  13. private CanOpenSlaveDevice m_slaveDevice;
  14. // UI控件
  15. private GroupBox grpDeviceInfo;
  16. private Label lblNodeId;
  17. private Label lblState;
  18. private Label lblStatus;
  19. private GroupBox grpControl;
  20. private Button btnStart;
  21. private Button btnStop;
  22. private Button btnSendTpdo1;
  23. private Button btnSendTpdo2;
  24. private Button btnSendAllTpdos;
  25. private GroupBox grpHeartbeat;
  26. private NumericUpDown nudHeartbeatTime;
  27. private Button btnConfigureHeartbeat;
  28. private TextBox txtLog;
  29. private Label lblHeartbeat;
  30. private System.ComponentModel.IContainer components;
  31. private System.Windows.Forms.Timer m_updateTimer;
  32. // PDO数据输入控件
  33. private GroupBox grpPdoData;
  34. private Label[] lblPdoDataLabels;
  35. private TextBox[] txtPdoDataInputs;
  36. // 连续发送相关控件
  37. private Button btnContinuousSend;
  38. private Label lblPeriodTime;
  39. private TextBox txtPeriodTime;
  40. private Label lblPeriodUnit;
  41. private Label lblContinuousDuration;
  42. private TextBox txtContinuousDuration;
  43. private Label lblContinuousDurationUnit;
  44. // 规则发送相关控件
  45. private Button btnRuleSend;
  46. private Label lblRuleDuration;
  47. private TextBox txtRuleDuration;
  48. private Label lblRuleDurationUnit;
  49. // 连续发送状态
  50. private bool m_isContinuousSending = false;
  51. private CancellationTokenSource m_continuousSendCts;
  52. private long m_continuousSendCounter = 0;
  53. private DateTime m_continuousSendStartTime;
  54. private double m_continuousSendDurationMs; // 连续发送的运行时长(毫秒),0表示无限制
  55. // 规则发送状态
  56. private bool m_isRuleSending = false;
  57. private long m_ruleSendCounter = 0;
  58. private DateTime m_ruleSendStartTime;
  59. private double m_ruleSendDurationMs; // 规则发送的运行时长(毫秒),0表示无限制
  60. public SlaveTestForm()
  61. {
  62. InitializeComponent();
  63. // 在构造函数中创建从站设备(避免设计器错误)
  64. if (!DesignMode)
  65. {
  66. m_slaveDevice = new CanOpenSlaveDevice(nodeId: 1);
  67. // 注册事件
  68. m_slaveDevice.OnRpdoReceived += SlaveDevice_OnRpdoReceived;
  69. m_slaveDevice.OnNmtStateChanged += SlaveDevice_OnNmtStateChanged;
  70. m_slaveDevice.OnSdoReadRequest += SlaveDevice_OnSdoReadRequest;
  71. m_slaveDevice.OnSdoWriteRequest += SlaveDevice_OnSdoWriteRequest;
  72. // 初始化日志
  73. AppendLog("CANopen从站测试程序已启动");
  74. AppendLog("节点ID: " + m_slaveDevice.NodeId.ToString());
  75. AppendLog("点击'启动从站'按钮开始测试\n");
  76. }
  77. }
  78. private void InitializeComponent()
  79. {
  80. this.components = new System.ComponentModel.Container();
  81. this.grpDeviceInfo = new System.Windows.Forms.GroupBox();
  82. this.lblNodeId = new System.Windows.Forms.Label();
  83. this.lblState = new System.Windows.Forms.Label();
  84. this.lblStatus = new System.Windows.Forms.Label();
  85. this.grpControl = new System.Windows.Forms.GroupBox();
  86. this.btnStart = new System.Windows.Forms.Button();
  87. this.btnStop = new System.Windows.Forms.Button();
  88. this.btnSendTpdo1 = new System.Windows.Forms.Button();
  89. this.btnSendTpdo2 = new System.Windows.Forms.Button();
  90. this.btnSendAllTpdos = new System.Windows.Forms.Button();
  91. this.grpHeartbeat = new System.Windows.Forms.GroupBox();
  92. this.lblHeartbeat = new System.Windows.Forms.Label();
  93. this.nudHeartbeatTime = new System.Windows.Forms.NumericUpDown();
  94. this.btnConfigureHeartbeat = new System.Windows.Forms.Button();
  95. this.txtLog = new System.Windows.Forms.TextBox();
  96. this.m_updateTimer = new System.Windows.Forms.Timer(this.components);
  97. // 初始化PDO数据输入控件
  98. this.grpPdoData = new System.Windows.Forms.GroupBox();
  99. this.lblPdoDataLabels = new Label[8];
  100. this.txtPdoDataInputs = new TextBox[8];
  101. for (int i = 0; i < 8; i++)
  102. {
  103. this.lblPdoDataLabels[i] = new System.Windows.Forms.Label();
  104. this.txtPdoDataInputs[i] = new System.Windows.Forms.TextBox();
  105. }
  106. // 初始化连续发送控件
  107. this.btnContinuousSend = new System.Windows.Forms.Button();
  108. this.lblPeriodTime = new System.Windows.Forms.Label();
  109. this.txtPeriodTime = new System.Windows.Forms.TextBox();
  110. this.lblPeriodUnit = new System.Windows.Forms.Label();
  111. this.lblContinuousDuration = new System.Windows.Forms.Label();
  112. this.txtContinuousDuration = new System.Windows.Forms.TextBox();
  113. this.lblContinuousDurationUnit = new System.Windows.Forms.Label();
  114. // 初始化规则发送控件
  115. this.btnRuleSend = new System.Windows.Forms.Button();
  116. this.lblRuleDuration = new System.Windows.Forms.Label();
  117. this.txtRuleDuration = new System.Windows.Forms.TextBox();
  118. this.lblRuleDurationUnit = new System.Windows.Forms.Label();
  119. this.grpDeviceInfo.SuspendLayout();
  120. this.grpControl.SuspendLayout();
  121. this.grpHeartbeat.SuspendLayout();
  122. ((System.ComponentModel.ISupportInitialize)(this.nudHeartbeatTime)).BeginInit();
  123. this.SuspendLayout();
  124. //
  125. // grpDeviceInfo
  126. //
  127. this.grpDeviceInfo.Controls.Add(this.lblNodeId);
  128. this.grpDeviceInfo.Controls.Add(this.lblState);
  129. this.grpDeviceInfo.Controls.Add(this.lblStatus);
  130. this.grpDeviceInfo.Location = new System.Drawing.Point(10, 10);
  131. this.grpDeviceInfo.Name = "grpDeviceInfo";
  132. this.grpDeviceInfo.Size = new System.Drawing.Size(380, 100);
  133. this.grpDeviceInfo.TabIndex = 0;
  134. this.grpDeviceInfo.TabStop = false;
  135. this.grpDeviceInfo.Text = "设备信息";
  136. //
  137. // lblNodeId
  138. //
  139. this.lblNodeId.AutoSize = true;
  140. this.lblNodeId.Location = new System.Drawing.Point(10, 25);
  141. this.lblNodeId.Name = "lblNodeId";
  142. this.lblNodeId.Size = new System.Drawing.Size(77, 15);
  143. this.lblNodeId.TabIndex = 0;
  144. this.lblNodeId.Text = "节点ID: -";
  145. //
  146. // lblState
  147. //
  148. this.lblState.AutoSize = true;
  149. this.lblState.Location = new System.Drawing.Point(10, 50);
  150. this.lblState.Name = "lblState";
  151. this.lblState.Size = new System.Drawing.Size(61, 15);
  152. this.lblState.TabIndex = 1;
  153. this.lblState.Text = "状态: -";
  154. //
  155. // lblStatus
  156. //
  157. this.lblStatus.AutoSize = true;
  158. this.lblStatus.Location = new System.Drawing.Point(10, 75);
  159. this.lblStatus.Name = "lblStatus";
  160. this.lblStatus.Size = new System.Drawing.Size(128, 15);
  161. this.lblStatus.TabIndex = 2;
  162. this.lblStatus.Text = "运行状态: 未启动";
  163. //
  164. // grpControl
  165. //
  166. this.grpControl.Controls.Add(this.btnStart);
  167. this.grpControl.Controls.Add(this.btnStop);
  168. this.grpControl.Controls.Add(this.btnSendTpdo1);
  169. this.grpControl.Controls.Add(this.btnSendTpdo2);
  170. this.grpControl.Controls.Add(this.btnSendAllTpdos);
  171. this.grpControl.Location = new System.Drawing.Point(400, 10);
  172. this.grpControl.Name = "grpControl";
  173. this.grpControl.Size = new System.Drawing.Size(380, 100);
  174. this.grpControl.TabIndex = 1;
  175. this.grpControl.TabStop = false;
  176. this.grpControl.Text = "控制";
  177. //
  178. // btnStart
  179. //
  180. this.btnStart.Location = new System.Drawing.Point(10, 25);
  181. this.btnStart.Name = "btnStart";
  182. this.btnStart.Size = new System.Drawing.Size(100, 30);
  183. this.btnStart.TabIndex = 0;
  184. this.btnStart.Text = "启动从站";
  185. this.btnStart.Click += new System.EventHandler(this.BtnStart_Click);
  186. //
  187. // btnStop
  188. //
  189. this.btnStop.Enabled = false;
  190. this.btnStop.Location = new System.Drawing.Point(120, 25);
  191. this.btnStop.Name = "btnStop";
  192. this.btnStop.Size = new System.Drawing.Size(100, 30);
  193. this.btnStop.TabIndex = 1;
  194. this.btnStop.Text = "停止从站";
  195. this.btnStop.Click += new System.EventHandler(this.BtnStop_Click);
  196. //
  197. // btnSendTpdo1
  198. //
  199. this.btnSendTpdo1.Enabled = false;
  200. this.btnSendTpdo1.Location = new System.Drawing.Point(10, 60);
  201. this.btnSendTpdo1.Name = "btnSendTpdo1";
  202. this.btnSendTpdo1.Size = new System.Drawing.Size(100, 30);
  203. this.btnSendTpdo1.TabIndex = 2;
  204. this.btnSendTpdo1.Text = "发送TPDO1";
  205. this.btnSendTpdo1.Click += new System.EventHandler(this.BtnSendTpdo1_Click);
  206. //
  207. // btnSendTpdo2
  208. //
  209. this.btnSendTpdo2.Enabled = false;
  210. this.btnSendTpdo2.Location = new System.Drawing.Point(120, 60);
  211. this.btnSendTpdo2.Name = "btnSendTpdo2";
  212. this.btnSendTpdo2.Size = new System.Drawing.Size(100, 30);
  213. this.btnSendTpdo2.TabIndex = 3;
  214. this.btnSendTpdo2.Text = "发送TPDO2";
  215. this.btnSendTpdo2.Click += new System.EventHandler(this.BtnSendTpdo2_Click);
  216. //
  217. // btnSendAllTpdos
  218. //
  219. this.btnSendAllTpdos.Enabled = false;
  220. this.btnSendAllTpdos.Location = new System.Drawing.Point(230, 60);
  221. this.btnSendAllTpdos.Name = "btnSendAllTpdos";
  222. this.btnSendAllTpdos.Size = new System.Drawing.Size(120, 30);
  223. this.btnSendAllTpdos.TabIndex = 4;
  224. this.btnSendAllTpdos.Text = "发送所有TPDO";
  225. this.btnSendAllTpdos.Click += new System.EventHandler(this.BtnSendAllTpdos_Click);
  226. //
  227. // grpHeartbeat
  228. //
  229. this.grpHeartbeat.Controls.Add(this.lblHeartbeat);
  230. this.grpHeartbeat.Controls.Add(this.nudHeartbeatTime);
  231. this.grpHeartbeat.Controls.Add(this.btnConfigureHeartbeat);
  232. this.grpHeartbeat.Location = new System.Drawing.Point(10, 120);
  233. this.grpHeartbeat.Name = "grpHeartbeat";
  234. this.grpHeartbeat.Size = new System.Drawing.Size(770, 60);
  235. this.grpHeartbeat.TabIndex = 2;
  236. this.grpHeartbeat.TabStop = false;
  237. this.grpHeartbeat.Text = "心跳配置";
  238. //
  239. // grpPdoData
  240. //
  241. this.grpPdoData.Location = new System.Drawing.Point(10, 190);
  242. this.grpPdoData.Name = "grpPdoData";
  243. this.grpPdoData.Size = new System.Drawing.Size(770, 100);
  244. this.grpPdoData.TabIndex = 3;
  245. this.grpPdoData.TabStop = false;
  246. this.grpPdoData.Text = "TPDO数据配置";
  247. //
  248. // lblHeartbeat
  249. //
  250. this.lblHeartbeat.AutoSize = true;
  251. this.lblHeartbeat.Location = new System.Drawing.Point(10, 25);
  252. this.lblHeartbeat.Name = "lblHeartbeat";
  253. this.lblHeartbeat.Size = new System.Drawing.Size(107, 15);
  254. this.lblHeartbeat.TabIndex = 0;
  255. this.lblHeartbeat.Text = "心跳时间(ms):";
  256. //
  257. // nudHeartbeatTime
  258. //
  259. this.nudHeartbeatTime.Location = new System.Drawing.Point(110, 22);
  260. this.nudHeartbeatTime.Maximum = new decimal(new int[] {
  261. 10000,
  262. 0,
  263. 0,
  264. 0});
  265. this.nudHeartbeatTime.Name = "nudHeartbeatTime";
  266. this.nudHeartbeatTime.Size = new System.Drawing.Size(100, 25);
  267. this.nudHeartbeatTime.TabIndex = 1;
  268. this.nudHeartbeatTime.Value = new decimal(new int[] {
  269. 100,
  270. 0,
  271. 0,
  272. 0});
  273. //
  274. // btnConfigureHeartbeat
  275. //
  276. this.btnConfigureHeartbeat.Enabled = false;
  277. this.btnConfigureHeartbeat.Location = new System.Drawing.Point(220, 20);
  278. this.btnConfigureHeartbeat.Name = "btnConfigureHeartbeat";
  279. this.btnConfigureHeartbeat.Size = new System.Drawing.Size(100, 30);
  280. this.btnConfigureHeartbeat.TabIndex = 2;
  281. this.btnConfigureHeartbeat.Text = "配置心跳";
  282. this.btnConfigureHeartbeat.Click += new System.EventHandler(this.BtnConfigureHeartbeat_Click);
  283. //
  284. // txtLog
  285. //
  286. this.txtLog.BackColor = System.Drawing.Color.White;
  287. this.txtLog.Font = new System.Drawing.Font("微软雅黑", 9F);
  288. this.txtLog.ForeColor = System.Drawing.Color.Black;
  289. this.txtLog.Location = new System.Drawing.Point(10, 190);
  290. this.txtLog.Multiline = true;
  291. this.txtLog.Name = "txtLog";
  292. this.txtLog.ReadOnly = true;
  293. this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
  294. this.txtLog.Size = new System.Drawing.Size(770, 360);
  295. this.txtLog.TabIndex = 3;
  296. //
  297. // m_updateTimer
  298. //
  299. this.m_updateTimer.Enabled = true;
  300. this.m_updateTimer.Interval = 500;
  301. //
  302. // SlaveTestForm
  303. //
  304. this.ClientSize = new System.Drawing.Size(782, 553);
  305. this.Controls.Add(this.grpDeviceInfo);
  306. this.Controls.Add(this.grpControl);
  307. this.Controls.Add(this.grpHeartbeat);
  308. this.Controls.Add(this.txtLog);
  309. this.Name = "SlaveTestForm";
  310. this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
  311. this.Text = "CANopen从站设备测试";
  312. this.grpDeviceInfo.ResumeLayout(false);
  313. this.grpDeviceInfo.PerformLayout();
  314. this.grpControl.ResumeLayout(false);
  315. this.grpHeartbeat.ResumeLayout(false);
  316. this.grpHeartbeat.PerformLayout();
  317. ((System.ComponentModel.ISupportInitialize)(this.nudHeartbeatTime)).EndInit();
  318. this.ResumeLayout(false);
  319. this.PerformLayout();
  320. }
  321. private void BtnStart_Click(object sender, EventArgs e)
  322. {
  323. try
  324. {
  325. AppendLog("正在启动从站...");
  326. if (m_slaveDevice.Start(CanBaudRate.BaudRate_1M))
  327. {
  328. AppendLog("✓ 从站启动成功");
  329. btnStart.Enabled = false;
  330. btnStop.Enabled = true;
  331. btnSendTpdo1.Enabled = true;
  332. btnSendTpdo2.Enabled = true;
  333. btnSendAllTpdos.Enabled = true;
  334. btnConfigureHeartbeat.Enabled = true;
  335. UpdateDeviceInfo();
  336. }
  337. else
  338. {
  339. AppendLog("✗ 从站启动失败");
  340. MessageBox.Show("启动失败!请检查CAN设备连接。", "错误",
  341. MessageBoxButtons.OK, MessageBoxIcon.Error);
  342. }
  343. }
  344. catch (Exception ex)
  345. {
  346. AppendLog($"✗ 启动错误: {ex.Message}");
  347. MessageBox.Show(ex.Message, "错误",
  348. MessageBoxButtons.OK, MessageBoxIcon.Error);
  349. }
  350. }
  351. private void BtnStop_Click(object sender, EventArgs e)
  352. {
  353. try
  354. {
  355. AppendLog("正在停止从站...");
  356. m_slaveDevice.Stop();
  357. AppendLog("✓ 从站已停止");
  358. btnStart.Enabled = true;
  359. btnStop.Enabled = false;
  360. btnSendTpdo1.Enabled = false;
  361. btnSendTpdo2.Enabled = false;
  362. btnSendAllTpdos.Enabled = false;
  363. btnConfigureHeartbeat.Enabled = false;
  364. UpdateDeviceInfo();
  365. }
  366. catch (Exception ex)
  367. {
  368. AppendLog($"✗ 停止错误: {ex.Message}");
  369. }
  370. }
  371. private void BtnSendTpdo1_Click(object sender, EventArgs e)
  372. {
  373. try
  374. {
  375. m_slaveDevice.SendTpdo(1);
  376. AppendLog("→ 已发送TPDO1");
  377. }
  378. catch (Exception ex)
  379. {
  380. AppendLog($"✗ 发送TPDO1失败: {ex.Message}");
  381. }
  382. }
  383. private void BtnSendTpdo2_Click(object sender, EventArgs e)
  384. {
  385. try
  386. {
  387. m_slaveDevice.SendTpdo(2);
  388. AppendLog("→ 已发送TPDO2");
  389. }
  390. catch (Exception ex)
  391. {
  392. AppendLog($"✗ 发送TPDO2失败: {ex.Message}");
  393. }
  394. }
  395. private void BtnSendAllTpdos_Click(object sender, EventArgs e)
  396. {
  397. try
  398. {
  399. m_slaveDevice.SendAllTpdos();
  400. AppendLog("→ 已发送所有TPDO");
  401. }
  402. catch (Exception ex)
  403. {
  404. AppendLog($"✗ 发送TPDO失败: {ex.Message}");
  405. }
  406. }
  407. private void BtnConfigureHeartbeat_Click(object sender, EventArgs e)
  408. {
  409. try
  410. {
  411. ushort heartbeatTime = (ushort)nudHeartbeatTime.Value;
  412. m_slaveDevice.ConfigureHeartbeat(heartbeatTime);
  413. AppendLog($"✓ 心跳已配置: {heartbeatTime}ms");
  414. }
  415. catch (Exception ex)
  416. {
  417. AppendLog($"✗ 配置心跳失败: {ex.Message}");
  418. }
  419. }
  420. private void SlaveDevice_OnRpdoReceived(byte nodeId, byte pdoNumber, byte[] data)
  421. {
  422. if (this.InvokeRequired)
  423. {
  424. this.Invoke(new Action(() =>
  425. SlaveDevice_OnRpdoReceived(nodeId, pdoNumber, data)));
  426. return;
  427. }
  428. string dataStr = BitConverter.ToString(data).Replace("-", " ");
  429. AppendLog($"← 收到RPDO{pdoNumber} [节点{nodeId}]: {dataStr}");
  430. }
  431. private void SlaveDevice_OnNmtStateChanged(NmtState oldState, NmtState newState)
  432. {
  433. if (this.InvokeRequired)
  434. {
  435. this.Invoke(new Action(() =>
  436. SlaveDevice_OnNmtStateChanged(oldState, newState)));
  437. return;
  438. }
  439. AppendLog($"⟳ NMT状态改变: {oldState} → {newState}");
  440. UpdateDeviceInfo();
  441. }
  442. private void SlaveDevice_OnSdoReadRequest(byte nodeId, ushort index, byte subIndex)
  443. {
  444. if (this.InvokeRequired)
  445. {
  446. this.Invoke(new Action(() =>
  447. SlaveDevice_OnSdoReadRequest(nodeId, index, subIndex)));
  448. return;
  449. }
  450. AppendLog($"? SDO读取请求: 索引0x{index:X4}, 子索引{subIndex}");
  451. }
  452. private void SlaveDevice_OnSdoWriteRequest(byte nodeId, ushort index, byte subIndex, uint value)
  453. {
  454. if (this.InvokeRequired)
  455. {
  456. this.Invoke(new Action(() =>
  457. SlaveDevice_OnSdoWriteRequest(nodeId, index, subIndex, value)));
  458. return;
  459. }
  460. AppendLog($"↓ SDO写入请求: 索引0x{index:X4}, 子索引{subIndex}, 值0x{value:X8}");
  461. }
  462. private void UpdateTimer_Tick(object sender, EventArgs e)
  463. {
  464. UpdateDeviceInfo();
  465. }
  466. private void UpdateDeviceInfo()
  467. {
  468. if (this.InvokeRequired)
  469. {
  470. this.Invoke(new Action(UpdateDeviceInfo));
  471. return;
  472. }
  473. lblNodeId.Text = $"节点ID: {m_slaveDevice.NodeId}";
  474. lblState.Text = $"状态: {m_slaveDevice.CurrentState}";
  475. lblStatus.Text = $"运行状态: {(m_slaveDevice.IsRunning ? "运行中" : "已停止")}";
  476. }
  477. private void AppendLog(string message)
  478. {
  479. if (this.InvokeRequired)
  480. {
  481. this.Invoke(new Action<string>(AppendLog), message);
  482. return;
  483. }
  484. string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
  485. // 使用字符串拼接而非插值,确保兼容性
  486. txtLog.AppendText("[" + timestamp + "] " + message + "\r\n");
  487. // 自动滚动到底部
  488. txtLog.SelectionStart = txtLog.Text.Length;
  489. txtLog.ScrollToCaret();
  490. }
  491. protected override void OnFormClosing(FormClosingEventArgs e)
  492. {
  493. if (m_slaveDevice != null)
  494. {
  495. m_slaveDevice.Stop();
  496. m_slaveDevice.Dispose();
  497. }
  498. m_updateTimer?.Stop();
  499. m_updateTimer?.Dispose();
  500. base.OnFormClosing(e);
  501. }
  502. }
  503. }