Make_PLCMonitor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using UAS_MES_NEW.DataOperate;
  10. using UAS_MES_NEW.Entity;
  11. using UAS_MES_NEW.PublicMethod;
  12. namespace UAS_MES_NEW.Make
  13. {
  14. public partial class Make_PLCMonitor : Form
  15. {
  16. AutoSizeFormClass asc = new AutoSizeFormClass();
  17. DataHelper dh;
  18. // Modbus TCP 连接对象(保持长连接,断线自动重连)
  19. TcpClient plcClient;
  20. NetworkStream plcStream;
  21. bool connected = false;
  22. ushort transactionId = 0;
  23. readonly object readLock = new object();
  24. // 台达PLC数据寄存器:D寄存器对应Modbus保持寄存器(功能码0x03),地址=D编号
  25. // 1#舱温度 D141 / 2#舱温度 D142,地址相邻一次读完
  26. const int D141_ADDR = 141;
  27. const int READ_COUNT = 2;
  28. // 温度记录表:每次采集往里插一条(1#舱、2#舱各一条)
  29. // TL_ID 用序列 TEMPLOG_SEQ,TL_DATE 取sysdate,TL_DEVICEID 存舱号,TL_TP 存换算后的温度值
  30. const string TempLogTable = "TEMPLOG";
  31. const string TempLogSeq = "TEMPLOG_SEQ";
  32. public Make_PLCMonitor()
  33. {
  34. InitializeComponent();
  35. }
  36. private void Make_PLCMonitor_Load(object sender, EventArgs e)
  37. {
  38. asc.controllInitializeSize(this);
  39. dh = SystemInf.dh;
  40. ip.Text = "192.168.0.1";
  41. port.Value = 502;
  42. unitid.Value = 1;
  43. interval.Value = 2;
  44. decimals.Value = 1;
  45. SetStatus("未连接");
  46. }
  47. /// <summary>
  48. /// 连接串口转网口模块(Modbus TCP)
  49. /// </summary>
  50. private void Connect_Click(object sender, EventArgs e)
  51. {
  52. try
  53. {
  54. ClosePlc();
  55. plcClient = new TcpClient();
  56. IAsyncResult ar = plcClient.BeginConnect(ip.Text.Trim(), (int)port.Value, null, null);
  57. if (!ar.AsyncWaitHandle.WaitOne(3000))
  58. {
  59. ClosePlc();
  60. SetStatus("连接超时");
  61. MessageBox.Show("连接PLC超时,请检查IP和端口!");
  62. return;
  63. }
  64. plcClient.EndConnect(ar);
  65. plcClient.ReceiveTimeout = 3000;
  66. plcClient.SendTimeout = 3000;
  67. plcStream = plcClient.GetStream();
  68. connected = true;
  69. SetStatus("已连接 " + ip.Text.Trim() + ":" + port.Value);
  70. OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 已连接 " + ip.Text.Trim() + ":" + port.Value + ",站号" + unitid.Value + "\n");
  71. timer1.Interval = (int)interval.Value * 1000;
  72. timer1.Start();
  73. // 连接成功后立即读一次
  74. ReadNow_Click(null, null);
  75. }
  76. catch (Exception ex)
  77. {
  78. ClosePlc();
  79. SetStatus("连接失败:" + ex.Message);
  80. MessageBox.Show("连接失败:" + ex.Message);
  81. }
  82. }
  83. /// <summary>
  84. /// 断开连接
  85. /// </summary>
  86. private void Disconnect_Click(object sender, EventArgs e)
  87. {
  88. timer1.Stop();
  89. ClosePlc();
  90. SetStatus("未连接");
  91. OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 已断开连接\n");
  92. }
  93. /// <summary>
  94. /// 立即读取一次
  95. /// </summary>
  96. private void ReadNow_Click(object sender, EventArgs e)
  97. {
  98. if (!connected)
  99. {
  100. SetStatus("未连接");
  101. return;
  102. }
  103. try
  104. {
  105. int[] values = ReadRegisters((ushort)D141_ADDR, READ_COUNT);
  106. ShowValues(values);
  107. InsertTempLog(values);
  108. SetStatus("读取成功 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  109. }
  110. catch (Exception ex)
  111. {
  112. // 读取失败:断开连接,等待下一次重连
  113. timer1.Stop();
  114. ClosePlc();
  115. connected = false;
  116. SetStatus("读取失败:" + ex.Message);
  117. OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 读取失败:" + ex.Message + "\n");
  118. // 自动重连
  119. TryReconnect();
  120. }
  121. }
  122. /// <summary>
  123. /// 定时获取数据
  124. /// </summary>
  125. private void timer1_Tick(object sender, EventArgs e)
  126. {
  127. if (!connected)
  128. {
  129. TryReconnect();
  130. return;
  131. }
  132. ReadNow_Click(null, null);
  133. }
  134. /// <summary>
  135. /// 尝试重新连接(不打断界面,仅更新状态)
  136. /// </summary>
  137. private void TryReconnect()
  138. {
  139. try
  140. {
  141. ClosePlc();
  142. plcClient = new TcpClient();
  143. IAsyncResult ar = plcClient.BeginConnect(ip.Text.Trim(), (int)port.Value, null, null);
  144. if (!ar.AsyncWaitHandle.WaitOne(3000))
  145. {
  146. ClosePlc();
  147. SetStatus("重连超时,继续重试...");
  148. return;
  149. }
  150. plcClient.EndConnect(ar);
  151. plcClient.ReceiveTimeout = 3000;
  152. plcClient.SendTimeout = 3000;
  153. plcStream = plcClient.GetStream();
  154. connected = true;
  155. SetStatus("重连成功 " + DateTime.Now.ToString("HH:mm:ss"));
  156. OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 重连成功\n");
  157. }
  158. catch
  159. {
  160. ClosePlc();
  161. SetStatus("重连失败,继续重试...");
  162. }
  163. }
  164. /// <summary>
  165. /// 把本次采集的温度写入TEMPLOG表,1#舱、2#舱各一条
  166. /// TL_ID=序列TEMPLOG_SEQ,TL_DATE=sysdate,TL_DEVICEID=舱号,TL_TP=换算后的温度
  167. /// </summary>
  168. private void InsertTempLog(int[] values)
  169. {
  170. if (values == null || values.Length < 2) return;
  171. try
  172. {
  173. int d = (int)decimals.Value;
  174. double v1 = values[0] / Math.Pow(10, d);
  175. double v2 = values[1] / Math.Pow(10, d);
  176. string v1s = v1.ToString("0.###");
  177. string v2s = v2.ToString("0.###");
  178. dh.ExecuteSql("insert into " + TempLogTable + "(TL_ID,TL_DATE,TL_DEVICEID,TL_TP)values(" + TempLogSeq + ".nextval,sysdate,'1#'," + v1s + ")", "insert");
  179. dh.ExecuteSql("insert into " + TempLogTable + "(TL_ID,TL_DATE,TL_DEVICEID,TL_TP)values(" + TempLogSeq + ".nextval,sysdate,'2#'," + v2s + ")", "insert");
  180. }
  181. catch (Exception ex)
  182. {
  183. // 插入失败不影响采集显示,只记录
  184. OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 写入" + TempLogTable + "失败:" + ex.Message + "\n");
  185. }
  186. }
  187. /// <summary>
  188. /// 显示温度值,并根据小数位设置换算(台达一般存实际值x10)
  189. /// </summary>
  190. private void ShowValues(int[] values)
  191. {
  192. if (values == null || values.Length < 2) return;
  193. int d = (int)decimals.Value;
  194. double v1 = values[0] / Math.Pow(10, d);
  195. double v2 = values[1] / Math.Pow(10, d);
  196. string fmt = d > 0 ? "F" + d : "F0";
  197. lblD141Value.Text = v1.ToString(fmt);
  198. lblD142Value.Text = v2.ToString(fmt);
  199. OperatResult.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] 1#舱(D141)=" + values[0] + " 2#舱(D142)=" + values[1] + "\n");
  200. }
  201. /// <summary>
  202. /// Modbus TCP 读保持寄存器(功能码0x03)
  203. /// </summary>
  204. private int[] ReadRegisters(ushort startAddr, ushort count)
  205. {
  206. lock (readLock)
  207. {
  208. if (!connected || plcStream == null)
  209. {
  210. throw new Exception("未连接PLC");
  211. }
  212. transactionId++;
  213. byte[] req = new byte[12];
  214. // MBAP头:事务标识、协议标识0、长度6、单元标识符
  215. req[0] = (byte)(transactionId >> 8);
  216. req[1] = (byte)(transactionId & 0xFF);
  217. req[2] = 0x00;
  218. req[3] = 0x00;
  219. req[4] = 0x00;
  220. req[5] = 0x06;
  221. req[6] = (byte)unitid.Value;
  222. // 功能码0x03 读保持寄存器
  223. req[7] = 0x03;
  224. req[8] = (byte)(startAddr >> 8);
  225. req[9] = (byte)(startAddr & 0xFF);
  226. req[10] = (byte)(count >> 8);
  227. req[11] = (byte)(count & 0xFF);
  228. plcStream.Write(req, 0, req.Length);
  229. byte[] head = ReadBytes(plcStream, 9);
  230. if (head[7] == 0x83 || (head[7] & 0x80) != 0)
  231. {
  232. byte[] ec = ReadBytes(plcStream, 1);
  233. throw new Exception("PLC返回异常码 0x" + (ec.Length > 0 ? ec[0] : 0).ToString("X2"));
  234. }
  235. int byteCount = head[8];
  236. byte[] data = ReadBytes(plcStream, byteCount);
  237. int[] values = new int[count];
  238. for (int i = 0; i < count; i++)
  239. {
  240. values[i] = (data[i * 2] << 8) | data[i * 2 + 1];
  241. }
  242. return values;
  243. }
  244. }
  245. /// <summary>
  246. /// 从流中读满指定长度,读不满视为连接异常
  247. /// </summary>
  248. private byte[] ReadBytes(NetworkStream stream, int length)
  249. {
  250. byte[] buffer = new byte[length];
  251. int read = 0;
  252. while (read < length)
  253. {
  254. int n = stream.Read(buffer, read, length - read);
  255. if (n <= 0)
  256. {
  257. throw new Exception("连接已断开");
  258. }
  259. read += n;
  260. }
  261. return buffer;
  262. }
  263. /// <summary>
  264. /// 关闭PLC连接
  265. /// </summary>
  266. private void ClosePlc()
  267. {
  268. try
  269. {
  270. if (plcStream != null)
  271. {
  272. plcStream.Close();
  273. plcStream = null;
  274. }
  275. if (plcClient != null)
  276. {
  277. plcClient.Close();
  278. plcClient = null;
  279. }
  280. }
  281. catch { }
  282. connected = false;
  283. }
  284. private void SetStatus(string text)
  285. {
  286. lblStatus.Text = "状态:" + text;
  287. }
  288. private void Make_PLCMonitor_FormClosing(object sender, FormClosingEventArgs e)
  289. {
  290. timer1.Stop();
  291. ClosePlc();
  292. }
  293. private void Make_PLCMonitor_SizeChanged(object sender, EventArgs e)
  294. {
  295. asc.controlAutoSize(this);
  296. }
  297. }
  298. }