MelsecHelper.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. using MECF.Framework.RT.Core.IoProviders.Common;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. namespace MECF.Framework.RT.Core.IoProviders.Melsec
  7. {
  8. /// <summary>
  9. /// 所有三菱通讯类的通用辅助工具类,包含了一些通用的静态方法,可以使用本类来获取一些原始的报文信息。详细的操作参见例子
  10. /// </summary>
  11. public class MelsecHelper
  12. {
  13. #region Melsec Mc
  14. /// <summary>
  15. /// 解析A1E协议数据地址
  16. /// </summary>
  17. /// <param name="address">数据地址</param>
  18. /// <returns></returns>
  19. public static OperateResult<MelsecA1EDataType, ushort> McA1EAnalysisAddress(string address)
  20. {
  21. var result = new OperateResult<MelsecA1EDataType, ushort>();
  22. try
  23. {
  24. switch (address[0])
  25. {
  26. case 'X':
  27. case 'x':
  28. {
  29. result.Content1 = MelsecA1EDataType.X;
  30. result.Content2 = Convert.ToUInt16(address.Substring(1), MelsecA1EDataType.X.FromBase);
  31. break;
  32. }
  33. case 'Y':
  34. case 'y':
  35. {
  36. result.Content1 = MelsecA1EDataType.Y;
  37. result.Content2 = Convert.ToUInt16(address.Substring(1), MelsecA1EDataType.Y.FromBase);
  38. break;
  39. }
  40. case 'M':
  41. case 'm':
  42. {
  43. result.Content1 = MelsecA1EDataType.M;
  44. result.Content2 = Convert.ToUInt16(address.Substring(1), MelsecA1EDataType.M.FromBase);
  45. break;
  46. }
  47. case 'S':
  48. case 's':
  49. {
  50. result.Content1 = MelsecA1EDataType.S;
  51. result.Content2 = Convert.ToUInt16(address.Substring(1), MelsecA1EDataType.S.FromBase);
  52. break;
  53. }
  54. case 'D':
  55. case 'd':
  56. {
  57. result.Content1 = MelsecA1EDataType.D;
  58. result.Content2 = Convert.ToUInt16(address.Substring(1), MelsecA1EDataType.D.FromBase);
  59. break;
  60. }
  61. case 'R':
  62. case 'r':
  63. {
  64. result.Content1 = MelsecA1EDataType.R;
  65. result.Content2 = Convert.ToUInt16(address.Substring(1), MelsecA1EDataType.R.FromBase);
  66. break;
  67. }
  68. default: throw new Exception(StringResources.Language.NotSupportedDataType);
  69. }
  70. }
  71. catch (Exception ex)
  72. {
  73. result.Message = ex.Message;
  74. return result;
  75. }
  76. result.IsSuccess = true;
  77. return result;
  78. }
  79. /// <summary>
  80. /// 解析数据地址
  81. /// </summary>
  82. /// <param name="address">数据地址</param>
  83. /// <returns>解析值</returns>
  84. public static OperateResult<MelsecMcDataType, int> McAnalysisAddress( string address )
  85. {
  86. var result = new OperateResult<MelsecMcDataType, int>( );
  87. try
  88. {
  89. switch (address[0])
  90. {
  91. case 'M':
  92. case 'm':
  93. {
  94. result.Content1 = MelsecMcDataType.M;
  95. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.M.FromBase );
  96. break;
  97. }
  98. case 'X':
  99. case 'x':
  100. {
  101. result.Content1 = MelsecMcDataType.X;
  102. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.X.FromBase );
  103. break;
  104. }
  105. case 'Y':
  106. case 'y':
  107. {
  108. result.Content1 = MelsecMcDataType.Y;
  109. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Y.FromBase );
  110. break;
  111. }
  112. case 'D':
  113. case 'd':
  114. {
  115. result.Content1 = MelsecMcDataType.D;
  116. result.Content2 = Convert.ToInt32( address.Substring( 1 ), MelsecMcDataType.D.FromBase );
  117. break;
  118. }
  119. case 'W':
  120. case 'w':
  121. {
  122. result.Content1 = MelsecMcDataType.W;
  123. result.Content2 = Convert.ToInt32( address.Substring( 1 ), MelsecMcDataType.W.FromBase );
  124. break;
  125. }
  126. case 'L':
  127. case 'l':
  128. {
  129. result.Content1 = MelsecMcDataType.L;
  130. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.L.FromBase );
  131. break;
  132. }
  133. case 'F':
  134. case 'f':
  135. {
  136. result.Content1 = MelsecMcDataType.F;
  137. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.F.FromBase );
  138. break;
  139. }
  140. case 'V':
  141. case 'v':
  142. {
  143. result.Content1 = MelsecMcDataType.V;
  144. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.V.FromBase );
  145. break;
  146. }
  147. case 'B':
  148. case 'b':
  149. {
  150. result.Content1 = MelsecMcDataType.B;
  151. result.Content2 = Convert.ToInt32( address.Substring( 1 ), MelsecMcDataType.B.FromBase );
  152. break;
  153. }
  154. case 'R':
  155. case 'r':
  156. {
  157. result.Content1 = MelsecMcDataType.R;
  158. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.R.FromBase );
  159. break;
  160. }
  161. case 'S':
  162. case 's':
  163. {
  164. if (address[1] == 'N' || address[1] == 'n')
  165. {
  166. result.Content1 = MelsecMcDataType.SN;
  167. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.SN.FromBase );
  168. break;
  169. }
  170. else if (address[1] == 'S' || address[1] == 's')
  171. {
  172. result.Content1 = MelsecMcDataType.SS;
  173. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.SS.FromBase );
  174. break;
  175. }
  176. else if (address[1] == 'C' || address[1] == 'c')
  177. {
  178. result.Content1 = MelsecMcDataType.SC;
  179. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.SC.FromBase );
  180. break;
  181. }
  182. else
  183. {
  184. result.Content1 = MelsecMcDataType.S;
  185. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.S.FromBase );
  186. break;
  187. }
  188. }
  189. case 'Z':
  190. case 'z':
  191. {
  192. if (address.StartsWith( "ZR" ) || address.StartsWith( "zr" ))
  193. {
  194. result.Content1 = MelsecMcDataType.ZR;
  195. result.Content2 = Convert.ToInt32( address.Substring( 2 ), MelsecMcDataType.ZR.FromBase );
  196. break;
  197. }
  198. else
  199. {
  200. result.Content1 = MelsecMcDataType.Z;
  201. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Z.FromBase );
  202. break;
  203. }
  204. }
  205. case 'T':
  206. case 't':
  207. {
  208. if (address[1] == 'N' || address[1] == 'n')
  209. {
  210. result.Content1 = MelsecMcDataType.TN;
  211. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.TN.FromBase );
  212. break;
  213. }
  214. else if (address[1] == 'S' || address[1] == 's')
  215. {
  216. result.Content1 = MelsecMcDataType.TS;
  217. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.TS.FromBase );
  218. break;
  219. }
  220. else if (address[1] == 'C' || address[1] == 'c')
  221. {
  222. result.Content1 = MelsecMcDataType.TC;
  223. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.TC.FromBase );
  224. break;
  225. }
  226. else
  227. {
  228. throw new Exception( StringResources.Language.NotSupportedDataType );
  229. }
  230. }
  231. case 'C':
  232. case 'c':
  233. {
  234. if (address[1] == 'N' || address[1] == 'n')
  235. {
  236. result.Content1 = MelsecMcDataType.CN;
  237. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.CN.FromBase );
  238. break;
  239. }
  240. else if (address[1] == 'S' || address[1] == 's')
  241. {
  242. result.Content1 = MelsecMcDataType.CS;
  243. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.CS.FromBase );
  244. break;
  245. }
  246. else if (address[1] == 'C' || address[1] == 'c')
  247. {
  248. result.Content1 = MelsecMcDataType.CC;
  249. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.CC.FromBase );
  250. break;
  251. }
  252. else
  253. {
  254. throw new Exception( StringResources.Language.NotSupportedDataType );
  255. }
  256. }
  257. default: throw new Exception( StringResources.Language.NotSupportedDataType );
  258. }
  259. }
  260. catch (Exception ex)
  261. {
  262. result.Message = ex.Message;
  263. return result;
  264. }
  265. result.IsSuccess = true;
  266. result.Message = StringResources.Language.SuccessText;
  267. return result;
  268. }
  269. /// <summary>
  270. /// 基恩士解析数据地址
  271. /// </summary>
  272. /// <param name="address">数据地址</param>
  273. /// <returns>解析值</returns>
  274. public static OperateResult<MelsecMcDataType, int> KeyenceAnalysisAddress( string address )
  275. {
  276. var result = new OperateResult<MelsecMcDataType, int>( );
  277. try
  278. {
  279. switch (address[0])
  280. {
  281. case 'M':
  282. case 'm':
  283. {
  284. result.Content1 = MelsecMcDataType.Keyence_M;
  285. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_M.FromBase );
  286. break;
  287. }
  288. case 'X':
  289. case 'x':
  290. {
  291. result.Content1 = MelsecMcDataType.Keyence_X;
  292. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_X.FromBase );
  293. break;
  294. }
  295. case 'Y':
  296. case 'y':
  297. {
  298. result.Content1 = MelsecMcDataType.Keyence_Y;
  299. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_Y.FromBase );
  300. break;
  301. }
  302. case 'B':
  303. case 'b':
  304. {
  305. result.Content1 = MelsecMcDataType.Keyence_B;
  306. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_B.FromBase );
  307. break;
  308. }
  309. case 'L':
  310. case 'l':
  311. {
  312. result.Content1 = MelsecMcDataType.Keyence_L;
  313. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_L.FromBase );
  314. break;
  315. }
  316. case 'S':
  317. case 's':
  318. {
  319. if (address[1] == 'M' || address[1] == 'm')
  320. {
  321. result.Content1 = MelsecMcDataType.Keyence_SM;
  322. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.Keyence_SM.FromBase );
  323. break;
  324. }
  325. else if (address[1] == 'D' || address[1] == 'd')
  326. {
  327. result.Content1 = MelsecMcDataType.Keyence_SD;
  328. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.Keyence_SD.FromBase );
  329. break;
  330. }
  331. else
  332. {
  333. throw new Exception( StringResources.Language.NotSupportedDataType );
  334. }
  335. }
  336. case 'D':
  337. case 'd':
  338. {
  339. result.Content1 = MelsecMcDataType.Keyence_D;
  340. result.Content2 = Convert.ToInt32( address.Substring( 1 ), MelsecMcDataType.Keyence_D.FromBase );
  341. break;
  342. }
  343. case 'R':
  344. case 'r':
  345. {
  346. result.Content1 = MelsecMcDataType.Keyence_R;
  347. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_R.FromBase );
  348. break;
  349. }
  350. case 'Z':
  351. case 'z':
  352. {
  353. if (address[1] == 'R' || address[1] == 'r')
  354. {
  355. result.Content1 = MelsecMcDataType.Keyence_ZR;
  356. result.Content2 = Convert.ToInt32( address.Substring( 2 ), MelsecMcDataType.Keyence_ZR.FromBase );
  357. break;
  358. }
  359. else
  360. {
  361. throw new Exception( StringResources.Language.NotSupportedDataType );
  362. }
  363. }
  364. case 'W':
  365. case 'w':
  366. {
  367. result.Content1 = MelsecMcDataType.Keyence_W;
  368. result.Content2 = Convert.ToUInt16( address.Substring( 1 ), MelsecMcDataType.Keyence_W.FromBase );
  369. break;
  370. }
  371. case 'T':
  372. case 't':
  373. {
  374. if (address[1] == 'N' || address[1] == 'n')
  375. {
  376. result.Content1 = MelsecMcDataType.Keyence_TN;
  377. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.Keyence_TN.FromBase );
  378. break;
  379. }
  380. else if (address[1] == 'S' || address[1] == 's')
  381. {
  382. result.Content1 = MelsecMcDataType.Keyence_TS;
  383. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.Keyence_TS.FromBase );
  384. break;
  385. }
  386. else
  387. {
  388. throw new Exception( StringResources.Language.NotSupportedDataType );
  389. }
  390. }
  391. case 'C':
  392. case 'c':
  393. {
  394. if (address[1] == 'N' || address[1] == 'n')
  395. {
  396. result.Content1 = MelsecMcDataType.Keyence_CN;
  397. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.Keyence_CN.FromBase );
  398. break;
  399. }
  400. else if (address[1] == 'S' || address[1] == 's')
  401. {
  402. result.Content1 = MelsecMcDataType.Keyence_CS;
  403. result.Content2 = Convert.ToUInt16( address.Substring( 2 ), MelsecMcDataType.Keyence_CS.FromBase );
  404. break;
  405. }
  406. else
  407. {
  408. throw new Exception( StringResources.Language.NotSupportedDataType );
  409. }
  410. }
  411. default: throw new Exception( StringResources.Language.NotSupportedDataType );
  412. }
  413. }
  414. catch (Exception ex)
  415. {
  416. result.Message = ex.Message;
  417. return result;
  418. }
  419. result.IsSuccess = true;
  420. result.Message = StringResources.Language.SuccessText;
  421. return result;
  422. }
  423. /// <summary>
  424. /// 从地址,长度,是否位读取进行创建读取的MC的核心报文
  425. /// </summary>
  426. /// <param name="address">三菱的地址信息,具体格式参照<seealso cref="MelsecMcNet"/> 的注释说明</param>
  427. /// <param name="length">读取的长度信息</param>
  428. /// <param name="isBit">是否进行了位读取操作</param>
  429. /// <param name="analysisAddress">对地址分析的委托方法</param>
  430. /// <returns>带有成功标识的报文对象</returns>
  431. public static OperateResult<byte[]> BuildReadMcCoreCommand(string address, ushort length, bool isBit, Func<string, OperateResult<MelsecMcDataType, int>> analysisAddress)
  432. {
  433. OperateResult<MelsecMcDataType, int> analysis = analysisAddress( address );
  434. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  435. byte[] command = new byte[10];
  436. command[0] = 0x01; // 批量读取数据命令
  437. command[1] = 0x04;
  438. command[2] = isBit ? (byte)0x01 : (byte)0x00; // 以点为单位还是字为单位成批读取
  439. command[3] = 0x00;
  440. command[4] = BitConverter.GetBytes( analysis.Content2 )[0]; // 起始地址的地位
  441. command[5] = BitConverter.GetBytes( analysis.Content2 )[1];
  442. command[6] = BitConverter.GetBytes( analysis.Content2 )[2];
  443. command[7] = analysis.Content1.DataCode; // 指明读取的数据
  444. command[8] = (byte)(length % 256); // 软元件的长度
  445. command[9] = (byte)(length / 256);
  446. return OperateResult.CreateSuccessResult( command );
  447. }
  448. /// <summary>
  449. /// 从地址,长度,是否位读取进行创建读取Ascii格式的MC的核心报文
  450. /// </summary>
  451. /// <param name="address">三菱的地址信息,具体格式参照<seealso cref="MelsecMcNet"/> 的注释说明</param>
  452. /// <param name="length">读取的长度信息</param>
  453. /// <param name="isBit">是否进行了位读取操作</param>
  454. /// <param name="analysisAddress">对地址分析的委托方法</param>
  455. /// <returns>带有成功标识的报文对象</returns>
  456. public static OperateResult<byte[]> BuildAsciiReadMcCoreCommand(string address, ushort length, bool isBit, Func<string, OperateResult<MelsecMcDataType, int>> analysisAddress )
  457. {
  458. OperateResult<MelsecMcDataType, int> analysis = analysisAddress( address );
  459. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  460. byte[] command = new byte[20];
  461. command[ 0] = 0x30; // 批量读取数据命令
  462. command[ 1] = 0x34;
  463. command[ 2] = 0x30;
  464. command[ 3] = 0x31;
  465. command[ 4] = 0x30; // 以点为单位还是字为单位成批读取
  466. command[ 5] = 0x30;
  467. command[ 6] = 0x30;
  468. command[ 7] = isBit ? (byte)0x31 : (byte)0x30;
  469. command[ 8] = Encoding.ASCII.GetBytes( analysis.Content1.AsciiCode )[0]; // 软元件类型
  470. command[ 9] = Encoding.ASCII.GetBytes( analysis.Content1.AsciiCode )[1];
  471. command[10] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[0]; // 起始地址的地位
  472. command[11] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[1];
  473. command[12] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[2];
  474. command[13] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[3];
  475. command[14] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[4];
  476. command[15] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[5];
  477. command[16] = MelsecHelper.BuildBytesFromData( length )[0]; // 软元件点数
  478. command[17] = MelsecHelper.BuildBytesFromData( length )[1];
  479. command[18] = MelsecHelper.BuildBytesFromData( length )[2];
  480. command[19] = MelsecHelper.BuildBytesFromData( length )[3];
  481. return OperateResult.CreateSuccessResult( command );
  482. }
  483. /// <summary>
  484. /// 以字为单位,创建数据写入的核心报文
  485. /// </summary>
  486. /// <param name="address">三菱的地址信息,具体格式参照<seealso cref="MelsecMcNet"/> 的注释说明</param>
  487. /// <param name="value">实际的原始数据信息</param>
  488. /// <param name="analysisAddress">对地址分析的委托方法</param>
  489. /// <returns>带有成功标识的报文对象</returns>
  490. public static OperateResult<byte[]> BuildWriteWordCoreCommand(string address, byte[] value, Func<string, OperateResult<MelsecMcDataType, int>> analysisAddress )
  491. {
  492. OperateResult<MelsecMcDataType, int> analysis = analysisAddress( address );
  493. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  494. if (value == null) value = new byte[0];
  495. byte[] command = new byte[10 + value.Length];
  496. command[0] = 0x01; // 批量读取数据命令
  497. command[1] = 0x14;
  498. command[2] = 0x00; // 以字为单位成批读取
  499. command[3] = 0x00;
  500. command[4] = BitConverter.GetBytes( analysis.Content2 )[0]; // 起始地址的地位
  501. command[5] = BitConverter.GetBytes( analysis.Content2 )[1];
  502. command[6] = BitConverter.GetBytes( analysis.Content2 )[2];
  503. command[7] = analysis.Content1.DataCode; // 指明写入的数据
  504. command[8] = (byte)(value.Length / 2 % 256); // 软元件长度的地位
  505. command[9] = (byte)(value.Length / 2 / 256);
  506. value.CopyTo( command, 10 );
  507. return OperateResult.CreateSuccessResult( command );
  508. }
  509. /// <summary>
  510. /// 以字为单位,创建ASCII数据写入的核心报文
  511. /// </summary>
  512. /// <param name="address">三菱的地址信息,具体格式参照<seealso cref="MelsecMcNet"/> 的注释说明</param>
  513. /// <param name="value">实际的原始数据信息</param>
  514. /// <param name="analysisAddress">对地址分析的委托方法</param>
  515. /// <returns>带有成功标识的报文对象</returns>
  516. public static OperateResult<byte[]> BuildAsciiWriteWordCoreCommand(string address, byte[] value, Func<string, OperateResult<MelsecMcDataType, int>> analysisAddress )
  517. {
  518. OperateResult<MelsecMcDataType, int> analysis = analysisAddress( address );
  519. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  520. if (value == null) value = new byte[0];
  521. byte[] buffer = new byte[value.Length * 2];
  522. for (int i = 0; i < value.Length / 2; i++)
  523. {
  524. MelsecHelper.BuildBytesFromData( BitConverter.ToUInt16( value, i * 2 ) ).CopyTo( buffer, 4 * i );
  525. }
  526. value = buffer;
  527. byte[] command = new byte[20 + value.Length];
  528. command[ 0] = 0x31; // 批量写入的命令
  529. command[ 1] = 0x34;
  530. command[ 2] = 0x30;
  531. command[ 3] = 0x31;
  532. command[ 4] = 0x30; // 子命令
  533. command[ 5] = 0x30;
  534. command[ 6] = 0x30;
  535. command[ 7] = 0x30;
  536. command[ 8] = Encoding.ASCII.GetBytes( analysis.Content1.AsciiCode )[0]; // 软元件类型
  537. command[ 9] = Encoding.ASCII.GetBytes( analysis.Content1.AsciiCode )[1];
  538. command[10] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[0]; // 起始地址的地位
  539. command[11] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[1];
  540. command[12] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[2];
  541. command[13] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[3];
  542. command[14] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[4];
  543. command[15] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[5];
  544. command[16] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length / 4) )[0]; // 软元件点数
  545. command[17] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length / 4) )[1];
  546. command[18] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length / 4) )[2];
  547. command[19] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length / 4) )[3];
  548. value.CopyTo( command, 20 );
  549. return OperateResult.CreateSuccessResult( command );
  550. }
  551. /// <summary>
  552. /// 以位为单位,创建数据写入的核心报文
  553. /// </summary>
  554. /// <param name="address">三菱的地址信息,具体格式参照<seealso cref="MelsecMcNet"/> 的注释说明</param>
  555. /// <param name="value">原始的bool数组数据</param>
  556. /// <param name="analysisAddress">对地址分析的委托方法</param>
  557. /// <returns>带有成功标识的报文对象</returns>
  558. public static OperateResult<byte[]> BuildWriteBitCoreCommand( string address, bool[] value, Func<string, OperateResult<MelsecMcDataType, int>> analysisAddress )
  559. {
  560. OperateResult<MelsecMcDataType, int> analysis = analysisAddress( address );
  561. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  562. if (value == null) value = new bool[0];
  563. byte[] buffer = MelsecHelper.TransBoolArrayToByteData( value );
  564. byte[] command = new byte[10 + buffer.Length];
  565. command[0] = 0x01; // 批量写入数据命令
  566. command[1] = 0x14;
  567. command[2] = 0x01; // 以位为单位成批写入
  568. command[3] = 0x00;
  569. command[4] = BitConverter.GetBytes( analysis.Content2 )[0]; // 起始地址的地位
  570. command[5] = BitConverter.GetBytes( analysis.Content2 )[1];
  571. command[6] = BitConverter.GetBytes( analysis.Content2 )[2];
  572. command[7] = analysis.Content1.DataCode; // 指明写入的数据
  573. command[8] = (byte)(value.Length % 256); // 软元件长度的地位
  574. command[9] = (byte)(value.Length / 256);
  575. buffer.CopyTo( command, 10 );
  576. return OperateResult.CreateSuccessResult( command );
  577. }
  578. /// <summary>
  579. /// 以位为单位,创建ASCII数据写入的核心报文
  580. /// </summary>
  581. /// <param name="address">三菱的地址信息,具体格式参照<seealso cref="MelsecMcNet"/> 的注释说明</param>
  582. /// <param name="value">原始的bool数组数据</param>
  583. /// <param name="analysisAddress">对地址分析的委托方法</param>
  584. /// <returns>带有成功标识的报文对象</returns>
  585. public static OperateResult<byte[]> BuildAsciiWriteBitCoreCommand( string address, bool[] value, Func<string, OperateResult<MelsecMcDataType, int>> analysisAddress )
  586. {
  587. OperateResult<MelsecMcDataType, int> analysis = analysisAddress( address );
  588. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>( analysis );
  589. if (value == null) value = new bool[0];
  590. byte[] buffer = value.Select( m => m ? (byte)0x31 : (byte)0x30 ).ToArray( );
  591. byte[] command = new byte[20 + buffer.Length];
  592. command[0] = 0x31; // 批量写入的命令
  593. command[1] = 0x34;
  594. command[2] = 0x30;
  595. command[3] = 0x31;
  596. command[4] = 0x30; // 子命令
  597. command[5] = 0x30;
  598. command[6] = 0x30;
  599. command[7] = 0x31;
  600. command[8] = Encoding.ASCII.GetBytes( analysis.Content1.AsciiCode )[0]; // 软元件类型
  601. command[9] = Encoding.ASCII.GetBytes( analysis.Content1.AsciiCode )[1];
  602. command[10] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[0]; // 起始地址的地位
  603. command[11] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[1];
  604. command[12] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[2];
  605. command[13] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[3];
  606. command[14] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[4];
  607. command[15] = MelsecHelper.BuildBytesFromAddress( analysis.Content2, analysis.Content1 )[5];
  608. command[16] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length) )[0]; // 软元件点数
  609. command[17] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length) )[1];
  610. command[18] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length) )[2];
  611. command[19] = MelsecHelper.BuildBytesFromData( (ushort)(value.Length) )[3];
  612. buffer.CopyTo( command, 20 );
  613. return OperateResult.CreateSuccessResult( command );
  614. }
  615. #endregion
  616. #region Common Logic
  617. /// <summary>
  618. /// 从字节构建一个ASCII格式的地址字节
  619. /// </summary>
  620. /// <param name="value">字节信息</param>
  621. /// <returns>ASCII格式的地址</returns>
  622. internal static byte[] BuildBytesFromData( byte value )
  623. {
  624. return Encoding.ASCII.GetBytes( value.ToString( "X2" ) );
  625. }
  626. /// <summary>
  627. /// 从short数据构建一个ASCII格式地址字节
  628. /// </summary>
  629. /// <param name="value">short值</param>
  630. /// <returns>ASCII格式的地址</returns>
  631. internal static byte[] BuildBytesFromData( short value )
  632. {
  633. return Encoding.ASCII.GetBytes( value.ToString( "X4" ) );
  634. }
  635. /// <summary>
  636. /// 从ushort数据构建一个ASCII格式地址字节
  637. /// </summary>
  638. /// <param name="value">ushort值</param>
  639. /// <returns>ASCII格式的地址</returns>
  640. internal static byte[] BuildBytesFromData( ushort value )
  641. {
  642. return Encoding.ASCII.GetBytes( value.ToString( "X4" ) );
  643. }
  644. /// <summary>
  645. /// 从三菱的地址中构建MC协议的6字节的ASCII格式的地址
  646. /// </summary>
  647. /// <param name="address">三菱地址</param>
  648. /// <param name="type">三菱的数据类型</param>
  649. /// <returns>6字节的ASCII格式的地址</returns>
  650. internal static byte[] BuildBytesFromAddress( int address, MelsecMcDataType type )
  651. {
  652. return Encoding.ASCII.GetBytes( address.ToString( type.FromBase == 10 ? "D6" : "X6" ) );
  653. }
  654. /// <summary>
  655. /// 从字节数组构建一个ASCII格式的地址字节
  656. /// </summary>
  657. /// <param name="value">字节信息</param>
  658. /// <returns>ASCII格式的地址</returns>
  659. internal static byte[] BuildBytesFromData( byte[] value )
  660. {
  661. byte[] buffer = new byte[value.Length * 2];
  662. for (int i = 0; i < value.Length; i++)
  663. {
  664. BuildBytesFromData( value[i] ).CopyTo( buffer, 2 * i );
  665. }
  666. return buffer;
  667. }
  668. /// <summary>
  669. /// 将0,1,0,1的字节数组压缩成三菱格式的字节数组来表示开关量的
  670. /// </summary>
  671. /// <param name="value">原始的数据字节</param>
  672. /// <returns>压缩过后的数据字节</returns>
  673. internal static byte[] TransBoolArrayToByteData( byte[] value )
  674. {
  675. int length = (value.Length + 1) / 2;
  676. byte[] buffer = new byte[length];
  677. for (int i = 0; i < length; i++)
  678. {
  679. if (value[i * 2 + 0] != 0x00) buffer[i] += 0x10;
  680. if ((i * 2 + 1) < value.Length)
  681. {
  682. if (value[i * 2 + 1] != 0x00) buffer[i] += 0x01;
  683. }
  684. }
  685. return buffer;
  686. }
  687. /// <summary>
  688. /// 将bool的组压缩成三菱格式的字节数组来表示开关量的
  689. /// </summary>
  690. /// <param name="value">原始的数据字节</param>
  691. /// <returns>压缩过后的数据字节</returns>
  692. internal static byte[] TransBoolArrayToByteData( bool[] value )
  693. {
  694. int length = (value.Length + 1) / 2;
  695. byte[] buffer = new byte[length];
  696. for (int i = 0; i < length; i++)
  697. {
  698. if (value[i * 2 + 0]) buffer[i] += 0x10;
  699. if ((i * 2 + 1) < value.Length)
  700. {
  701. if (value[i * 2 + 1]) buffer[i] += 0x01;
  702. }
  703. }
  704. return buffer;
  705. }
  706. #endregion
  707. #region CRC Check
  708. /// <summary>
  709. /// 计算Fx协议指令的和校验信息
  710. /// </summary>
  711. /// <param name="data">字节数据</param>
  712. /// <returns>校验之后的数据</returns>
  713. internal static byte[] FxCalculateCRC( byte[] data )
  714. {
  715. int sum = 0;
  716. for (int i = 1; i < data.Length - 2; i++)
  717. {
  718. sum += data[i];
  719. }
  720. return BuildBytesFromData( (byte)sum );
  721. }
  722. /// <summary>
  723. /// 检查指定的和校验是否是正确的
  724. /// </summary>
  725. /// <param name="data">字节数据</param>
  726. /// <returns>是否成功</returns>
  727. internal static bool CheckCRC( byte[] data )
  728. {
  729. byte[] crc = FxCalculateCRC( data );
  730. if (crc[0] != data[data.Length - 2]) return false;
  731. if (crc[1] != data[data.Length - 1]) return false;
  732. return true;
  733. }
  734. #endregion
  735. }
  736. }