PLCHelpers.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. using S7.Net.Helper;
  2. using S7.Net.Protocol.S7;
  3. using S7.Net.Types;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using DateTime = S7.Net.Types.DateTime;
  7. namespace S7.Net
  8. {
  9. public partial class Plc
  10. {
  11. private static void WriteTpktHeader(System.IO.MemoryStream stream, int length)
  12. {
  13. stream.Write(new byte[] { 0x03, 0x00 });
  14. stream.Write(Word.ToByteArray((ushort) length));
  15. }
  16. private static void WriteDataHeader(System.IO.MemoryStream stream)
  17. {
  18. stream.Write(new byte[] { 0x02, 0xf0, 0x80 });
  19. }
  20. private static void WriteS7Header(System.IO.MemoryStream stream, byte messageType, int parameterLength, int dataLength)
  21. {
  22. stream.WriteByte(0x32); // S7 protocol ID
  23. stream.WriteByte(messageType); // Message type
  24. stream.Write(new byte[] { 0x00, 0x00 }); // Reserved
  25. stream.Write(new byte[] { 0x00, 0x00 }); // PDU ref
  26. stream.Write(Word.ToByteArray((ushort) parameterLength));
  27. stream.Write(Word.ToByteArray((ushort) dataLength));
  28. }
  29. /// <summary>
  30. /// Creates the header to read bytes from the PLC.
  31. /// </summary>
  32. /// <param name="stream">The stream to write to.</param>
  33. /// <param name="amount">The number of items to read.</param>
  34. private static void WriteReadHeader(System.IO.MemoryStream stream, int amount = 1)
  35. {
  36. // Header size 19, 12 bytes per item
  37. WriteTpktHeader(stream, 19 + 12 * amount);
  38. WriteDataHeader(stream);
  39. WriteS7Header(stream, 0x01, 2 + 12 * amount, 0);
  40. // Function code: read request
  41. stream.WriteByte(0x04);
  42. //amount of requests
  43. stream.WriteByte((byte)amount);
  44. }
  45. private static void WriteUserDataHeader(System.IO.MemoryStream stream, int parameterLength, int dataLength)
  46. {
  47. const byte s7MessageTypeUserData = 0x07;
  48. WriteTpktHeader(stream, 17 + parameterLength + dataLength);
  49. WriteDataHeader(stream);
  50. WriteS7Header(stream, s7MessageTypeUserData, parameterLength, dataLength);
  51. }
  52. private static void WriteUserDataRequest(System.IO.MemoryStream stream, byte functionGroup, byte subFunction, int dataLength)
  53. {
  54. WriteUserDataHeader(stream, 8, dataLength);
  55. // Parameter
  56. const byte userDataMethodRequest = 0x11;
  57. const byte userDataTypeRequest = 0x4;
  58. // Parameter head
  59. stream.Write(new byte[] { 0x00, 0x01, 0x12 });
  60. // Parameter length
  61. stream.WriteByte(0x04);
  62. // Method
  63. stream.WriteByte(userDataMethodRequest);
  64. // Type / function group
  65. stream.WriteByte((byte)(userDataTypeRequest << 4 | (functionGroup & 0x0f)));
  66. // Subfunction
  67. stream.WriteByte(subFunction);
  68. // Sequence number
  69. stream.WriteByte(0);
  70. }
  71. private static void WriteSzlReadRequest(System.IO.MemoryStream stream, ushort szlId, ushort szlIndex)
  72. {
  73. // Parameter
  74. const byte szlFunctionGroupCpuFunctions = 0b100;
  75. const byte subFunctionReadSzl = 0x01;
  76. WriteUserDataRequest(stream, szlFunctionGroupCpuFunctions, subFunctionReadSzl, 8);
  77. // Data
  78. const byte success = 0xff;
  79. const byte transportSizeOctetString = 0x09;
  80. // Return code
  81. stream.WriteByte(success);
  82. // Transport size
  83. stream.WriteByte(transportSizeOctetString);
  84. // Length
  85. stream.Write(Word.ToByteArray(4));
  86. // SZL-ID
  87. stream.Write(Word.ToByteArray(szlId));
  88. // SZL-Index
  89. stream.Write(Word.ToByteArray(szlIndex));
  90. }
  91. /// <summary>
  92. /// Create the bytes-package to request data from the PLC. You have to specify the memory type (dataType),
  93. /// the address of the memory, the address of the byte and the bytes count.
  94. /// </summary>
  95. /// <param name="stream">The stream to write the read data request to.</param>
  96. /// <param name="dataType">MemoryType (DB, Timer, Counter, etc.)</param>
  97. /// <param name="db">Address of the memory to be read</param>
  98. /// <param name="startByteAdr">Start address of the byte</param>
  99. /// <param name="count">Number of bytes to be read</param>
  100. /// <returns></returns>
  101. private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, DataType dataType, int db, int startByteAdr, int count = 1)
  102. {
  103. //single data req = 12
  104. stream.Write(new byte[] { 0x12, 0x0a, 0x10 });
  105. switch (dataType)
  106. {
  107. case DataType.Timer:
  108. case DataType.Counter:
  109. stream.WriteByte((byte)dataType);
  110. break;
  111. default:
  112. stream.WriteByte(0x02);
  113. break;
  114. }
  115. stream.Write(Word.ToByteArray((ushort)(count)));
  116. stream.Write(Word.ToByteArray((ushort)(db)));
  117. stream.WriteByte((byte)dataType);
  118. var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
  119. stream.WriteByte((byte)overflow);
  120. switch (dataType)
  121. {
  122. case DataType.Timer:
  123. case DataType.Counter:
  124. stream.Write(Word.ToByteArray((ushort)(startByteAdr)));
  125. break;
  126. default:
  127. stream.Write(Word.ToByteArray((ushort)((startByteAdr) * 8)));
  128. break;
  129. }
  130. }
  131. /// <summary>
  132. /// Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format.
  133. /// </summary>
  134. /// <param name="varType"></param>
  135. /// <param name="bytes"></param>
  136. /// <param name="varCount"></param>
  137. /// <param name="bitAdr"></param>
  138. /// <returns></returns>
  139. private object? ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0)
  140. {
  141. if (bytes == null || bytes.Length == 0)
  142. return null;
  143. switch (varType)
  144. {
  145. case VarType.Byte:
  146. if (varCount == 1)
  147. return bytes[0];
  148. else
  149. return bytes;
  150. case VarType.Word:
  151. if (varCount == 1)
  152. return Word.FromByteArray(bytes);
  153. else
  154. return Word.ToArray(bytes);
  155. case VarType.Int:
  156. if (varCount == 1)
  157. return Int.FromByteArray(bytes);
  158. else
  159. return Int.ToArray(bytes);
  160. case VarType.DWord:
  161. if (varCount == 1)
  162. return DWord.FromByteArray(bytes);
  163. else
  164. return DWord.ToArray(bytes);
  165. case VarType.DInt:
  166. if (varCount == 1)
  167. return DInt.FromByteArray(bytes);
  168. else
  169. return DInt.ToArray(bytes);
  170. case VarType.Real:
  171. if (varCount == 1)
  172. return Types.Real.FromByteArray(bytes);
  173. else
  174. return Types.Real.ToArray(bytes);
  175. case VarType.LReal:
  176. if (varCount == 1)
  177. return Types.LReal.FromByteArray(bytes);
  178. else
  179. return Types.LReal.ToArray(bytes);
  180. case VarType.String:
  181. return Types.String.FromByteArray(bytes);
  182. case VarType.S7String:
  183. return S7String.FromByteArray(bytes);
  184. case VarType.S7WString:
  185. return S7WString.FromByteArray(bytes);
  186. case VarType.Timer:
  187. if (varCount == 1)
  188. return Timer.FromByteArray(bytes);
  189. else
  190. return Timer.ToArray(bytes);
  191. case VarType.Counter:
  192. if (varCount == 1)
  193. return Counter.FromByteArray(bytes);
  194. else
  195. return Counter.ToArray(bytes);
  196. case VarType.Bit:
  197. if (varCount == 1)
  198. {
  199. if (bitAdr > 7)
  200. return null;
  201. else
  202. return Bit.FromByte(bytes[0], bitAdr);
  203. }
  204. else
  205. {
  206. return Bit.ToBitArray(bytes, varCount);
  207. }
  208. case VarType.DateTime:
  209. if (varCount == 1)
  210. {
  211. return DateTime.FromByteArray(bytes);
  212. }
  213. else
  214. {
  215. return DateTime.ToArray(bytes);
  216. }
  217. case VarType.DateTimeLong:
  218. if (varCount == 1)
  219. {
  220. return DateTimeLong.FromByteArray(bytes);
  221. }
  222. else
  223. {
  224. return DateTimeLong.ToArray(bytes);
  225. }
  226. case VarType.Time:
  227. if (varCount == 1)
  228. {
  229. return TimeSpan.FromByteArray(bytes);
  230. }
  231. else
  232. {
  233. return TimeSpan.ToArray(bytes);
  234. }
  235. case VarType.Date:
  236. if (varCount == 1)
  237. {
  238. return Date.FromByteArray(bytes);
  239. }
  240. else
  241. {
  242. return Date.ToArray(bytes);
  243. }
  244. default:
  245. return null;
  246. }
  247. }
  248. /// <summary>
  249. /// Given a S7 <see cref="VarType"/> (Bool, Word, DWord, etc.), it returns how many bytes to read.
  250. /// </summary>
  251. /// <param name="varType"></param>
  252. /// <param name="varCount"></param>
  253. /// <returns>Byte lenght of variable</returns>
  254. internal static int VarTypeToByteLength(VarType varType, int varCount = 1)
  255. {
  256. switch (varType)
  257. {
  258. case VarType.Bit:
  259. return (varCount + 7) / 8;
  260. case VarType.Byte:
  261. return (varCount < 1) ? 1 : varCount;
  262. case VarType.String:
  263. return varCount;
  264. case VarType.S7String:
  265. return ((varCount + 2) & 1) == 1 ? (varCount + 3) : (varCount + 2);
  266. case VarType.S7WString:
  267. return (varCount * 2) + 4;
  268. case VarType.Word:
  269. case VarType.Timer:
  270. case VarType.Int:
  271. case VarType.Counter:
  272. case VarType.Date:
  273. return varCount * 2;
  274. case VarType.DWord:
  275. case VarType.DInt:
  276. case VarType.Real:
  277. case VarType.Time:
  278. return varCount * 4;
  279. case VarType.LReal:
  280. case VarType.DateTime:
  281. return varCount * 8;
  282. case VarType.DateTimeLong:
  283. return varCount * 12;
  284. default:
  285. return 0;
  286. }
  287. }
  288. private byte[] GetS7ConnectionSetup()
  289. {
  290. return new byte[] { 3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3,
  291. 3, 192 // Use 960 PDU size
  292. };
  293. }
  294. private void ParseDataIntoDataItems(byte[] s7data, List<DataItem> dataItems)
  295. {
  296. int offset = 14;
  297. foreach (var dataItem in dataItems)
  298. {
  299. // check for Return Code = Success
  300. if (s7data[offset] != 0xff)
  301. throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
  302. // to Data bytes
  303. offset += 4;
  304. int byteCnt = VarTypeToByteLength(dataItem.VarType, dataItem.Count);
  305. dataItem.Value = ParseBytes(
  306. dataItem.VarType,
  307. s7data.Skip(offset).Take(byteCnt).ToArray(),
  308. dataItem.Count,
  309. dataItem.BitAdr
  310. );
  311. // next Item
  312. offset += byteCnt;
  313. // Always align to even offset
  314. if (offset % 2 != 0)
  315. offset++;
  316. }
  317. }
  318. private static byte[] BuildReadRequestPackage(IList<DataItemAddress> dataItems)
  319. {
  320. int packageSize = 19 + (dataItems.Count * 12);
  321. var package = new System.IO.MemoryStream(packageSize);
  322. WriteReadHeader(package, dataItems.Count);
  323. foreach (var dataItem in dataItems)
  324. {
  325. BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAddress, dataItem.ByteLength);
  326. }
  327. return package.ToArray();
  328. }
  329. private static byte[] BuildSzlReadRequestPackage(ushort szlId, ushort szlIndex)
  330. {
  331. var stream = new System.IO.MemoryStream();
  332. WriteSzlReadRequest(stream, szlId, szlIndex);
  333. stream.SetLength(stream.Position);
  334. return stream.ToArray();
  335. }
  336. }
  337. }