SlaveTestForm.cs 19 KB

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