123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- using System;
- using System.Collections.Generic;
- using S7.Net.Types;
- namespace S7.Net.Protocol
- {
- internal static class S7WriteMultiple
- {
- public static int CreateRequest(ByteArray message, DataItem[] dataItems)
- {
- message.Add(Header.Template);
- message[Header.Offsets.ParameterCount] = (byte) dataItems.Length;
- var paramSize = dataItems.Length * Parameter.Template.Length;
- Serialization.SetWordAt(message, Header.Offsets.ParameterSize,
- (ushort) (2 + paramSize));
- var paramOffset = Header.Template.Length;
- var data = new ByteArray();
- var itemCount = 0;
- foreach (var item in dataItems)
- {
- itemCount++;
- message.Add(Parameter.Template);
- var value = Serialization.SerializeDataItem(item);
- var wordLen = item.Value is bool ? 1 : 2;
- message[paramOffset + Parameter.Offsets.WordLength] = (byte) wordLen;
- Serialization.SetWordAt(message, paramOffset + Parameter.Offsets.Amount, (ushort) value.Length);
- Serialization.SetWordAt(message, paramOffset + Parameter.Offsets.DbNumber, (ushort) item.DB);
- message[paramOffset + Parameter.Offsets.Area] = (byte) item.DataType;
- data.Add(0x00);
- if (item.Value is bool b)
- {
- if (item.BitAdr > 7)
- throw new ArgumentException(
- $"Cannot read bit with invalid {nameof(item.BitAdr)} '{item.BitAdr}'.", nameof(dataItems));
- Serialization.SetAddressAt(message, paramOffset + Parameter.Offsets.Address, item.StartByteAdr,
- item.BitAdr);
- data.Add(0x03);
- data.AddWord(1);
- data.Add(b ? (byte)1 : (byte)0);
- if (itemCount != dataItems.Length) {
- data.Add(0);
- }
- }
- else
- {
- Serialization.SetAddressAt(message, paramOffset + Parameter.Offsets.Address, item.StartByteAdr, 0);
- var len = value.Length;
- data.Add(0x04);
- data.AddWord((ushort) (len << 3));
- data.Add(value);
-
- if ((len & 0b1) == 1 && itemCount != dataItems.Length)
- {
- data.Add(0);
- }
- }
- paramOffset += Parameter.Template.Length;
- }
- message.Add(data.Array);
- Serialization.SetWordAt(message, Header.Offsets.MessageLength, (ushort) message.Length);
- Serialization.SetWordAt(message, Header.Offsets.DataLength, (ushort) (message.Length - paramOffset));
- return message.Length;
- }
- public static void ParseResponse(byte[] message, int length, DataItem[] dataItems)
- {
- if (length < 12) throw new Exception("Not enough data received to parse write response.");
- var messageError = Serialization.GetWordAt(message, 10);
- if (messageError != 0)
- throw new Exception($"Write failed with error {messageError}.");
- if (length < 14 + dataItems.Length)
- throw new Exception("Not enough data received to parse individual item responses.");
- IList<byte> itemResults = new ArraySegment<byte>(message, 14, dataItems.Length);
- List<Exception>? errors = null;
- for (int i = 0; i < dataItems.Length; i++)
- {
- try
- {
- Plc.ValidateResponseCode((ReadWriteErrorCode)itemResults[i]);
- }
- catch(Exception e)
- {
- if (errors == null) errors = new List<Exception>();
- errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed: {e.Message}."));
- }
- }
- if (errors != null)
- throw new AggregateException(
- $"Write failed for {errors.Count} items. See the innerExceptions for details.", errors);
- }
- private static class Header
- {
- public static byte[] Template { get; } =
- {
- 0x03, 0x00, 0x00, 0x00, // TPKT
- 0x02, 0xf0, 0x80, // ISO DT
- 0x32, // S7 protocol ID
- 0x01, // JobRequest
- 0x00, 0x00, // Reserved
- 0x05, 0x00, // PDU reference
- 0x00, 0x0e, // Parameters length
- 0x00, 0x00, // Data length
- 0x05, // Function: Write var
- 0x00, // Number of items to write
- };
- public static class Offsets
- {
- public const int MessageLength = 2;
- public const int ParameterSize = 13;
- public const int DataLength = 15;
- public const int ParameterCount = 18;
- }
- }
- private static class Parameter
- {
- public static byte[] Template { get; } =
- {
- 0x12, // Spec
- 0x0a, // Length of remaining bytes
- 0x10, // Addressing mode
- 0x02, // Transport size
- 0x00, 0x00, // Number of elements
- 0x00, 0x00, // DB number
- 0x84, // Area type
- 0x00, 0x00, 0x00 // Area offset
- };
- public static class Offsets
- {
- public const int WordLength = 3;
- public const int Amount = 4;
- public const int DbNumber = 6;
- public const int Area = 8;
- public const int Address = 9;
- }
- }
- }
- }
|