SiemensS7Net.cs 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. using MECF.Framework.RT.Core.IoProviders.Common;
  7. using MECF.Framework.RT.Core.IoProviders.Common.IMessage;
  8. using MECF.Framework.RT.Core.IoProviders.Common.Net.NetworkBase;
  9. using MECF.Framework.RT.Core.IoProviders.Common.Transfer;
  10. namespace MECF.Framework.RT.Core.IoProviders.Siemens
  11. { /// <summary>
  12. /// 一个西门子的客户端类,使用S7协议来进行数据交互 ->
  13. /// A Siemens client class that uses the S7 protocol for data interaction
  14. /// </summary>
  15. /// <remarks>
  16. /// 地址支持的列表如下:
  17. /// <list type="table">
  18. /// <listheader>
  19. /// <term>地址名称</term>
  20. /// <term>地址代号</term>
  21. /// <term>示例</term>
  22. /// <term>地址进制</term>
  23. /// <term>字操作</term>
  24. /// <term>位操作</term>
  25. /// <term>备注</term>
  26. /// </listheader>
  27. /// <item>
  28. /// <term>中间寄存器</term>
  29. /// <term>M</term>
  30. /// <term>M100,M200</term>
  31. /// <term>10</term>
  32. /// <term>√</term>
  33. /// <term>√</term>
  34. /// <term></term>
  35. /// </item>
  36. /// <item>
  37. /// <term>输入寄存器</term>
  38. /// <term>I</term>
  39. /// <term>I100,I200</term>
  40. /// <term>10</term>
  41. /// <term>√</term>
  42. /// <term>√</term>
  43. /// <term></term>
  44. /// </item>
  45. /// <item>
  46. /// <term>输出寄存器</term>
  47. /// <term>Q</term>
  48. /// <term>Q100,Q200</term>
  49. /// <term>10</term>
  50. /// <term>√</term>
  51. /// <term>√</term>
  52. /// <term></term>
  53. /// </item>
  54. /// <item>
  55. /// <term>DB块寄存器</term>
  56. /// <term>DB</term>
  57. /// <term>DB1.100,DB1.200</term>
  58. /// <term>10</term>
  59. /// <term>√</term>
  60. /// <term>√</term>
  61. /// <term></term>
  62. /// </item>
  63. /// <item>
  64. /// <term>V寄存器</term>
  65. /// <term>V</term>
  66. /// <term>V100,V200</term>
  67. /// <term>10</term>
  68. /// <term>√</term>
  69. /// <term>√</term>
  70. /// <term>V寄存器本质就是DB块1</term>
  71. /// </item>
  72. /// <item>
  73. /// <term>定时器的值</term>
  74. /// <term>T</term>
  75. /// <term>T100,T200</term>
  76. /// <term>10</term>
  77. /// <term>√</term>
  78. /// <term>√</term>
  79. /// <term>未测试通过</term>
  80. /// </item>
  81. /// <item>
  82. /// <term>计数器的值</term>
  83. /// <term>C</term>
  84. /// <term>C100,C200</term>
  85. /// <term>10</term>
  86. /// <term>√</term>
  87. /// <term>√</term>
  88. /// <term>未测试通过</term>
  89. /// </item>
  90. /// </list>
  91. /// <note type="important">对于200smartPLC的V区,就是DB1.X,例如,V100=DB1.100</note>
  92. /// </remarks>
  93. /// <example>
  94. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="Usage" title="简单的短连接使用" />
  95. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="Usage2" title="简单的长连接使用" />
  96. /// </example>
  97. public class SiemensS7Net : NetworkDeviceBase<S7Message, ReverseBytesTransform>
  98. {
  99. #region Constructor
  100. /// <summary>
  101. /// 实例化一个西门子的S7协议的通讯对象 ->
  102. /// Instantiate a communication object for a Siemens S7 protocol
  103. /// </summary>
  104. /// <param name="siemens">指定西门子的型号</param>
  105. public SiemensS7Net(SiemensPLCS siemens)
  106. {
  107. Initialization(siemens, string.Empty);
  108. }
  109. /// <summary>
  110. /// 实例化一个西门子的S7协议的通讯对象并指定Ip地址 ->
  111. /// Instantiate a communication object for a Siemens S7 protocol and specify an IP address
  112. /// </summary>
  113. /// <param name="siemens">指定西门子的型号</param>
  114. /// <param name="ipAddress">Ip地址</param>
  115. public SiemensS7Net(SiemensPLCS siemens, string ipAddress)
  116. {
  117. Initialization(siemens, ipAddress);
  118. }
  119. /// <summary>
  120. /// 初始化方法 -> Initialize method
  121. /// </summary>
  122. /// <param name="siemens">指定西门子的型号 -> Designation of Siemens</param>
  123. /// <param name="ipAddress">Ip地址 -> IpAddress</param>
  124. private void Initialization(SiemensPLCS siemens, string ipAddress)
  125. {
  126. WordLength = 2;
  127. IpAddress = ipAddress;
  128. Port = 102;
  129. CurrentPlc = siemens;
  130. switch (siemens)
  131. {
  132. case SiemensPLCS.S1200: plcHead1[21] = 0; break;
  133. case SiemensPLCS.S300: plcHead1[21] = 2; break;
  134. case SiemensPLCS.S400: plcHead1[21] = 3; plcHead1[17] = 0x00; break;
  135. case SiemensPLCS.S1500: plcHead1[21] = 0; break;
  136. case SiemensPLCS.S200Smart:
  137. {
  138. plcHead1 = plcHead1_200smart;
  139. plcHead2 = plcHead2_200smart;
  140. break;
  141. }
  142. case SiemensPLCS.S200:
  143. {
  144. plcHead1 = plcHead1_200;
  145. plcHead2 = plcHead2_200;
  146. break;
  147. }
  148. default: plcHead1[18] = 0; break;
  149. }
  150. }
  151. /// <summary>
  152. /// PLC的槽号,针对S7-400的PLC设置的
  153. /// </summary>
  154. public byte Slot
  155. {
  156. get => plc_slot;
  157. set
  158. {
  159. plc_slot = value;
  160. plcHead1[21] = (byte)((this.plc_rack * 0x20) + this.plc_slot);
  161. }
  162. }
  163. /// <summary>
  164. /// PLC的机架号,针对S7-400的PLC设置的
  165. /// </summary>
  166. public byte Rack
  167. {
  168. get => plc_rack;
  169. set
  170. {
  171. this.plc_rack = value;
  172. plcHead1[21] = (byte)((this.plc_rack * 0x20) + this.plc_slot);
  173. }
  174. }
  175. /// <summary>
  176. /// 获取或设置当前PLC的连接方式,PG: 0x01,OP: 0x02,S7Basic: 0x03...0x10<br />
  177. /// Get or set the current PLC connection mode, PG: 0x01, OP: 0x02, S7Basic: 0x03...0x10
  178. /// </summary>
  179. public byte ConnectionType
  180. {
  181. get => this.plcHead1[20];
  182. set
  183. {
  184. if (CurrentPlc == SiemensPLCS.S200 ||
  185. CurrentPlc == SiemensPLCS.S200Smart)
  186. {
  187. }
  188. else
  189. {
  190. this.plcHead1[20] = value;
  191. }
  192. }
  193. }
  194. /// <summary>
  195. /// 西门子相关的本地TSAP参数信息<br />
  196. /// A parameter information related to Siemens
  197. /// </summary>
  198. public int LocalTSAP
  199. {
  200. get => this.plcHead1[16] * 256 + this.plcHead1[17];
  201. set
  202. {
  203. if (CurrentPlc == SiemensPLCS.S200 ||
  204. CurrentPlc == SiemensPLCS.S200Smart)
  205. {
  206. }
  207. else
  208. {
  209. this.plcHead1[16] = BitConverter.GetBytes(value)[1];
  210. this.plcHead1[17] = BitConverter.GetBytes(value)[0];
  211. }
  212. }
  213. }
  214. #endregion
  215. #region NetworkDoubleBase Override
  216. /// <summary>
  217. /// 连接上服务器后需要进行的二次握手操作 -> Two handshake actions required after connecting to the server
  218. /// </summary>
  219. /// <param name="socket">网络套接字 -> Network sockets</param>
  220. /// <returns>是否初始化成功,依据具体的协议进行重写 ->
  221. /// Whether the initialization succeeds and is rewritten according to the specific protocol</returns>
  222. protected override OperateResult InitializationOnConnect(Socket socket)
  223. {
  224. // 第一次握手 -> First handshake
  225. OperateResult<byte[]> read_first = ReadFromCoreServer(socket, plcHead1);
  226. if (!read_first.IsSuccess) return read_first;
  227. // 第二次握手 -> Second handshake
  228. OperateResult<byte[]> read_second = ReadFromCoreServer(socket, plcHead2);
  229. if (!read_second.IsSuccess) return read_second;
  230. // 返回成功的信号 -> Return a successful signal
  231. return OperateResult.CreateSuccessResult();
  232. }
  233. #endregion
  234. #region Read OrderNumber
  235. /// <summary>
  236. /// 从PLC读取订货号信息 -> Reading order number information from PLC
  237. /// </summary>
  238. /// <returns>CPU的订货号信息 -> Order number information for the CPU</returns>
  239. public OperateResult<string> ReadOrderNumber()
  240. {
  241. OperateResult<byte[]> read = ReadFromCoreServer(plcOrderNumber);
  242. if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);
  243. return OperateResult.CreateSuccessResult(Encoding.ASCII.GetString(read.Content, 71, 20));
  244. }
  245. #endregion
  246. #region Start Stop
  247. /// <summary>
  248. /// 对PLC进行热启动
  249. /// </summary>
  250. /// <returns>是否启动成功的结果对象</returns>
  251. public OperateResult HotStart()
  252. {
  253. OperateResult<byte[]> read = ReadFromCoreServer(S7_HOT_START);
  254. if (!read.IsSuccess) return read;
  255. if (read.Content.Length < 19) return new OperateResult("Receive error");
  256. if (read.Content[19] != pduStart) return new OperateResult("Can not start PLC");
  257. else if (read.Content[20] != pduAlreadyStarted) return new OperateResult("Can not start PLC");
  258. return OperateResult.CreateSuccessResult();
  259. }
  260. /// <summary>
  261. /// 对PLC进行冷启动
  262. /// </summary>
  263. /// <returns>是否启动成功的结果对象</returns>
  264. public OperateResult ColdStart()
  265. {
  266. OperateResult<byte[]> read = ReadFromCoreServer(S7_COLD_START);
  267. if (!read.IsSuccess) return read;
  268. if (read.Content.Length < 19) return new OperateResult("Receive error");
  269. if (read.Content[19] != pduStart) return new OperateResult("Can not start PLC");
  270. else if (read.Content[20] != pduAlreadyStarted) return new OperateResult("Can not start PLC");
  271. return OperateResult.CreateSuccessResult();
  272. }
  273. /// <summary>
  274. /// 对PLC进行停止
  275. /// </summary>
  276. /// <returns>是否启动成功的结果对象</returns>
  277. public OperateResult Stop()
  278. {
  279. OperateResult<byte[]> read = ReadFromCoreServer(S7_STOP);
  280. if (!read.IsSuccess) return read;
  281. if (read.Content.Length < 19) return new OperateResult("Receive error");
  282. if (read.Content[19] != pduStop) return new OperateResult("Can not stop PLC");
  283. else if (read.Content[20] != pduAlreadyStopped) return new OperateResult("Can not stop PLC");
  284. return OperateResult.CreateSuccessResult();
  285. }
  286. #endregion
  287. #region Read Support
  288. /// <summary>
  289. /// 从PLC读取数据,地址格式为I100,Q100,DB20.100,M100,T100,C100以字节为单位 ->
  290. /// Read data from PLC, address format I100,Q100,DB20.100,M100,T100,C100 in bytes
  291. /// </summary>
  292. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 ->
  293. /// Starting address, formatted as I100,M100,Q100,DB20.100</param>
  294. /// <param name="length">读取的数量,以字节为单位 -> The number of reads, in bytes</param>
  295. /// <returns>是否读取成功的结果对象 -> Whether to read the successful result object</returns>
  296. /// <remarks>
  297. /// <note type="important">对于200smartPLC的V区,就是DB1.X,例如,V100=DB1.100</note>
  298. /// </remarks>
  299. /// <example>
  300. /// 假设起始地址为M100,M100存储了温度,100.6℃值为1006,M102存储了压力,1.23Mpa值为123,M104,M105,M106,M107存储了产量计数,读取如下:
  301. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="ReadExample2" title="Read示例" />
  302. /// 以下是读取不同类型数据的示例
  303. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="ReadExample1" title="Read示例" />
  304. /// </example>
  305. public override OperateResult<byte[]> Read(string address, ushort length)
  306. {
  307. OperateResult<byte, int, ushort> addressResult = AnalysisAddress(address);
  308. if (!addressResult.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(addressResult);
  309. // 如果长度超过200,分批次读取 -> If the length is more than 200, read in batches
  310. List<byte> bytesContent = new List<byte>();
  311. ushort alreadyFinished = 0;
  312. while (alreadyFinished < length)
  313. {
  314. ushort readLength = (ushort)Math.Min(length - alreadyFinished, 200);
  315. OperateResult<byte[]> read = Read(new OperateResult<byte, int, ushort>[] { addressResult }, new ushort[] { readLength });
  316. if (!read.IsSuccess) return read;
  317. bytesContent.AddRange(read.Content);
  318. alreadyFinished += readLength;
  319. addressResult.Content2 += readLength * 8;
  320. }
  321. return OperateResult.CreateSuccessResult(bytesContent.ToArray());
  322. }
  323. /// <summary>
  324. /// 从PLC读取数据,地址格式为I100,Q100,DB20.100,M100,以位为单位 ->
  325. /// Read the data from the PLC, the address format is I100,Q100,DB20.100,M100, in bits units
  326. /// </summary>
  327. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 ->
  328. /// Starting address, formatted as I100,M100,Q100,DB20.100</param>
  329. /// <returns>是否读取成功的结果对象 -> Whether to read the successful result object</returns>
  330. private OperateResult<byte[]> ReadBitFromPLC(string address)
  331. {
  332. // 指令生成 -> Build bit read command
  333. OperateResult<byte[]> command = BuildBitReadCommand(address);
  334. if (!command.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(command);
  335. // 核心交互 -> Core interactive
  336. OperateResult<byte[]> read = ReadFromCoreServer(command.Content);
  337. if (!read.IsSuccess) return read;
  338. // 分析结果 -> Analysis read result
  339. int receiveCount = 1;
  340. if (read.Content.Length >= 21 && read.Content[20] == 1)
  341. {
  342. byte[] buffer = new byte[receiveCount];
  343. if (22 < read.Content.Length)
  344. {
  345. if (read.Content[21] == 0xFF &&
  346. read.Content[22] == 0x03)
  347. {
  348. buffer[0] = read.Content[25];
  349. }
  350. }
  351. return OperateResult.CreateSuccessResult(buffer);
  352. }
  353. else
  354. {
  355. return new OperateResult<byte[]>(read.ErrorCode, StringResources.Language.SiemensDataLengthCheckFailed);
  356. }
  357. }
  358. /// <summary>
  359. /// 一次性从PLC获取所有的数据,按照先后顺序返回一个统一的Buffer,需要按照顺序处理,两个数组长度必须一致 ->
  360. /// One-time from the PLC to obtain all the data, in order to return a unified buffer, need to be processed sequentially, two array length must be consistent
  361. /// </summary>
  362. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 ->
  363. /// Starting address, formatted as I100,M100,Q100,DB20.100</param>
  364. /// <param name="length">数据长度数组 -> Array of data Lengths</param>
  365. /// <returns>是否读取成功的结果对象 -> Whether to read the successful result object</returns>
  366. /// <exception cref="NullReferenceException"></exception>
  367. /// <remarks>
  368. /// <note type="warning">批量读取的长度有限制,最大为19个地址</note>
  369. /// </remarks>
  370. /// <example>
  371. /// 参照<see cref="Read(string, ushort)"/>
  372. /// </example>
  373. public OperateResult<byte[]> Read(string[] address, ushort[] length)
  374. {
  375. OperateResult<byte, int, ushort>[] addressResult = new OperateResult<byte, int, ushort>[address.Length];
  376. for (int i = 0; i < address.Length; i++)
  377. {
  378. OperateResult<byte, int, ushort> tmp = AnalysisAddress(address[i]);
  379. if (!tmp.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(addressResult[i]);
  380. addressResult[i] = tmp;
  381. }
  382. return Read(addressResult, length);
  383. }
  384. private OperateResult<byte[]> Read(OperateResult<byte, int, ushort>[] address, ushort[] length)
  385. {
  386. // 构建指令 -> Build read command
  387. OperateResult<byte[]> command = BuildReadCommand(address, length);
  388. if (!command.IsSuccess) return command;
  389. // 核心交互 -> Core Interactions
  390. OperateResult<byte[]> read = ReadFromCoreServer(command.Content);
  391. if (!read.IsSuccess) return read;
  392. // 分析结果 -> Analysis results
  393. int receiveCount = 0;
  394. for (int i = 0; i < length.Length; i++)
  395. {
  396. receiveCount += length[i];
  397. }
  398. if (read.Content.Length >= 21 && read.Content[20] == length.Length)
  399. {
  400. byte[] buffer = new byte[receiveCount];
  401. int kk = 0;
  402. int ll = 0;
  403. for (int ii = 21; ii < read.Content.Length; ii++)
  404. {
  405. if ((ii + 1) < read.Content.Length)
  406. {
  407. if (read.Content[ii] == 0xFF &&
  408. read.Content[ii + 1] == 0x04)
  409. {
  410. Array.Copy(read.Content, ii + 4, buffer, ll, length[kk]);
  411. ii += length[kk] + 3;
  412. ll += length[kk];
  413. kk++;
  414. }
  415. }
  416. }
  417. return OperateResult.CreateSuccessResult(buffer);
  418. }
  419. else
  420. {
  421. return new OperateResult<byte[]>(read.ErrorCode, StringResources.Language.SiemensDataLengthCheckFailed);
  422. }
  423. }
  424. /// <summary>
  425. /// 读取指定地址的bool数据,地址格式为I100,M100,Q100,DB20.100 ->
  426. /// reads bool data for the specified address in the format I100,M100,Q100,DB20.100
  427. /// </summary>
  428. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 ->
  429. /// Starting address, formatted as I100,M100,Q100,DB20.100</param>
  430. /// <returns>是否读取成功的结果对象 -> Whether to read the successful result object</returns>
  431. /// <remarks>
  432. /// <note type="important">
  433. /// 对于200smartPLC的V区,就是DB1.X,例如,V100=DB1.100
  434. /// </note>
  435. /// </remarks>
  436. /// <example>
  437. /// 假设读取M100.0的位是否通断
  438. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="ReadBool" title="ReadBool示例" />
  439. /// </example>
  440. public OperateResult<bool> ReadBool(string address)
  441. {
  442. return ByteTransformHelper.GetResultFromBytes(ReadBitFromPLC(address), m => m[0] != 0x00);
  443. }
  444. /// <summary>
  445. /// 读取指定地址的byte数据,地址格式I100,M100,Q100,DB20.100 ->
  446. /// Reads the byte data of the specified address, the address format I100,Q100,DB20.100,M100
  447. /// </summary>
  448. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 ->
  449. /// Starting address, formatted as I100,M100,Q100,DB20.100</param>
  450. /// <returns>是否读取成功的结果对象 -> Whether to read the successful result object</returns>
  451. /// <example>参考<see cref="Read(string, ushort)"/>的注释</example>
  452. public OperateResult<byte> ReadByte(string address)
  453. {
  454. return ByteTransformHelper.GetResultFromArray(Read(address, 1));
  455. }
  456. #endregion
  457. #region Write Base
  458. /// <summary>
  459. /// 基础的写入数据的操作支持 -> Operational support for the underlying write data
  460. /// </summary>
  461. /// <param name="entireValue">完整的字节数据 -> Full byte data</param>
  462. /// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>
  463. private OperateResult WriteBase(byte[] entireValue)
  464. {
  465. OperateResult<byte[]> write = ReadFromCoreServer(entireValue);
  466. if (!write.IsSuccess) return write;
  467. if (write.Content[write.Content.Length - 1] != 0xFF)
  468. {
  469. // 写入异常 -> WriteError
  470. return new OperateResult(write.Content[write.Content.Length - 1], StringResources.Language.SiemensWriteError + write.Content[write.Content.Length - 1]);
  471. }
  472. else
  473. {
  474. return OperateResult.CreateSuccessResult();
  475. }
  476. }
  477. /// <summary>
  478. /// 将数据写入到PLC数据,地址格式为I100,Q100,DB20.100,M100,以字节为单位 ->
  479. /// Writes data to the PLC data, in the address format I100,Q100,DB20.100,M100, in bytes
  480. /// </summary>
  481. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 ->
  482. /// Starting address, formatted as I100,M100,Q100,DB20.100</param>
  483. /// <param name="value">写入的原始数据 -> Raw data written to</param>
  484. /// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>
  485. /// <example>
  486. /// 假设起始地址为M100,M100,M101存储了温度,100.6℃值为1006,M102,M103存储了压力,1.23Mpa值为123,M104-M107存储了产量计数,写入如下:
  487. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="WriteExample2" title="Write示例" />
  488. /// 以下是写入不同类型数据的示例
  489. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="WriteExample1" title="Write示例" />
  490. /// </example>
  491. public override OperateResult Write(string address, byte[] value)
  492. {
  493. OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
  494. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
  495. int length = value.Length;
  496. ushort alreadyFinished = 0;
  497. while (alreadyFinished < length)
  498. {
  499. ushort writeLength = (ushort)Math.Min(length - alreadyFinished, 200);
  500. byte[] buffer = ByteTransform.TransByte(value, alreadyFinished, writeLength);
  501. OperateResult<byte[]> command = BuildWriteByteCommand(analysis, buffer);
  502. if (!command.IsSuccess) return command;
  503. OperateResult write = WriteBase(command.Content);
  504. if (!write.IsSuccess) return write;
  505. alreadyFinished += writeLength;
  506. analysis.Content2 += writeLength * 8;
  507. }
  508. return OperateResult.CreateSuccessResult();
  509. }
  510. /// <summary>
  511. /// 写入PLC的一个位,例如"M100.6","I100.7","Q100.0","DB20.100.0",如果只写了"M100"默认为"M100.0" ->
  512. /// Write a bit of PLC, for example "M100.6", "I100.7", "Q100.0", "DB20.100.0", if only write "M100" defaults to "M100.0"
  513. /// </summary>
  514. /// <param name="address">起始地址,格式为"M100.6", "I100.7", "Q100.0", "DB20.100.0" ->
  515. /// Start address, format "M100.6", "I100.7", "Q100.0", "DB20.100.0"</param>
  516. /// <param name="value">写入的数据,True或是False -> Writes the data, either True or False</param>
  517. /// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>
  518. /// <example>
  519. /// 假设写入M100.0的位是否通断
  520. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Profinet\SiemensS7Net.cs" region="WriteBool" title="WriteBool示例" />
  521. /// </example>
  522. public OperateResult Write(string address, bool value)
  523. {
  524. // 生成指令 -> Build Command
  525. OperateResult<byte[]> command = BuildWriteBitCommand(address, value);
  526. if (!command.IsSuccess) return command;
  527. return WriteBase(command.Content);
  528. }
  529. #endregion
  530. #region Write bool[]
  531. /// <summary>
  532. /// 向PLC中写入bool数组,比如你写入M100,那么data[0]对应M100.0 ->
  533. /// Write the bool array to the PLC, for example, if you write M100, then data[0] corresponds to M100.0
  534. /// </summary>
  535. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 -> Starting address, formatted as I100,mM100,Q100,DB20.100</param>
  536. /// <param name="values">要写入的bool数组,长度为8的倍数 -> The bool array to write, a multiple of 8 in length</param>
  537. /// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>
  538. /// <remarks>
  539. /// <note type="warning">
  540. /// 批量写入bool数组存在一定的风险,原因是只能批量写入长度为8的倍数的数组,否则会影响其他的位的数据,请谨慎使用。
  541. /// </note>
  542. /// </remarks>
  543. public OperateResult Write(string address, bool[] values)
  544. {
  545. return Write(address, SoftBasic.BoolArrayToByte(values));
  546. }
  547. #endregion
  548. #region Write Byte
  549. /// <summary>
  550. /// 向PLC中写入byte数据,返回值说明 -> Write byte data to the PLC, return value description
  551. /// </summary>
  552. /// <param name="address">起始地址,格式为I100,M100,Q100,DB20.100 -> Starting address, formatted as I100,mM100,Q100,DB20.100</param>
  553. /// <param name="value">byte数据 -> Byte data</param>
  554. /// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>
  555. public OperateResult Write(string address, byte value)
  556. {
  557. return Write(address, new byte[] { value });
  558. }
  559. #endregion
  560. #region ReadWrite String
  561. /// <summary>
  562. /// 向设备中写入字符串,编码格式为ASCII
  563. /// </summary>
  564. /// <param name="address">数据地址</param>
  565. /// <param name="value">字符串数据</param>
  566. /// <returns>是否写入成功的结果对象</returns>
  567. /// <example>
  568. /// 以下为三菱的连接对象示例,其他的设备读写情况参照下面的代码:
  569. /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkDeviceBase.cs" region="WriteString" title="String类型示例" />
  570. /// </example>
  571. public override OperateResult Write(string address, string value)
  572. {
  573. if (value == null) value = string.Empty;
  574. byte[] buffer = Encoding.ASCII.GetBytes(value);
  575. if (CurrentPlc != SiemensPLCS.S200Smart)
  576. {
  577. // need read one time
  578. OperateResult<byte[]> readLength = Read(address, 2);
  579. if (!readLength.IsSuccess) return readLength;
  580. if (readLength.Content[0] == 255) return new OperateResult<string>("Value in plc is not string type");
  581. if (readLength.Content[0] == 0) readLength.Content[0] = 254; // allow to create new string
  582. if (value.Length > readLength.Content[0]) return new OperateResult<string>("String length is too long than plc defined");
  583. return Write(address, SoftBasic.SpliceTwoByteArray(new byte[] { readLength.Content[0], (byte)buffer.Length }, buffer));
  584. }
  585. else
  586. {
  587. return Write(address, SoftBasic.SpliceTwoByteArray(new byte[] { (byte)buffer.Length }, buffer));
  588. }
  589. }
  590. /// <summary>
  591. /// 读取西门子的地址的字符串信息,这个信息是和西门子绑定在一起,长度随西门子的信息动态变化的
  592. /// </summary>
  593. /// <param name="address">数据地址,具体的格式需要参照类的说明文档</param>
  594. /// <returns>带有是否成功的字符串结果类对象</returns>
  595. public OperateResult<string> ReadString(string address)
  596. {
  597. if (CurrentPlc != SiemensPLCS.S200Smart)
  598. {
  599. var read = Read(address, 2);
  600. if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);
  601. if (read.Content[0] == 0 || read.Content[0] == 255) return new OperateResult<string>("Value in plc is not string type"); // max string length can't be zero
  602. var readString = Read(address, (ushort)(2 + read.Content[1]));
  603. if (!readString.IsSuccess) return OperateResult.CreateFailedResult<string>(readString);
  604. return OperateResult.CreateSuccessResult(Encoding.ASCII.GetString(readString.Content, 2, readString.Content.Length - 2));
  605. }
  606. else
  607. {
  608. var read = Read(address, 1);
  609. if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);
  610. var readString = Read(address, (ushort)(1 + read.Content[0]));
  611. if (!readString.IsSuccess) return OperateResult.CreateFailedResult<string>(readString);
  612. return OperateResult.CreateSuccessResult(Encoding.ASCII.GetString(readString.Content, 1, readString.Content.Length - 1));
  613. }
  614. }
  615. #endregion
  616. #region Head Codes
  617. private byte[] plcHead1 = new byte[22]
  618. {
  619. 0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC0,0x01,0x0A,0xC1,0x02,
  620. 0x01,0x02,0xC2,0x02,0x01,0x00
  621. };
  622. private byte[] plcHead2 = new byte[25]
  623. {
  624. 0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0x04,0x00,0x00,0x08,0x00,
  625. 0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x01,0xE0
  626. };
  627. private byte[] plcOrderNumber = new byte[]
  628. {
  629. 0x03,0x00,0x00,0x21,0x02,0xF0,0x80,0x32,0x07,0x00,0x00,0x00,0x01,0x00,0x08,0x00,
  630. 0x08,0x00,0x01,0x12,0x04,0x11,0x44,0x01,0x00,0xFF,0x09,0x00,0x04,0x00,0x11,0x00,
  631. 0x00
  632. };
  633. private SiemensPLCS CurrentPlc = SiemensPLCS.S1200;
  634. private byte[] plcHead1_200smart = new byte[22]
  635. {
  636. 0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,0x02,0x10,0x00,0xC2,
  637. 0x02,0x03,0x00,0xC0,0x01,0x0A
  638. };
  639. private byte[] plcHead2_200smart = new byte[25]
  640. {
  641. 0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0xCC,0xC1,0x00,0x08,0x00,
  642. 0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
  643. };
  644. private byte[] plcHead1_200 = new byte[]
  645. {
  646. 0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,0x02,0x4D,0x57,0xC2,
  647. 0x02,0x4D,0x57,0xC0,0x01,0x09
  648. };
  649. private byte[] plcHead2_200 = new byte[]
  650. {
  651. 0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
  652. 0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
  653. };
  654. byte[] S7_STOP = {
  655. 0x03, 0x00, 0x00, 0x21, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00,
  656. 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41,
  657. 0x4d
  658. };
  659. byte[] S7_HOT_START = {
  660. 0x03, 0x00, 0x00, 0x25, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x14, 0x00,
  661. 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x09, 0x50, 0x5f, 0x50, 0x52,
  662. 0x4f, 0x47, 0x52, 0x41, 0x4d
  663. };
  664. byte[] S7_COLD_START = {
  665. 0x03, 0x00, 0x00, 0x27, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x16, 0x00,
  666. 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x02, 0x43, 0x20, 0x09, 0x50, 0x5f,
  667. 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d
  668. };
  669. #endregion
  670. #region Private Member
  671. private byte plc_rack = 0x00;
  672. private byte plc_slot = 0x00;
  673. const byte pduStart = 0x28; // CPU start
  674. const byte pduStop = 0x29; // CPU stop
  675. const byte pduAlreadyStarted = 0x02; // CPU already in run mode
  676. const byte pduAlreadyStopped = 0x07; // CPU already in stop mode
  677. #endregion
  678. #region Object Override
  679. /// <summary>
  680. /// 返回表示当前对象的字符串 -> Returns a String representing the current object
  681. /// </summary>
  682. /// <returns>字符串信息 -> String information</returns>
  683. public override string ToString()
  684. {
  685. return $"SiemensS7Net[{IpAddress}:{Port}]";
  686. }
  687. #endregion
  688. #region Static Method Helper
  689. /// <summary>
  690. /// 计算特殊的地址信息 -> Calculate Special Address information
  691. /// </summary>
  692. /// <param name="address">字符串地址 -> String address</param>
  693. /// <returns>实际值 -> Actual value</returns>
  694. public static int CalculateAddressStarted(string address)
  695. {
  696. if (address.IndexOf('.') < 0)
  697. {
  698. return Convert.ToInt32(address) * 8;
  699. }
  700. else
  701. {
  702. string[] temp = address.Split('.');
  703. return Convert.ToInt32(temp[0]) * 8 + Convert.ToInt32(temp[1]);
  704. }
  705. }
  706. /// <summary>
  707. /// 解析数据地址,解析出地址类型,起始地址,DB块的地址 ->
  708. /// Parse data address, parse out address type, start address, db block address
  709. /// </summary>
  710. /// <param name="address">起始地址,例如M100,I0,Q0,DB2.100 ->
  711. /// Start address, such as M100,I0,Q0,DB2.100</param>
  712. /// <returns>解析数据地址,解析出地址类型,起始地址,DB块的地址 ->
  713. /// Parse data address, parse out address type, start address, db block address</returns>
  714. public static OperateResult<byte, int, ushort> AnalysisAddress(string address)
  715. {
  716. var result = new OperateResult<byte, int, ushort>();
  717. try
  718. {
  719. result.Content3 = 0;
  720. if (address[0] == 'I')
  721. {
  722. result.Content1 = 0x81;
  723. result.Content2 = CalculateAddressStarted(address.Substring(1));
  724. }
  725. else if (address[0] == 'Q')
  726. {
  727. result.Content1 = 0x82;
  728. result.Content2 = CalculateAddressStarted(address.Substring(1));
  729. }
  730. else if (address[0] == 'M')
  731. {
  732. result.Content1 = 0x83;
  733. result.Content2 = CalculateAddressStarted(address.Substring(1));
  734. }
  735. else if (address[0] == 'D' || address.Substring(0, 2) == "DB")
  736. {
  737. result.Content1 = 0x84;
  738. string[] adds = address.Split('.');
  739. if (address[1] == 'B')
  740. {
  741. result.Content3 = Convert.ToUInt16(adds[0].Substring(2));
  742. }
  743. else
  744. {
  745. result.Content3 = Convert.ToUInt16(adds[0].Substring(1));
  746. }
  747. result.Content2 = CalculateAddressStarted(address.Substring(address.IndexOf('.') + 4));
  748. }
  749. else if (address[0] == 'T')
  750. {
  751. result.Content1 = 0x1D;
  752. result.Content2 = CalculateAddressStarted(address.Substring(1));
  753. }
  754. else if (address[0] == 'C')
  755. {
  756. result.Content1 = 0x1C;
  757. result.Content2 = CalculateAddressStarted(address.Substring(1));
  758. }
  759. else if (address[0] == 'V')
  760. {
  761. result.Content1 = 0x84;
  762. result.Content3 = 1;
  763. result.Content2 = CalculateAddressStarted(address.Substring(1));
  764. }
  765. else
  766. {
  767. result.Message = StringResources.Language.NotSupportedDataType;
  768. result.Content1 = 0;
  769. result.Content2 = 0;
  770. result.Content3 = 0;
  771. return result;
  772. }
  773. }
  774. catch (Exception ex)
  775. {
  776. result.Message = ex.Message;
  777. return result;
  778. }
  779. result.IsSuccess = true;
  780. return result;
  781. }
  782. #endregion
  783. #region Build Command
  784. /// <summary>
  785. /// 生成一个读取字数据指令头的通用方法 ->
  786. /// A general method for generating a command header to read a Word data
  787. /// </summary>
  788. /// <param name="address">起始地址,例如M100,I0,Q0,DB2.100 ->
  789. /// Start address, such as M100,I0,Q0,DB2.100</param>
  790. /// <param name="length">读取数据长度 -> Read Data length</param>
  791. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  792. public static OperateResult<byte[]> BuildReadCommand(string address, ushort length)
  793. {
  794. OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
  795. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
  796. return BuildReadCommand(new OperateResult<byte, int, ushort>[] { analysis }, new ushort[] { length });
  797. }
  798. /// <summary>
  799. /// 生成一个读取字数据指令头的通用方法 ->
  800. /// A general method for generating a command header to read a Word data
  801. /// </summary>
  802. /// <param name="address">起始地址,例如M100,I0,Q0,DB2.100 ->
  803. /// Start address, such as M100,I0,Q0,DB2.100</param>
  804. /// <param name="length">读取数据长度 -> Read Data length</param>
  805. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  806. public static OperateResult<byte[]> BuildReadCommand(OperateResult<byte, int, ushort>[] address, ushort[] length)
  807. {
  808. if (address == null) throw new NullReferenceException("address");
  809. if (length == null) throw new NullReferenceException("count");
  810. if (address.Length != length.Length) throw new Exception(StringResources.Language.TwoParametersLengthIsNotSame);
  811. if (length.Length > 19) throw new Exception(StringResources.Language.SiemensReadLengthCannotLargerThan19);
  812. int readCount = length.Length;
  813. byte[] _PLCCommand = new byte[19 + readCount * 12];
  814. // ======================================================================================
  815. _PLCCommand[0] = 0x03; // 报文头 -> Head
  816. _PLCCommand[1] = 0x00;
  817. _PLCCommand[2] = (byte)(_PLCCommand.Length / 256); // 长度 -> Length
  818. _PLCCommand[3] = (byte)(_PLCCommand.Length % 256);
  819. _PLCCommand[4] = 0x02; // 固定 -> Fixed
  820. _PLCCommand[5] = 0xF0;
  821. _PLCCommand[6] = 0x80;
  822. _PLCCommand[7] = 0x32; // 协议标识 -> Protocol identification
  823. _PLCCommand[8] = 0x01; // 命令:发 -> Command: Send
  824. _PLCCommand[9] = 0x00; // redundancy identification (reserved): 0x0000;
  825. _PLCCommand[10] = 0x00; // protocol data unit reference; it’s increased by request event;
  826. _PLCCommand[11] = 0x00;
  827. _PLCCommand[12] = 0x01; // 参数命令数据总长度 -> Parameter command Data total length
  828. _PLCCommand[13] = (byte)((_PLCCommand.Length - 17) / 256);
  829. _PLCCommand[14] = (byte)((_PLCCommand.Length - 17) % 256);
  830. _PLCCommand[15] = 0x00; // 读取内部数据时为00,读取CPU型号为Data数据长度 -> Read internal data is 00, read CPU model is data length
  831. _PLCCommand[16] = 0x00;
  832. // =====================================================================================
  833. _PLCCommand[17] = 0x04; // 读写指令,04读,05写 -> Read-write instruction, 04 read, 05 Write
  834. _PLCCommand[18] = (byte)readCount; // 读取数据块个数 -> Number of data blocks read
  835. for (int ii = 0; ii < readCount; ii++)
  836. {
  837. //===========================================================================================
  838. // 指定有效值类型 -> Specify a valid value type
  839. _PLCCommand[19 + ii * 12] = 0x12;
  840. // 接下来本次地址访问长度 -> The next time the address access length
  841. _PLCCommand[20 + ii * 12] = 0x0A;
  842. // 语法标记,ANY -> Syntax tag, any
  843. _PLCCommand[21 + ii * 12] = 0x10;
  844. // 按字为单位 -> by word
  845. _PLCCommand[22 + ii * 12] = 0x02; // (byte)(address[ii].Content1 == 0x1D ? 0x1D : address[ii].Content1 == 0x1C ? 0x1C : 0x02);
  846. // 访问数据的个数 -> Number of Access data
  847. _PLCCommand[23 + ii * 12] = (byte)(length[ii] / 256);
  848. _PLCCommand[24 + ii * 12] = (byte)(length[ii] % 256);
  849. // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
  850. _PLCCommand[25 + ii * 12] = (byte)(address[ii].Content3 / 256);
  851. _PLCCommand[26 + ii * 12] = (byte)(address[ii].Content3 % 256);
  852. // 访问数据类型 -> Accessing data types
  853. _PLCCommand[27 + ii * 12] = address[ii].Content1;
  854. // 偏移位置 -> Offset position
  855. _PLCCommand[28 + ii * 12] = (byte)(address[ii].Content2 / 256 / 256 % 256);
  856. _PLCCommand[29 + ii * 12] = (byte)(address[ii].Content2 / 256 % 256);
  857. _PLCCommand[30 + ii * 12] = (byte)(address[ii].Content2 % 256);
  858. }
  859. return OperateResult.CreateSuccessResult(_PLCCommand);
  860. }
  861. /// <summary>
  862. /// 生成一个位读取数据指令头的通用方法 ->
  863. /// A general method for generating a bit-read-Data instruction header
  864. /// </summary>
  865. /// <param name="address">起始地址,例如M100.0,I0.1,Q0.1,DB2.100.2 ->
  866. /// Start address, such as M100.0,I0.1,Q0.1,DB2.100.2
  867. /// </param>
  868. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  869. public static OperateResult<byte[]> BuildBitReadCommand(string address)
  870. {
  871. OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
  872. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
  873. byte[] _PLCCommand = new byte[31];
  874. _PLCCommand[0] = 0x03;
  875. _PLCCommand[1] = 0x00;
  876. // 长度 -> Length
  877. _PLCCommand[2] = (byte)(_PLCCommand.Length / 256);
  878. _PLCCommand[3] = (byte)(_PLCCommand.Length % 256);
  879. // 固定 -> Fixed
  880. _PLCCommand[4] = 0x02;
  881. _PLCCommand[5] = 0xF0;
  882. _PLCCommand[6] = 0x80;
  883. _PLCCommand[7] = 0x32;
  884. // 命令:发 -> command to send
  885. _PLCCommand[8] = 0x01;
  886. // 标识序列号
  887. _PLCCommand[9] = 0x00;
  888. _PLCCommand[10] = 0x00;
  889. _PLCCommand[11] = 0x00;
  890. _PLCCommand[12] = 0x01;
  891. // 命令数据总长度 -> Identification serial Number
  892. _PLCCommand[13] = (byte)((_PLCCommand.Length - 17) / 256);
  893. _PLCCommand[14] = (byte)((_PLCCommand.Length - 17) % 256);
  894. _PLCCommand[15] = 0x00;
  895. _PLCCommand[16] = 0x00;
  896. // 命令起始符 -> Command start character
  897. _PLCCommand[17] = 0x04;
  898. // 读取数据块个数 -> Number of data blocks read
  899. _PLCCommand[18] = 0x01;
  900. //===========================================================================================
  901. // 读取地址的前缀 -> Read the prefix of the address
  902. _PLCCommand[19] = 0x12;
  903. _PLCCommand[20] = 0x0A;
  904. _PLCCommand[21] = 0x10;
  905. // 读取的数据时位 -> Data read-time bit
  906. _PLCCommand[22] = 0x01;
  907. // 访问数据的个数 -> Number of Access data
  908. _PLCCommand[23] = 0x00;
  909. _PLCCommand[24] = 0x01;
  910. // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
  911. _PLCCommand[25] = (byte)(analysis.Content3 / 256);
  912. _PLCCommand[26] = (byte)(analysis.Content3 % 256);
  913. // 访问数据类型 -> Types of reading data
  914. _PLCCommand[27] = analysis.Content1;
  915. // 偏移位置 -> Offset position
  916. _PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256 % 256);
  917. _PLCCommand[29] = (byte)(analysis.Content2 / 256 % 256);
  918. _PLCCommand[30] = (byte)(analysis.Content2 % 256);
  919. return OperateResult.CreateSuccessResult(_PLCCommand);
  920. }
  921. /// <summary>
  922. /// 生成一个写入字节数据的指令 -> Generate an instruction to write byte data
  923. /// </summary>
  924. /// <param name="address">起始地址,示例M100,I100,Q100,DB1.100 -> Start Address, example M100,I100,Q100,DB1.100</param>
  925. /// <param name="data">原始的字节数据 -> Raw byte data</param>
  926. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  927. public static OperateResult<byte[]> BuildWriteByteCommand(string address, byte[] data)
  928. {
  929. if (data == null) data = new byte[0];
  930. OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
  931. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
  932. return BuildWriteByteCommand(analysis, data);
  933. }
  934. /// <summary>
  935. /// 生成一个写入字节数据的指令 -> Generate an instruction to write byte data
  936. /// </summary>
  937. /// <param name="analysis">起始地址,示例M100,I100,Q100,DB1.100 -> Start Address, example M100,I100,Q100,DB1.100</param>
  938. /// <param name="data">原始的字节数据 -> Raw byte data</param>
  939. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  940. public static OperateResult<byte[]> BuildWriteByteCommand(OperateResult<byte, int, ushort> analysis, byte[] data)
  941. {
  942. byte[] _PLCCommand = new byte[35 + data.Length];
  943. _PLCCommand[0] = 0x03;
  944. _PLCCommand[1] = 0x00;
  945. // 长度 -> Length
  946. _PLCCommand[2] = (byte)((35 + data.Length) / 256);
  947. _PLCCommand[3] = (byte)((35 + data.Length) % 256);
  948. // 固定 -> Fixed
  949. _PLCCommand[4] = 0x02;
  950. _PLCCommand[5] = 0xF0;
  951. _PLCCommand[6] = 0x80;
  952. _PLCCommand[7] = 0x32;
  953. // 命令 发 -> command to send
  954. _PLCCommand[8] = 0x01;
  955. // 标识序列号 -> Identification serial Number
  956. _PLCCommand[9] = 0x00;
  957. _PLCCommand[10] = 0x00;
  958. _PLCCommand[11] = 0x00;
  959. _PLCCommand[12] = 0x01;
  960. // 固定 -> Fixed
  961. _PLCCommand[13] = 0x00;
  962. _PLCCommand[14] = 0x0E;
  963. // 写入长度+4 -> Write Length +4
  964. _PLCCommand[15] = (byte)((4 + data.Length) / 256);
  965. _PLCCommand[16] = (byte)((4 + data.Length) % 256);
  966. // 读写指令 -> Read and write instructions
  967. _PLCCommand[17] = 0x05;
  968. // 写入数据块个数 -> Number of data blocks written
  969. _PLCCommand[18] = 0x01;
  970. // 固定,返回数据长度 -> Fixed, return data length
  971. _PLCCommand[19] = 0x12;
  972. _PLCCommand[20] = 0x0A;
  973. _PLCCommand[21] = 0x10;
  974. // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word
  975. _PLCCommand[22] = 0x02;
  976. // 写入数据的个数 -> Number of Write Data
  977. _PLCCommand[23] = (byte)(data.Length / 256);
  978. _PLCCommand[24] = (byte)(data.Length % 256);
  979. // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
  980. _PLCCommand[25] = (byte)(analysis.Content3 / 256);
  981. _PLCCommand[26] = (byte)(analysis.Content3 % 256);
  982. // 写入数据的类型 -> Types of writing data
  983. _PLCCommand[27] = analysis.Content1;
  984. // 偏移位置 -> Offset position
  985. _PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256 % 256); ;
  986. _PLCCommand[29] = (byte)(analysis.Content2 / 256 % 256);
  987. _PLCCommand[30] = (byte)(analysis.Content2 % 256);
  988. // 按字写入 -> Write by Word
  989. _PLCCommand[31] = 0x00;
  990. _PLCCommand[32] = 0x04;
  991. // 按位计算的长度 -> The length of the bitwise calculation
  992. _PLCCommand[33] = (byte)(data.Length * 8 / 256);
  993. _PLCCommand[34] = (byte)(data.Length * 8 % 256);
  994. data.CopyTo(_PLCCommand, 35);
  995. return OperateResult.CreateSuccessResult(_PLCCommand);
  996. }
  997. /// <summary>
  998. /// 生成一个写入位数据的指令 -> Generate an instruction to write bit data
  999. /// </summary>
  1000. /// <param name="address">起始地址,示例M100,I100,Q100,DB1.100 -> Start Address, example M100,I100,Q100,DB1.100</param>
  1001. /// <param name="data">是否通断 -> Power on or off</param>
  1002. /// <returns>包含结果对象的报文 -> Message containing the result object</returns>
  1003. public static OperateResult<byte[]> BuildWriteBitCommand(string address, bool data)
  1004. {
  1005. OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);
  1006. if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
  1007. byte[] buffer = new byte[1];
  1008. buffer[0] = data ? (byte)0x01 : (byte)0x00;
  1009. byte[] _PLCCommand = new byte[35 + buffer.Length];
  1010. _PLCCommand[0] = 0x03;
  1011. _PLCCommand[1] = 0x00;
  1012. // 长度 -> length
  1013. _PLCCommand[2] = (byte)((35 + buffer.Length) / 256);
  1014. _PLCCommand[3] = (byte)((35 + buffer.Length) % 256);
  1015. // 固定 -> fixed
  1016. _PLCCommand[4] = 0x02;
  1017. _PLCCommand[5] = 0xF0;
  1018. _PLCCommand[6] = 0x80;
  1019. _PLCCommand[7] = 0x32;
  1020. // 命令 发 -> command to send
  1021. _PLCCommand[8] = 0x01;
  1022. // 标识序列号 -> Identification serial Number
  1023. _PLCCommand[9] = 0x00;
  1024. _PLCCommand[10] = 0x00;
  1025. _PLCCommand[11] = 0x00;
  1026. _PLCCommand[12] = 0x01;
  1027. // 固定 -> fixed
  1028. _PLCCommand[13] = 0x00;
  1029. _PLCCommand[14] = 0x0E;
  1030. // 写入长度+4 -> Write Length +4
  1031. _PLCCommand[15] = (byte)((4 + buffer.Length) / 256);
  1032. _PLCCommand[16] = (byte)((4 + buffer.Length) % 256);
  1033. // 命令起始符 -> Command start character
  1034. _PLCCommand[17] = 0x05;
  1035. // 写入数据块个数 -> Number of data blocks written
  1036. _PLCCommand[18] = 0x01;
  1037. _PLCCommand[19] = 0x12;
  1038. _PLCCommand[20] = 0x0A;
  1039. _PLCCommand[21] = 0x10;
  1040. // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word
  1041. _PLCCommand[22] = 0x01;
  1042. // 写入数据的个数 -> Number of Write Data
  1043. _PLCCommand[23] = (byte)(buffer.Length / 256);
  1044. _PLCCommand[24] = (byte)(buffer.Length % 256);
  1045. // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block
  1046. _PLCCommand[25] = (byte)(analysis.Content3 / 256);
  1047. _PLCCommand[26] = (byte)(analysis.Content3 % 256);
  1048. // 写入数据的类型 -> Types of writing data
  1049. _PLCCommand[27] = analysis.Content1;
  1050. // 偏移位置 -> Offset position
  1051. _PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256);
  1052. _PLCCommand[29] = (byte)(analysis.Content2 / 256);
  1053. _PLCCommand[30] = (byte)(analysis.Content2 % 256);
  1054. // 按位写入 -> Bitwise Write
  1055. _PLCCommand[31] = 0x00;
  1056. _PLCCommand[32] = 0x03;
  1057. // 按位计算的长度 -> The length of the bitwise calculation
  1058. _PLCCommand[33] = (byte)(buffer.Length / 256);
  1059. _PLCCommand[34] = (byte)(buffer.Length % 256);
  1060. buffer.CopyTo(_PLCCommand, 35);
  1061. return OperateResult.CreateSuccessResult(_PLCCommand);
  1062. }
  1063. #endregion
  1064. }
  1065. }