EtherNetPLC.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. using System;
  2. using System.Net.Sockets;
  3. using System.Threading;
  4. namespace OmronFinsTCP.Net
  5. {
  6. public class EtherNetPLC
  7. {
  8. /// <summary>
  9. /// PLC节点号,调试方法,一般不需要使用
  10. /// </summary>
  11. public string PLCNode
  12. {
  13. get { return BasicClass.plcNode.ToString(); }
  14. }
  15. /// <summary>
  16. /// PC节点号,调试方法,一般不需要使用
  17. /// </summary>
  18. public string PCNode
  19. {
  20. get { return BasicClass.pcNode.ToString(); }
  21. }
  22. public bool IsConnect
  23. {
  24. get { return BasicClass.Client.Connected; }
  25. }
  26. /// <summary>
  27. /// 实例化PLC操作对象
  28. /// </summary>
  29. public EtherNetPLC()
  30. {
  31. BasicClass.Client = new TcpClient();
  32. }
  33. /// <summary>
  34. /// 与PLC建立TCP连接
  35. /// </summary>
  36. /// <param name="rIP">PLC的IP地址</param>
  37. /// <param name="rPort">端口号,默认9600</param>
  38. /// <param name="timeOut">超时时间,默认3000毫秒</param>
  39. /// <returns></returns>
  40. public short Link(string rIP, short rPort, short timeOut = 3000)
  41. {
  42. if(BasicClass.PingCheck(rIP,timeOut))
  43. {
  44. if(BasicClass.Client.Client==null)
  45. {
  46. BasicClass.Client = new TcpClient();
  47. }
  48. BasicClass.Client.Connect(rIP, (int)rPort);
  49. BasicClass.Stream = BasicClass.Client.GetStream();
  50. Thread.Sleep(10);
  51. if (BasicClass.SendData(FinsClass.HandShake()) != 0)
  52. {
  53. return -1;
  54. }
  55. else
  56. {
  57. //开始读取返回信号
  58. byte[] buffer = new byte[24];
  59. if (BasicClass.ReceiveData(buffer) != 0)
  60. {
  61. return -1;
  62. }
  63. else
  64. {
  65. if (buffer[15] != 0)//TODO:这里的15号是不是ERR信息暂时不能完全肯定
  66. return -1;
  67. else
  68. {
  69. BasicClass.pcNode = buffer[19];
  70. BasicClass.plcNode = buffer[23];
  71. return 0;
  72. }
  73. }
  74. }
  75. }
  76. else
  77. {
  78. BasicClass.Client?.Close();
  79. //连接超时
  80. return -1;
  81. }
  82. }
  83. /// <summary>
  84. /// 关闭PLC操作对象的TCP连接
  85. /// </summary>
  86. /// <returns></returns>
  87. public short Close()
  88. {
  89. try
  90. {
  91. BasicClass.Stream?.Close();
  92. BasicClass.Client?.Close();
  93. return 0;
  94. }
  95. catch
  96. {
  97. return -1;
  98. }
  99. }
  100. /// <summary>
  101. /// 读值方法(多个连续值)
  102. /// </summary>
  103. /// <param name="mr">地址类型枚举</param>
  104. /// <param name="ch">起始地址</param>
  105. /// <param name="cnt">地址个数</param>
  106. /// <param name="reData">返回值</param>
  107. /// <returns></returns>
  108. public short ReadWords(PlcMemory mr, short ch, short cnt, out short[] reData)
  109. {
  110. reData = new short[(int)(cnt)];//储存读取到的数据
  111. int num = (int)(30 + cnt * 2);//接收数据(Text)的长度,字节数
  112. byte[] buffer = new byte[num];//用于接收数据的缓存区大小
  113. byte[] array = FinsClass.FinsCmd(RorW.Read, mr, MemoryType.Word, ch, 00, cnt);
  114. if (BasicClass.SendData(array) == 0)
  115. {
  116. if (BasicClass.ReceiveData(buffer) == 0)
  117. {
  118. //命令返回成功,继续查询是否有错误码,然后在读取数据
  119. bool succeed = true;
  120. if (buffer[11] == 3)
  121. succeed = ErrorCode.CheckHeadError(buffer[15]);
  122. if (succeed)//no header error
  123. {
  124. //endcode为fins指令的返回错误码
  125. if (ErrorCode.CheckEndCode(buffer[28], buffer[29]))
  126. {
  127. //完全正确的返回,开始读取返回的具体数值
  128. for (int i = 0; i < cnt; i++)
  129. {
  130. //返回的数据从第30字节开始储存的,
  131. //PLC每个字占用两个字节,且是高位在前,这和微软的默认低位在前不同
  132. //因此无法直接使用,reData[i] = BitConverter.ToInt16(buffer, 30 + i * 2);
  133. //先交换了高低位的位置,然后再使用BitConverter.ToInt16转换
  134. byte[] temp = new byte[] { buffer[30 + i * 2 + 1], buffer[30 + i * 2] };
  135. reData[i] = BitConverter.ToInt16(temp, 0);
  136. }
  137. return 0;
  138. }
  139. else
  140. {
  141. return -1;
  142. }
  143. }
  144. else
  145. {
  146. return -1;
  147. }
  148. }
  149. else
  150. {
  151. return -1;
  152. }
  153. }
  154. else
  155. {
  156. return -1;
  157. }
  158. }
  159. /// <summary>
  160. /// 读单个字方法
  161. /// </summary>
  162. /// <param name="mr"></param>
  163. /// <param name="ch"></param>
  164. /// <param name="reData"></param>
  165. /// <returns></returns>
  166. public short ReadWord(PlcMemory mr, short ch, out short reData)
  167. {
  168. short[] temp;
  169. reData = new short();
  170. short re = ReadWords(mr, ch, (short)1, out temp);
  171. if (re != 0)
  172. return -1;
  173. else
  174. {
  175. reData = temp[0];
  176. return 0;
  177. }
  178. }
  179. /// <summary>
  180. /// 写值方法(多个连续值)
  181. /// </summary>
  182. /// <param name="mr">地址类型枚举</param>
  183. /// <param name="ch">起始地址</param>
  184. /// <param name="cnt">地址个数</param>
  185. /// <param name="inData">写入值</param>
  186. /// <returns></returns>
  187. public short WriteWords(PlcMemory mr, short ch, short cnt, short[] inData)
  188. {
  189. byte[] buffer = new byte[30];
  190. byte[] arrayhead = FinsClass.FinsCmd(RorW.Write, mr, MemoryType.Word, ch, 00, cnt);//前34字节和读指令基本一直,还需要拼接下面的输入数据数组
  191. byte[] wdata = new byte[(int)(cnt * 2)];
  192. //转换写入值到wdata数组
  193. for (int i = 0; i < cnt; i++)
  194. {
  195. byte[] temp = BitConverter.GetBytes(inData[i]);
  196. wdata[i * 2] = temp[1];//转换为PLC的高位在前储存方式
  197. wdata[i * 2 + 1] = temp[0];
  198. }
  199. //拼接写入数组
  200. byte[] array = new byte[(int)(cnt * 2 + 34)];
  201. arrayhead.CopyTo(array, 0);
  202. wdata.CopyTo(array, 34);
  203. if (BasicClass.SendData(array) == 0)
  204. {
  205. if (BasicClass.ReceiveData(buffer) == 0)
  206. {
  207. //命令返回成功,继续查询是否有错误码,然后在读取数据
  208. bool succeed = true;
  209. if (buffer[11] == 3)
  210. succeed = ErrorCode.CheckHeadError(buffer[15]);
  211. if (succeed)//no header error
  212. {
  213. //endcode为fins指令的返回错误码
  214. if (ErrorCode.CheckEndCode(buffer[28], buffer[29]))
  215. {
  216. //完全正确的返回0
  217. return 0;
  218. }
  219. else
  220. {
  221. return -1;
  222. }
  223. }
  224. else
  225. {
  226. return -1;
  227. }
  228. }
  229. else
  230. {
  231. return -1;
  232. }
  233. }
  234. else
  235. {
  236. return -1;
  237. }
  238. }
  239. /// <summary>
  240. /// 写单个字方法
  241. /// </summary>
  242. /// <param name="mr"></param>
  243. /// <param name="ch"></param>
  244. /// <param name="inData"></param>
  245. /// <returns></returns>
  246. public short WriteWord(PlcMemory mr, short ch, short inData)
  247. {
  248. short[] temp = new short[] { inData };
  249. short re = WriteWords(mr, ch, (short)1, temp);
  250. if (re != 0)
  251. return -1;
  252. else
  253. {
  254. return 0;
  255. }
  256. }
  257. /// <summary>
  258. /// 读值方法-按位bit(单个)
  259. /// </summary>
  260. /// <param name="mr">地址类型枚举</param>
  261. /// <param name="ch">地址000.00</param>
  262. /// <param name="bs">返回开关状态枚举EtherNetPLC.BitState,0/1</param>
  263. /// <returns></returns>
  264. public short GetBitState(PlcMemory mr, string ch, out short bs)
  265. {
  266. bs = new short();
  267. byte[] buffer = new byte[31];//用于接收数据的缓存区大小
  268. short cnInt = short.Parse(ch.Split('.')[0]);
  269. short cnBit = short.Parse(ch.Split('.')[1]);
  270. byte[] array = FinsClass.FinsCmd(RorW.Read, mr, MemoryType.Bit, cnInt, cnBit, 1);
  271. if (BasicClass.SendData(array) == 0)
  272. {
  273. if (BasicClass.ReceiveData(buffer) == 0)
  274. {
  275. //命令返回成功,继续查询是否有错误码,然后在读取数据
  276. bool succeed = true;
  277. if (buffer[11] == 3)
  278. succeed = ErrorCode.CheckHeadError(buffer[15]);
  279. if (succeed)//no header error
  280. {
  281. //endcode为fins指令的返回错误码
  282. if (ErrorCode.CheckEndCode(buffer[28], buffer[29]))
  283. {
  284. //完全正确的返回,开始读取返回的具体数值
  285. bs = (short)buffer[30];
  286. return 0;
  287. }
  288. else
  289. {
  290. return -1;
  291. }
  292. }
  293. else
  294. {
  295. return -1;
  296. }
  297. }
  298. else
  299. {
  300. return -1;
  301. }
  302. }
  303. else
  304. {
  305. return -1;
  306. }
  307. }
  308. /// <summary>
  309. /// 写值方法-按位bit(单个)
  310. /// </summary>
  311. /// <param name="mr">地址类型枚举</param>
  312. /// <param name="ch">地址000.00</param>
  313. /// <param name="bs">开关状态枚举EtherNetPLC.BitState,0/1</param>
  314. /// <returns></returns>
  315. public short SetBitState(PlcMemory mr, string ch, BitState bs)
  316. {
  317. byte[] buffer = new byte[30];
  318. short cnInt = short.Parse(ch.Split('.')[0]);
  319. short cnBit = short.Parse(ch.Split('.')[1]);
  320. byte[] arrayhead = FinsClass.FinsCmd(RorW.Write, mr, MemoryType.Bit, cnInt, cnBit, 1);
  321. byte[] array = new byte[35];
  322. arrayhead.CopyTo(array, 0);
  323. array[34] = (byte)bs;
  324. if (BasicClass.SendData(array) == 0)
  325. {
  326. if (BasicClass.ReceiveData(buffer) == 0)
  327. {
  328. //命令返回成功,继续查询是否有错误码,然后在读取数据
  329. bool succeed = true;
  330. if (buffer[11] == 3)
  331. succeed = ErrorCode.CheckHeadError(buffer[15]);
  332. if (succeed)//no header error
  333. {
  334. //endcode为fins指令的返回错误码
  335. if (ErrorCode.CheckEndCode(buffer[28], buffer[29]))
  336. {
  337. //完全正确的返回0
  338. return 0;
  339. }
  340. else
  341. {
  342. return -1;
  343. }
  344. }
  345. else
  346. {
  347. return -1;
  348. }
  349. }
  350. else
  351. {
  352. return -1;
  353. }
  354. }
  355. else
  356. {
  357. return -1;
  358. }
  359. }
  360. /// <summary>
  361. /// 读一个浮点数的方法,单精度,在PLC中占两个字
  362. /// </summary>
  363. /// <param name="mr">地址类型枚举</param>
  364. /// <param name="ch">起始地址,会读取两个连续的地址,因为单精度在PLC中占两个字</param>
  365. /// <param name="reData">返回一个float型</param>
  366. /// <returns></returns>
  367. public short ReadReal(PlcMemory mr, short ch,out float reData)
  368. {
  369. reData = new float();
  370. int num = (int)(30 + 2 * 2);//接收数据(Text)的长度,字节数
  371. byte[] buffer = new byte[num];//用于接收数据的缓存区大小
  372. byte[] array = FinsClass.FinsCmd(RorW.Read, mr, MemoryType.Word, ch, 00, 2);
  373. if (BasicClass.SendData(array) == 0)
  374. {
  375. if (BasicClass.ReceiveData(buffer) == 0)
  376. {
  377. //命令返回成功,继续查询是否有错误码,然后在读取数据
  378. bool succeed = true;
  379. if (buffer[11] == 3)
  380. succeed = ErrorCode.CheckHeadError(buffer[15]);
  381. if (succeed)//no header error
  382. {
  383. //endcode为fins指令的返回错误码
  384. if (ErrorCode.CheckEndCode(buffer[28], buffer[29]))
  385. {
  386. //完全正确的返回,开始读取返回的具体数值
  387. byte[] temp = new byte[] { buffer[30 + 1], buffer[30], buffer[30 + 3], buffer[30 + 2] };
  388. reData = BitConverter.ToSingle(temp, 0);
  389. return 0;
  390. }
  391. else
  392. {
  393. return -1;
  394. }
  395. }
  396. else
  397. {
  398. return -1;
  399. }
  400. }
  401. else
  402. {
  403. return -1;
  404. }
  405. }
  406. else
  407. {
  408. return -1;
  409. }
  410. }
  411. }
  412. }