EtherNetPLC.cs 15 KB

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