SiemensPPI.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using MECF.Framework.RT.Core.IoProviders.Common;
  6. using MECF.Framework.RT.Core.IoProviders.Common.Serial;
  7. using MECF.Framework.RT.Core.IoProviders.Common.Transfer;
  8. #if Net45
  9. using System.Threading.Tasks;
  10. #endif
  11. namespace MECF.Framework.RT.Core.IoProviders.Siemens
  12. {
  13. /// <summary>
  14. /// 西门子的PPI协议,适用于s7-200plc,注意,本类库有个致命的风险需要注意,由于本类库的每次通讯分成2次操作,故而不支持多线程同时读写,当发生线程竞争的时候,会导致数据异常,
  15. /// 想要解决的话,需要您在每次数据交互时添加同步锁。
  16. /// </summary>
  17. /// <remarks>
  18. /// 适用于西门子200的通信,非常感谢 合肥-加劲 的测试,让本类库圆满完成。
  19. ///
  20. /// 注意:M地址范围有限 0-31地址
  21. /// </remarks>
  22. public class SiemensPPI : SerialDeviceBase<ReverseBytesTransform>
  23. {
  24. #region Constructor
  25. /// <summary>
  26. /// 实例化一个西门子的PPI协议对象
  27. /// </summary>
  28. public SiemensPPI( )
  29. {
  30. WordLength = 2;
  31. }
  32. #endregion
  33. /// <summary>
  34. /// 西门子PLC的站号信息
  35. /// </summary>
  36. public byte Station { get => station;
  37. set {
  38. station = value;
  39. executeConfirm[1] = value;
  40. int count = 0;
  41. for (int i = 1; i < 4; i++)
  42. {
  43. count += executeConfirm[i];
  44. }
  45. executeConfirm[4] = (byte)count;
  46. }
  47. }
  48. /// <summary>
  49. /// 从西门子的PLC中读取数据信息,地址为"M100","AI100","I0","Q0","V100","S100"等,详细请参照API文档
  50. /// </summary>
  51. /// <param name="address">西门子的地址数据信息</param>
  52. /// <param name="length">数据长度</param>
  53. /// <returns>带返回结果的结果对象</returns>
  54. public override OperateResult<byte[]> Read( string address, ushort length )
  55. {
  56. // 解析指令
  57. OperateResult<byte[]> command = BuildReadCommand( station, address, length, false );
  58. if (!command.IsSuccess) return command;
  59. // 第一次数据交互
  60. OperateResult<byte[]> read1 = ReadBase( command.Content );
  61. if (!read1.IsSuccess) return read1;
  62. // 验证
  63. if (read1.Content[0] != 0xE5) return new OperateResult<byte[]>( "PLC Receive Check Failed:" + SoftBasic.ByteToHexString( read1.Content, ' ' ) );
  64. // 第二次数据交互
  65. OperateResult<byte[]> read2 = ReadBase( executeConfirm );
  66. if (!read2.IsSuccess) return read2;
  67. // 错误码判断
  68. if (read2.Content.Length < 21) return new OperateResult<byte[]>( read2.ErrorCode, "Failed: " + SoftBasic.ByteToHexString( read2.Content, ' ' ) );
  69. if (read2.Content[17] != 0x00 || read2.Content[18] != 0x00) return new OperateResult<byte[]>( read2.Content[19], GetMsgFromStatus( read2.Content[18], read2.Content[19] ) );
  70. if (read2.Content[21] != 0xFF) return new OperateResult<byte[]>( read2.Content[21], GetMsgFromStatus( read2.Content[21] ) );
  71. // 数据提取
  72. byte[] buffer = new byte[length];
  73. if (read2.Content[21] == 0xFF && read2.Content[22] == 0x04)
  74. {
  75. Array.Copy( read2.Content, 25, buffer, 0, length );
  76. }
  77. return OperateResult.CreateSuccessResult( buffer );
  78. }
  79. /// <summary>
  80. /// 从西门子的PLC中读取bool数据信息,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  81. /// </summary>
  82. /// <param name="address">西门子的地址数据信息</param>
  83. /// <param name="length">数据长度</param>
  84. /// <returns>带返回结果的结果对象</returns>
  85. public OperateResult<bool[]> ReadBool( string address, ushort length )
  86. {
  87. // 解析指令
  88. OperateResult<byte[]> command = BuildReadCommand( station, address, length, true );
  89. if (!command.IsSuccess) return OperateResult.CreateFailedResult<bool[]>( command );
  90. // 第一次数据交互
  91. OperateResult<byte[]> read1 = ReadBase( command.Content );
  92. if (!read1.IsSuccess) return OperateResult.CreateFailedResult<bool[]>( read1 );
  93. // 验证
  94. if (read1.Content[0] != 0xE5) return new OperateResult<bool[]>( "PLC Receive Check Failed:" + SoftBasic.ByteToHexString( read1.Content, ' ' ) );
  95. // 第二次数据交互
  96. OperateResult<byte[]> read2 = ReadBase( executeConfirm );
  97. if (!read2.IsSuccess) return OperateResult.CreateFailedResult<bool[]>( read2 );
  98. // 错误码判断
  99. if (read2.Content.Length < 21) return new OperateResult<bool[]>( read2.ErrorCode, "Failed: " + SoftBasic.ByteToHexString( read2.Content, ' ' ) );
  100. if (read2.Content[17] != 0x00 || read2.Content[18] != 0x00) return new OperateResult<bool[]>( read2.Content[19], GetMsgFromStatus( read2.Content[18], read2.Content[19] ) );
  101. if (read2.Content[21] != 0xFF) return new OperateResult<bool[]>( read2.Content[21], GetMsgFromStatus( read2.Content[21] ) );
  102. // 数据提取
  103. byte[] buffer = new byte[read2.Content.Length - 27];
  104. if (read2.Content[21] == 0xFF && read2.Content[22] == 0x03)
  105. {
  106. Array.Copy( read2.Content, 25, buffer, 0, buffer.Length );
  107. }
  108. return OperateResult.CreateSuccessResult( SoftBasic.ByteToBoolArray( buffer, length ) );
  109. }
  110. /// <summary>
  111. /// 从西门子的PLC中读取bool数据信息,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  112. /// </summary>
  113. /// <param name="address">西门子的地址数据信息</param>
  114. /// <returns>带返回结果的结果对象</returns>
  115. public OperateResult<bool> ReadBool( string address )
  116. {
  117. OperateResult<bool[]> read = ReadBool( address, 1 );
  118. if (!read.IsSuccess) return OperateResult.CreateFailedResult<bool>( read );
  119. return OperateResult.CreateSuccessResult( read.Content[0] );
  120. }
  121. /// <summary>
  122. /// 将字节数据写入到西门子PLC中,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  123. /// </summary>
  124. /// <param name="address">西门子的地址数据信息</param>
  125. /// <param name="value">数据长度</param>
  126. /// <returns>带返回结果的结果对象</returns>
  127. public override OperateResult Write( string address, byte[] value )
  128. {
  129. // 解析指令
  130. OperateResult<byte[]> command = BuildWriteCommand( station, address, value );
  131. if (!command.IsSuccess) return command;
  132. // 第一次数据交互
  133. OperateResult<byte[]> read1 = ReadBase( command.Content );
  134. if (!read1.IsSuccess) return read1;
  135. // 验证
  136. if (read1.Content[0] != 0xE5) return new OperateResult<byte[]>( "PLC Receive Check Failed:" + read1.Content[0] );
  137. // 第二次数据交互
  138. OperateResult<byte[]> read2 = ReadBase( executeConfirm );
  139. if (!read2.IsSuccess) return read2;
  140. // 错误码判断
  141. if (read2.Content.Length < 21) return new OperateResult( read2.ErrorCode, "Failed: " + SoftBasic.ByteToHexString( read2.Content, ' ' ) );
  142. if (read2.Content[17] != 0x00 || read2.Content[18] != 0x00) return new OperateResult( read2.Content[19], GetMsgFromStatus( read2.Content[18], read2.Content[19] ) );
  143. if (read2.Content[21] != 0xFF) return new OperateResult( read2.Content[21], GetMsgFromStatus( read2.Content[21] ) );
  144. // 数据提取
  145. return OperateResult.CreateSuccessResult( );
  146. }
  147. /// <summary>
  148. /// 将bool数据写入到西门子PLC中,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  149. /// </summary>
  150. /// <param name="address">西门子的地址数据信息</param>
  151. /// <param name="value">数据长度</param>
  152. /// <returns>带返回结果的结果对象</returns>
  153. public OperateResult Write(string address, bool[] value )
  154. {
  155. // 解析指令
  156. OperateResult<byte[]> command = BuildWriteCommand( station, address, value );
  157. if (!command.IsSuccess) return command;
  158. // 第一次数据交互
  159. OperateResult<byte[]> read1 = ReadBase( command.Content );
  160. if (!read1.IsSuccess) return read1;
  161. // 验证
  162. if (read1.Content[0] != 0xE5) return new OperateResult<byte[]>( "PLC Receive Check Failed:" + read1.Content[0] );
  163. // 第二次数据交互
  164. OperateResult<byte[]> read2 = ReadBase( executeConfirm );
  165. if (!read2.IsSuccess) return read2;
  166. // 错误码判断
  167. if (read2.Content.Length < 21) return new OperateResult( read2.ErrorCode, "Failed: " + SoftBasic.ByteToHexString( read2.Content, ' ' ) );
  168. if (read2.Content[17] != 0x00 || read2.Content[18] != 0x00) return new OperateResult( read2.Content[19], GetMsgFromStatus( read2.Content[18], read2.Content[19] ) );
  169. if (read2.Content[21] != 0xFF) return new OperateResult( read2.Content[21], GetMsgFromStatus( read2.Content[21] ) );
  170. // 数据提取
  171. return OperateResult.CreateSuccessResult( );
  172. }
  173. /// <summary>
  174. /// 将bool数据写入到西门子PLC中,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  175. /// </summary>
  176. /// <param name="address">西门子的地址数据信息</param>
  177. /// <param name="value">数据长度</param>
  178. /// <returns>带返回结果的结果对象</returns>
  179. public OperateResult Write( string address, bool value )
  180. {
  181. return Write( address, new bool[] { value } );
  182. }
  183. #region Byte Read Write
  184. /// <summary>
  185. /// 从西门子的PLC中读取byte数据信息,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  186. /// </summary>
  187. /// <param name="address">西门子的地址数据信息</param>
  188. /// <returns>带返回结果的结果对象</returns>
  189. public OperateResult<byte> ReadByte( string address )
  190. {
  191. OperateResult<byte[]> read = Read( address, 1 );
  192. if (!read.IsSuccess) return OperateResult.CreateFailedResult<byte>( read );
  193. return OperateResult.CreateSuccessResult( read.Content[0] );
  194. }
  195. /// <summary>
  196. /// 将byte数据写入到西门子PLC中,地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等,详细请参照API文档
  197. /// </summary>
  198. /// <param name="address">西门子的地址数据信息</param>
  199. /// <param name="value">数据长度</param>
  200. /// <returns>带返回结果的结果对象</returns>
  201. public OperateResult WriteByte(string address, byte value )
  202. {
  203. return Write( address, new byte[] { value } );
  204. }
  205. #endregion
  206. #region Start Stop
  207. /// <summary>
  208. /// 启动西门子PLC为RUN模式
  209. /// </summary>
  210. /// <returns>是否启动成功</returns>
  211. public OperateResult Start( )
  212. {
  213. byte[] cmd = new byte[] { 0x68, 0x21, 0x21, 0x68, station, 0x00, 0x6C, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x09, 0x50, 0x5F, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D, 0xAA, 0x16 };
  214. // 第一次数据交互
  215. OperateResult<byte[]> read1 = ReadBase( cmd );
  216. if (!read1.IsSuccess) return read1;
  217. // 验证
  218. if (read1.Content[0] != 0xE5) return new OperateResult<byte[]>( "PLC Receive Check Failed:" + read1.Content[0] );
  219. // 第二次数据交互
  220. OperateResult<byte[]> read2 = ReadBase( executeConfirm );
  221. if (!read2.IsSuccess) return read2;
  222. // 数据提取
  223. return OperateResult.CreateSuccessResult( );
  224. }
  225. /// <summary>
  226. /// 停止西门子PLC,切换为Stop模式
  227. /// </summary>
  228. /// <returns>是否停止成功</returns>
  229. public OperateResult Stop( )
  230. {
  231. byte[] cmd = new byte[] { 0x68, 0x1D, 0x1D, 0x68, station, 0x00, 0x6C, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x50, 0x5F, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D, 0xAA, 0x16 };
  232. // 第一次数据交互
  233. OperateResult<byte[]> read1 = ReadBase( cmd );
  234. if (!read1.IsSuccess) return read1;
  235. // 验证
  236. if (read1.Content[0] != 0xE5) return new OperateResult<byte[]>( "PLC Receive Check Failed:" + read1.Content[0] );
  237. // 第二次数据交互
  238. OperateResult<byte[]> read2 = ReadBase( executeConfirm );
  239. if (!read2.IsSuccess) return read2;
  240. // 数据提取
  241. return OperateResult.CreateSuccessResult( );
  242. }
  243. #endregion
  244. #region Private Member
  245. private byte station = 0x02; // PLC的站号信息
  246. private byte[] executeConfirm = new byte[] { 0x10, 0x02, 0x00, 0x5C, 0x5E, 0x16 };
  247. #endregion
  248. #region Static Helper
  249. /// <summary>
  250. /// 解析数据地址,解析出地址类型,起始地址,DB块的地址 ->
  251. /// Parse data address, parse out address type, start address, db block address
  252. /// </summary>
  253. /// <param name="address">起始地址,例如M100,I0,Q0,V100 ->
  254. /// Start address, such as M100,I0,Q0,V100</param>
  255. /// <returns>解析数据地址,解析出地址类型,起始地址,DB块的地址 ->
  256. /// Parse data address, parse out address type, start address, db block address</returns>
  257. private static OperateResult<byte, int, ushort> AnalysisAddress( string address )
  258. {
  259. var result = new OperateResult<byte, int, ushort>( );
  260. try
  261. {
  262. result.Content3 = 0;
  263. if(address.Substring(0,2) == "AI")
  264. {
  265. result.Content1 = 0x06;
  266. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 2 ) );
  267. }
  268. else if (address.Substring( 0, 2 ) == "AQ")
  269. {
  270. result.Content1 = 0x07;
  271. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 2 ) );
  272. }
  273. else if (address[0] == 'T')
  274. {
  275. result.Content1 = 0x1F;
  276. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  277. }
  278. else if (address[0] == 'C')
  279. {
  280. result.Content1 = 0x1E;
  281. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  282. }
  283. else if (address.Substring( 0, 2 ) == "SM")
  284. {
  285. result.Content1 = 0x05;
  286. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 2 ) );
  287. }
  288. else if (address[0] == 'S')
  289. {
  290. result.Content1 = 0x04;
  291. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  292. }
  293. else if (address[0] == 'I')
  294. {
  295. result.Content1 = 0x81;
  296. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  297. }
  298. else if (address[0] == 'Q')
  299. {
  300. result.Content1 = 0x82;
  301. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  302. }
  303. else if (address[0] == 'M')
  304. {
  305. result.Content1 = 0x83;
  306. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  307. }
  308. else if (address[0] == 'D' || address.Substring( 0, 2 ) == "DB")
  309. {
  310. result.Content1 = 0x84;
  311. string[] adds = address.Split( '.' );
  312. if (address[1] == 'B')
  313. {
  314. result.Content3 = Convert.ToUInt16( adds[0].Substring( 2 ) );
  315. }
  316. else
  317. {
  318. result.Content3 = Convert.ToUInt16( adds[0].Substring( 1 ) );
  319. }
  320. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( address.IndexOf( '.' ) + 1 ) );
  321. }
  322. else if (address[0] == 'V')
  323. {
  324. result.Content1 = 0x84;
  325. result.Content3 = 1;
  326. result.Content2 = SiemensS7Net.CalculateAddressStarted( address.Substring( 1 ) );
  327. }
  328. else
  329. {
  330. result.Message = StringResources.Language.NotSupportedDataType;
  331. result.Content1 = 0;
  332. result.Content2 = 0;
  333. result.Content3 = 0;
  334. return result;
  335. }
  336. }
  337. catch (Exception ex)
  338. {
  339. result.Message = ex.Message;
  340. return result;
  341. }
  342. result.IsSuccess = true;
  343. return result;
  344. }
  345. /// <summary>
  346. /// 生成一个读取字数据指令头的通用方法 ->
  347. /// A general method for generating a command header to read a Word data
  348. /// </summary>
  349. /// <param name="station">设备的站号信息 -> Station number information for the device</param>
  350. /// <param name="address">起始地址,例如M100,I0,Q0,V100 ->
  351. /// Start address, such as M100,I0,Q0,V100</param>
  352. /// <param name="length">读取数据长度 -> Read Data length</param>
  353. /// <param name="isBit">是否为位读取</param>
  354. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  355. public static OperateResult<byte[]> BuildReadCommand( byte station, string address, ushort length, bool isBit )
  356. {
  357. OperateResult<byte, int, ushort> analysis = AnalysisAddress( address );
  358. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  359. byte[] _PLCCommand = new byte[33];
  360. _PLCCommand[ 0] = 0x68;
  361. _PLCCommand[ 1] = BitConverter.GetBytes( _PLCCommand.Length - 6 )[0];
  362. _PLCCommand[ 2] = BitConverter.GetBytes( _PLCCommand.Length - 6 )[0];
  363. _PLCCommand[ 3] = 0x68;
  364. _PLCCommand[ 4] = station;
  365. _PLCCommand[ 5] = 0x00;
  366. _PLCCommand[ 6] = 0x6C;
  367. _PLCCommand[ 7] = 0x32;
  368. _PLCCommand[ 8] = 0x01;
  369. _PLCCommand[ 9] = 0x00;
  370. _PLCCommand[10] = 0x00;
  371. _PLCCommand[11] = 0x00;
  372. _PLCCommand[12] = 0x00;
  373. _PLCCommand[13] = 0x00;
  374. _PLCCommand[14] = 0x0E;
  375. _PLCCommand[15] = 0x00;
  376. _PLCCommand[16] = 0x00;
  377. _PLCCommand[17] = 0x04;
  378. _PLCCommand[18] = 0x01;
  379. _PLCCommand[19] = 0x12;
  380. _PLCCommand[20] = 0x0A;
  381. _PLCCommand[21] = 0x10;
  382. _PLCCommand[22] = isBit ? (byte)0x01 : (byte)0x02;
  383. _PLCCommand[23] = 0x00;
  384. _PLCCommand[24] = BitConverter.GetBytes( length )[0];
  385. _PLCCommand[25] = BitConverter.GetBytes( length )[1];
  386. _PLCCommand[26] = (byte)analysis.Content3;
  387. _PLCCommand[27] = analysis.Content1;
  388. _PLCCommand[28] = BitConverter.GetBytes( analysis.Content2 )[2];
  389. _PLCCommand[29] = BitConverter.GetBytes( analysis.Content2 )[1];
  390. _PLCCommand[30] = BitConverter.GetBytes( analysis.Content2 )[0];
  391. int count = 0;
  392. for(int i = 4; i< 31; i++)
  393. {
  394. count += _PLCCommand[i];
  395. }
  396. _PLCCommand[31] = BitConverter.GetBytes( count )[0];
  397. _PLCCommand[32] = 0x16;
  398. return OperateResult.CreateSuccessResult( _PLCCommand );
  399. }
  400. /// <summary>
  401. /// 生成一个写入PLC数据信息的报文内容
  402. /// </summary>
  403. /// <param name="station">PLC的站号</param>
  404. /// <param name="address">地址</param>
  405. /// <param name="values">数据值</param>
  406. /// <returns>是否写入成功</returns>
  407. public static OperateResult<byte[]> BuildWriteCommand( byte station, string address, byte[] values )
  408. {
  409. OperateResult<byte, int, ushort> analysis = AnalysisAddress( address );
  410. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  411. int length = values.Length;
  412. // 68 21 21 68 02 00 6C 32 01 00 00 00 00 00 0E 00 00 04 01 12 0A 10
  413. byte[] _PLCCommand = new byte[37 + values.Length];
  414. _PLCCommand[ 0] = 0x68;
  415. _PLCCommand[ 1] = BitConverter.GetBytes( _PLCCommand.Length - 6 )[0];
  416. _PLCCommand[ 2] = BitConverter.GetBytes( _PLCCommand.Length - 6 )[0];
  417. _PLCCommand[ 3] = 0x68;
  418. _PLCCommand[ 4] = station;
  419. _PLCCommand[ 5] = 0x00;
  420. _PLCCommand[ 6] = 0x7C;
  421. _PLCCommand[ 7] = 0x32;
  422. _PLCCommand[ 8] = 0x01;
  423. _PLCCommand[ 9] = 0x00;
  424. _PLCCommand[10] = 0x00;
  425. _PLCCommand[11] = 0x00;
  426. _PLCCommand[12] = 0x00;
  427. _PLCCommand[13] = 0x00;
  428. _PLCCommand[14] = 0x0E;
  429. _PLCCommand[15] = 0x00;
  430. _PLCCommand[16] = (byte)(values.Length + 4);
  431. _PLCCommand[17] = 0x05;
  432. _PLCCommand[18] = 0x01;
  433. _PLCCommand[19] = 0x12;
  434. _PLCCommand[20] = 0x0A;
  435. _PLCCommand[21] = 0x10;
  436. _PLCCommand[22] = 0x02;
  437. _PLCCommand[23] = 0x00;
  438. _PLCCommand[24] = BitConverter.GetBytes( length )[0];
  439. _PLCCommand[25] = BitConverter.GetBytes( length )[1];
  440. _PLCCommand[26] = (byte)analysis.Content3;
  441. _PLCCommand[27] = analysis.Content1;
  442. _PLCCommand[28] = BitConverter.GetBytes( analysis.Content2 )[2];
  443. _PLCCommand[29] = BitConverter.GetBytes( analysis.Content2 )[1];
  444. _PLCCommand[30] = BitConverter.GetBytes( analysis.Content2 )[0];
  445. _PLCCommand[31] = 0x00;
  446. _PLCCommand[32] = 0x04;
  447. _PLCCommand[33] = BitConverter.GetBytes( length * 8 )[1];
  448. _PLCCommand[34] = BitConverter.GetBytes( length * 8 )[0];
  449. values.CopyTo( _PLCCommand, 35 );
  450. int count = 0;
  451. for (int i = 4; i < _PLCCommand.Length - 2; i++)
  452. {
  453. count += _PLCCommand[i];
  454. }
  455. _PLCCommand[_PLCCommand.Length - 2] = BitConverter.GetBytes( count )[0];
  456. _PLCCommand[_PLCCommand.Length - 1] = 0x16;
  457. return OperateResult.CreateSuccessResult( _PLCCommand );
  458. }
  459. /// <summary>
  460. /// 根据错误信息,获取到文本信息
  461. /// </summary>
  462. /// <param name="code">状态</param>
  463. /// <returns>消息文本</returns>
  464. public static string GetMsgFromStatus( byte code )
  465. {
  466. switch (code)
  467. {
  468. case 0xFF: return "No error";
  469. case 0x01: return "Hardware fault";
  470. case 0x03: return "Illegal object access";
  471. case 0x05: return "Invalid address(incorrent variable address)";
  472. case 0x06: return "Data type is not supported";
  473. case 0x0A: return "Object does not exist or length error";
  474. default: return StringResources.Language.UnknownError;
  475. }
  476. }
  477. /// <summary>
  478. /// 根据错误信息,获取到文本信息
  479. /// </summary>
  480. /// <param name="errorClass">错误类型</param>
  481. /// <param name="errorCode">错误代码</param>
  482. /// <returns>错误信息</returns>
  483. public static string GetMsgFromStatus( byte errorClass, byte errorCode )
  484. {
  485. if(errorClass == 0x80 && errorCode == 0x01)
  486. {
  487. return "Switch in wrong position for requested operation";
  488. }
  489. else if (errorClass == 0x81 && errorCode == 0x04)
  490. {
  491. return "Miscellaneous structure error in command.  Command is not supportedby CPU";
  492. }
  493. else if (errorClass == 0x84 && errorCode == 0x04)
  494. {
  495. return "CPU is busy processing an upload or download CPU cannot process command because of system fault condition";
  496. }
  497. else if (errorClass == 0x85 && errorCode == 0x00)
  498. {
  499. return "Length fields are not correct or do not agree with the amount of data received";
  500. }
  501. else if (errorClass == 0xD2 )
  502. {
  503. return "Error in upload or download command";
  504. }
  505. else if (errorClass == 0xD6)
  506. {
  507. return "Protection error(password)";
  508. }
  509. else if (errorClass == 0xDC && errorCode == 0x01)
  510. {
  511. return "Error in time-of-day clock data";
  512. }
  513. else
  514. {
  515. return StringResources.Language.UnknownError;
  516. }
  517. }
  518. /// <summary>
  519. /// 创建写入PLC的bool类型数据报文指令
  520. /// </summary>
  521. /// <param name="station">PLC的站号信息</param>
  522. /// <param name="address">地址信息</param>
  523. /// <param name="values">bool[]数据值</param>
  524. /// <returns>带有成功标识的结果对象</returns>
  525. public static OperateResult<byte[]> BuildWriteCommand( byte station, string address, bool[] values )
  526. {
  527. OperateResult<byte, int, ushort> analysis = AnalysisAddress( address );
  528. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  529. byte[] bytesValue = SoftBasic.BoolArrayToByte( values );
  530. // 68 21 21 68 02 00 6C 32 01 00 00 00 00 00 0E 00 00 04 01 12 0A 10
  531. byte[] _PLCCommand = new byte[37 + bytesValue.Length];
  532. _PLCCommand[ 0] = 0x68;
  533. _PLCCommand[ 1] = BitConverter.GetBytes( _PLCCommand.Length - 6 )[0];
  534. _PLCCommand[ 2] = BitConverter.GetBytes( _PLCCommand.Length - 6 )[0];
  535. _PLCCommand[ 3] = 0x68;
  536. _PLCCommand[ 4] = station;
  537. _PLCCommand[ 5] = 0x00;
  538. _PLCCommand[ 6] = 0x7C;
  539. _PLCCommand[ 7] = 0x32;
  540. _PLCCommand[ 8] = 0x01;
  541. _PLCCommand[ 9] = 0x00;
  542. _PLCCommand[10] = 0x00;
  543. _PLCCommand[11] = 0x00;
  544. _PLCCommand[12] = 0x00;
  545. _PLCCommand[13] = 0x00;
  546. _PLCCommand[14] = 0x0E;
  547. _PLCCommand[15] = 0x00;
  548. _PLCCommand[16] = 0x05;
  549. _PLCCommand[17] = 0x05;
  550. _PLCCommand[18] = 0x01;
  551. _PLCCommand[19] = 0x12;
  552. _PLCCommand[20] = 0x0A;
  553. _PLCCommand[21] = 0x10;
  554. _PLCCommand[22] = 0x01;
  555. _PLCCommand[23] = 0x00;
  556. _PLCCommand[24] = BitConverter.GetBytes( values.Length )[0];
  557. _PLCCommand[25] = BitConverter.GetBytes( values.Length )[1];
  558. _PLCCommand[26] = (byte)analysis.Content3;
  559. _PLCCommand[27] = analysis.Content1;
  560. _PLCCommand[28] = BitConverter.GetBytes( analysis.Content2 )[2];
  561. _PLCCommand[29] = BitConverter.GetBytes( analysis.Content2 )[1];
  562. _PLCCommand[30] = BitConverter.GetBytes( analysis.Content2 )[0];
  563. _PLCCommand[31] = 0x00;
  564. _PLCCommand[32] = 0x03;
  565. _PLCCommand[33] = BitConverter.GetBytes( values.Length )[1];
  566. _PLCCommand[34] = BitConverter.GetBytes( values.Length )[0];
  567. bytesValue.CopyTo( _PLCCommand, 35 );
  568. int count = 0;
  569. for (int i = 4; i < _PLCCommand.Length - 2; i++)
  570. {
  571. count += _PLCCommand[i];
  572. }
  573. _PLCCommand[_PLCCommand.Length - 2] = BitConverter.GetBytes( count )[0];
  574. _PLCCommand[_PLCCommand.Length - 1] = 0x16;
  575. return OperateResult.CreateSuccessResult( _PLCCommand );
  576. }
  577. #endregion
  578. }
  579. }