public static int DataTypeToSizeByte(Type t, PlcArea area) { if (t.IsArray) { t = t.GetElementType(); } if (area == PlcArea.CT || area == PlcArea.TM) { return(1); } if (t == typeof(bool) || t == typeof(byte) || t == typeof(char)) { return(1); } if (t == typeof(Int16) || t == typeof(UInt16)) { return(2); } if (t == typeof(Int32) || t == typeof(UInt32) || t == typeof(double)) { return(4); } return(0); }
public static ReadItem CreateFromTag(string tag) { var parts = tag.Split(new[] { ',' }); var start = parts[0].Split(new[] { '.' }); var withPrefix = start.Length == 3; PlcArea selector = 0; ushort length = 1; ushort offset = UInt16.Parse(start[start.Length - 1]); ushort db = 0; if (!TryDetectArea(start[withPrefix ? 1 : 0], ref selector, ref db)) { throw new ArgumentException($"Invalid area in tag <{tag}>"); } if (parts.Length > 2) { length = UInt16.Parse(parts[2]); } offset = DetectTypes(parts[1], length, offset, out Type vtype, out Type rType); return(new ReadItem { Area = selector, DbNumber = db, Offset = offset, Length = length, VarType = vtype, ResultType = rType }); }
/// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="area">Where to read: e.g. DB1 or M or...</param> /// <param name="offset">offset in bytes, if you address booleans, you have to pass the address in bits (byteoffset * 8 + bitoffset)</param> /// <param name="length">The number of items to read</param> /// <returns></returns> public static ReadItem Create <T>(string area, ushort offset, ushort length = 1) { PlcArea selector = 0; ushort db = 0; if (!TryDetectArea(area, ref selector, ref db)) { throw new ArgumentException($"Invalid area <{area}>"); } var t = typeof(T); Type vtype; Type rType; if (t.IsArray) { vtype = t.GetElementType(); rType = t; } else { vtype = t; rType = length > 1 ? typeof(T[]) : vtype; } return(new ReadItem { Area = selector, DbNumber = db, Offset = offset, Length = length, VarType = vtype, ResultType = rType }); }
public RequestItem(PlcArea area, ushort dbNumber, ushort numberOfItems, int offset, ItemDataTransportSize transportSize, Memory <byte> address) { Area = area; DbNumber = dbNumber; NumberOfItems = numberOfItems; Offset = offset; Address = address; DetermineTransportAndElementSize(area, transportSize); }
protected RequestItem(PlcArea area, ushort dbNumber, ushort numberOfItems, int offset, DataTransportSize transportSize, ushort elementSize, Memory <byte> address) { Area = area; DbNumber = dbNumber; NumberOfItems = numberOfItems; Offset = offset; TransportSize = transportSize; Address = address; ElementSize = elementSize; }
private static int CalculateSizeForGenericWriteOperation <T>(PlcArea area, T value, int length = -1) { var size = Dacs7Client.CalculateSizeForGenericWriteOperation(area, value, length, out Type elementType); if (typeof(T) == typeof(string)) { size -= 2; } return(size); }
public static byte GetTransportSize(PlcArea area, Type t) { if (area == PlcArea.CT || area == PlcArea.TM) { return(0x01); } if (t.IsArray) { t = t.GetElementType(); } if (t == typeof(bool)) { return((byte)ItemDataTransportSize.Bit); } if (t == typeof(byte) || t == typeof(string) || t == typeof(Memory <byte>)) { return((byte)ItemDataTransportSize.Byte); } if (t == typeof(char)) { return((byte)ItemDataTransportSize.Char); } if (t == typeof(ushort)) { return((byte)ItemDataTransportSize.Word); } if (t == typeof(short)) { return((byte)ItemDataTransportSize.Int); } if (t == typeof(uint)) { return((byte)ItemDataTransportSize.Dword); } if (t == typeof(int)) { return((byte)ItemDataTransportSize.Dint); } if (t == typeof(float)) { return((byte)ItemDataTransportSize.Real); } return(0); }
//TODO: Handle array of bool correct!!!! public static IMessage CreateWriteRequest(ushort unitId, PlcArea area, ushort dbnr, int offset, ushort length, object data) { var msg = Message.Create(); var isArray = data is Array; var isBool = isArray ? (data as Array).GetValue(0) is bool : data is bool; // Handle Array of bools var numberOfItems = !isBool ? 1 : length; var itemLength = isBool ? (ushort)1 : length; var payloadLength = (ushort)(!isBool ? length + 4 : length * 6 - 1); //=we need a fillbyte between each item has to be a fill byte if the length is odd. var paramLength = (ushort)(!isBool ? 14 : 2 + 12 * numberOfItems); FillCommHeader(msg, (byte)PduType.Job, payloadLength, paramLength, unitId); AddReadWriteParameter(msg, (byte)FunctionCode.WriteVar, Convert.ToByte(numberOfItems)); var t = data.GetType(); var enumerable = ConvertDataToByteArray(data); var typeLength = TransportSizeHelper.DataTypeToSizeByte(t, area); for (var i = 0; i < numberOfItems; i++) { var size = isBool || area == PlcArea.CT || area == PlcArea.TM ? (byte)ItemDataTransportSize.Bit : (byte)ItemDataTransportSize.Byte; var addr = i * typeLength + offset; var prefix = $"Item[{i}]."; msg.SetAttribute(prefix + "VariableSpecification", (byte)0x12); const byte specLength = 0x0a; msg.SetAttribute(prefix + "LengthOfAddressSpecification", specLength); msg.SetAttribute(prefix + "SyntaxId", (byte)ItemSyntaxId.S7Any); msg.SetAttribute(prefix + "TransportSize", (byte)size); msg.SetAttribute(prefix + "ItemSpecLength", itemLength); msg.SetAttribute(prefix + "DbNumber", dbnr); msg.SetAttribute(prefix + "Area", area); var offsetAddress = size == 0x01 ? addr : addr * 8; var address = new byte[3]; address[2] = (byte)(offsetAddress & 0x000000FF); offsetAddress = offsetAddress >> 8; address[1] = (byte)(offsetAddress & 0x000000FF); offsetAddress = offsetAddress >> 8; address[0] = (byte)(offsetAddress & 0x000000FF); msg.SetAttribute(prefix + "Address", address); } for (var i = 0; i < numberOfItems; i++) { var prefix = $"DataItem[{i}]."; msg.SetAttribute(prefix + "ItemDataReturnCode", (byte)0x00); msg.SetAttribute(prefix + "ItemDataTransportSize", isBool ? (byte)DataTransportSize.Bit : (byte)DataTransportSize.Byte); msg.SetAttribute(prefix + "ItemDataLength", itemLength); msg.SetAttribute(prefix + "ItemData", isBool ? new byte[] { enumerable[i] } : enumerable); } return(msg); }
public void TryParseTagTests(string tag, PlcArea resultArea, int offset, int length, Type resultType, Type vartype = null) { if (vartype == null) { vartype = resultType; } Assert.True(TagParser.TryParseTag(tag, out var result)); Assert.Equal(resultArea, result.Area); Assert.Equal(offset, result.Offset); Assert.Equal(length, result.Length); Assert.Equal(resultType, result.ResultType); Assert.Equal(vartype, result.VarType); }
public PlcDataEntry(PlcArea area, ushort dbNumber, ushort length, Memory <byte> data = default) { Area = area; DbNumber = dbNumber; Length = length; if (!data.IsEmpty) { _externalData = data; } else { _owner = MemoryPool <byte> .Shared.Rent(length); } }
public bool Release(PlcArea area, ushort dbNumber = 0) { if (!_plcData.TryGetValue(area, out var areaData)) { return(false); } if (!areaData.TryGetValue(dbNumber, out var dataEntry)) { return(false); } dataEntry?.Dispose(); return(true); }
/// <summary> /// Create a WriteOperationParameter instance to write data to any area. /// </summary> /// <typeparam name="T">Specifies the type of the value we want to write.</typeparam> /// <param name="area">The target <see cref="PlcArea"></see> we want to write.</param> /// <param name="offset">This is the offset to the data you want to write.(offset is normally the number of bytes from the beginning of the area, /// excepted the data type is a boolean, then the offset is in number of bits. This means ByteOffset*8+BitNumber)</param> /// <param name="value">This is the value we want to write to the PLC.</param> /// <returns></returns> public static WriteOperationParameter Create <T>(PlcArea area, int offset, T value) { if (area == PlcArea.DB) { throw new ArgumentException("The argument area could not be of type DB."); } return(new WriteOperationParameter { Area = area, Offset = offset, Type = typeof(T), Data = value, Args = new[] { CalculateSizeForGenericWriteOperation <T>(PlcArea.DB, value) } }); }
public static ushort GetElementSize(PlcArea area, Type t, PlcEncoding encoding) { if (area == PlcArea.CT || area == PlcArea.TM) { return(2); } if (t.IsArray) { t = t.GetElementType(); } if (t == typeof(byte) || t == typeof(Memory <byte>)) { return(1); } if (t == typeof(bool)) { return(1); } if (t == typeof(char) || t == typeof(string)) { return((ushort)(encoding == PlcEncoding.Unicode ? 2 : 1)); } if (t == typeof(short) || t == typeof(ushort)) { return(2); } if (t == typeof(int) || t == typeof(uint)) { return(4); } if (t == typeof(ulong) || t == typeof(long)) { return(8); } if (t == typeof(float)) { return(4); } if (t == typeof(sbyte)) { return(1); } return(1); }
/// <summary> /// Create a ReadOperationParameter instance by the given arguments /// </summary> /// <typeparam name="T">Specifies the type of the value we want to read.</typeparam> /// <param name="area">The target <see cref="PlcArea"></see> from which we want to read.</param> /// <param name="offset">This is the offset to the data you want to read.(offset is normally the number of bytes from the beginning of the area, /// excepted the data type is a boolean, then the offset is in number of bits. This means ByteOffset*8+BitNumber)</param> /// <param name="numberOfItems">Number of items of the T to read. This could be the string length for a string, or the number of bytes/int for an array and so on. /// The default value is always 1. /// Reading array of BOOLs is not supported in this case at the moment!</param> /// <returns></returns> public static ReadOperationParameter Create <T>(PlcArea area, int offset, int numberOfItems = -1) { if (area == PlcArea.DB) { throw new ArgumentException("The argument area could not be DB."); } var t = typeof(T); CalculateElementLength <T>(ref offset, ref numberOfItems, t, area); return(new ReadOperationParameter { Area = area, Offset = offset, Type = t, Args = new[] { numberOfItems } }); }
public bool Register(PlcArea area, ushort dataLength, Memory <byte> data, ushort dbNumber = 0) { if (!_plcData.TryGetValue(area, out var areaData)) { areaData = new Dictionary <ushort, PlcDataEntry>(); _plcData.Add(area, areaData); } if (!areaData.TryGetValue(dbNumber, out var dataEntry)) { dataEntry = new PlcDataEntry ( area: area, dbNumber: dbNumber, length: dataLength, data: data ); areaData.Add(dbNumber, dataEntry); return(true); } return(false); }
private static bool TryDetectArea(string area, ref PlcArea selector, ref ushort db) { switch (area.ToUpper()) { // Inputs case "I": selector = PlcArea.IB; break; // English case "E": selector = PlcArea.IB; break; // German // Marker case "M": selector = PlcArea.FB; break; // English and German // Ouputs case "Q": selector = PlcArea.QB; break; // English case "A": selector = PlcArea.QB; break; // German // Timer case "T": selector = PlcArea.TM; break; // English and German // Counter case "C": selector = PlcArea.CT; break; // English case "Z": selector = PlcArea.CT; break; // German // Datablocks case var s when Regex.IsMatch(s, "^DB\\d+$", RegexOptions.IgnoreCase): { selector = PlcArea.DB; db = UInt16.Parse(s.Substring(2)); break; } default: return(false); } return(true); }
public static IMessage CreateReadRequest(ushort unitId, PlcArea area, ushort dbnr, int offset, ushort length, Type t) { var msg = Message.Create(); var isBit = t == typeof(bool); FillCommHeader(msg, (byte)PduType.Job, 0, 14, unitId); AddReadWriteParameter(msg, (byte)FunctionCode.ReadVar, 1); for (var i = 0; i < 1; i++) { var size = isBit || area == PlcArea.CT || area == PlcArea.TM ? (byte)ItemDataTransportSize.Bit : (byte)ItemDataTransportSize.Byte; var prefix = $"Item[{i}]."; msg.SetAttribute(prefix + "VariableSpecification", (byte)0x12); const byte specLength = 0x0a; msg.SetAttribute(prefix + "LengthOfAddressSpecification", specLength); msg.SetAttribute(prefix + "SyntaxId", (byte)ItemSyntaxId.S7Any); msg.SetAttribute(prefix + "TransportSize", (byte)size); msg.SetAttribute(prefix + "ItemSpecLength", length); msg.SetAttribute(prefix + "DbNumber", dbnr); msg.SetAttribute(prefix + "Area", area); offset = isBit ? offset : (offset * 8); var address = new byte[3]; address[2] = (byte)(offset & 0x000000FF); offset = offset >> 8; address[1] = (byte)(offset & 0x000000FF); offset = offset >> 8; address[0] = (byte)(offset & 0x000000FF); msg.SetAttribute(prefix + "Address", address); offset += specLength + 2; } return(msg); }
protected void DetermineTransportAndElementSize(PlcArea area, ItemDataTransportSize t) { if (area == PlcArea.CT || area == PlcArea.TM) { TransportSize = DataTransportSize.OctetString; ElementSize = 2; return; } switch (t) { case ItemDataTransportSize.Bit: { TransportSize = DataTransportSize.Bit; ElementSize = 1; } break; case ItemDataTransportSize.Byte: case ItemDataTransportSize.Char: { TransportSize = DataTransportSize.Byte; ElementSize = 1; } break; case ItemDataTransportSize.Word: { TransportSize = DataTransportSize.Byte; ElementSize = 2; } break; case ItemDataTransportSize.Int: { TransportSize = DataTransportSize.Int; ElementSize = 2; } break; case ItemDataTransportSize.Dword: { TransportSize = DataTransportSize.Byte; ElementSize = 4; } break; case ItemDataTransportSize.Dint: { TransportSize = DataTransportSize.Dint; ElementSize = 4; } break; case ItemDataTransportSize.Real: { TransportSize = DataTransportSize.Real; ElementSize = 4; } break; default: { TransportSize = DataTransportSize.Byte; ElementSize = 1; } break; } }
/// <summary> /// Create a WriteOperationParameter instance to write a bit. /// This is an specialization where you do not have to calculate the bit offset by yourselves. /// </summary> /// <param name="area">The target <see cref="PlcArea"></see> we want to write.</param> /// <param name="offset">This is the offset in byte to the data you want to write.</param> /// <param name="bitNumber">This is the number of the bit, in the byte with the given offset, we want to write.</param> /// <param name="value">This is the value we want to write to the PLC.</param> /// <returns></returns> public static WriteOperationParameter CreateForBit(PlcArea area, int offset, int bitNumber, bool value) { return(Create(area, offset * 8 + bitNumber, value)); }
private static void CalculateElementLength <T>(ref int offset, ref int numberOfItems, Type t, PlcArea area) { var isBool = t == typeof(bool); var isString = t == typeof(string); var elementLength = isString ? numberOfItems : TransportSizeHelper.DataTypeToSizeByte(typeof(T), area); if (numberOfItems >= 0) { if (isBool) { offset /= 8; } else if (isString) { numberOfItems = elementLength; } else { numberOfItems = numberOfItems * elementLength; } } else { numberOfItems = 1; } }
/// <summary> /// Create a ReadOperationParameter instance to read a bit. /// </summary> /// <param name="area">The target <see cref="PlcArea"></see> from which we want to read.</param> /// <param name="offset">This is the offset in byte to the data you want to read.</param> /// <param name="bitNumber">This is the number of the bit, in the byte with the given offset, we want to read.</param> /// <param name="numberOfItems">Number of items of the T to read. This could be the string length for a string, or the number of bytes/int for an array and so on. /// The default value is always 1. /// Reading array of BOOLs is not supported in this case at the moment!</param> /// <returns></returns> public static ReadOperationParameter CreateForBit(PlcArea area, int offset, int bitNumber, int numberOfItems = -1) { return(Create <bool>(area, offset * 8 + bitNumber, numberOfItems)); }
public ReadRequestItem(PlcArea area, ushort dbNumber, ushort numberOfItems, int offset, ItemDataTransportSize transportSize, Memory <byte> address) : base(area, dbNumber, numberOfItems, offset, transportSize, address) { }
public bool Register(PlcArea area, ushort dataLength, ushort dbNumber = 0) => Register(area, dataLength, default, dbNumber);
internal WriteRequestItem(PlcArea area, ushort dbNumber, ushort numberOfItems, int offset, DataTransportSize transportSize, ushort elementSize, Memory <byte> address, Memory <byte> data) : base(area, dbNumber, numberOfItems, offset, transportSize, elementSize, address) { Data = data; }
public static bool TryDetectArea(ReadOnlySpan <char> area, out PlcArea selector, out ushort db) { db = 0; var singleElement = area.Length == 1; switch (area[0]) { // Inputs case 'i' when singleElement: selector = PlcArea.IB; return(true); // English case 'e' when singleElement: selector = PlcArea.IB; return(true); // German case 'I' when singleElement: selector = PlcArea.IB; return(true); // English case 'E' when singleElement: selector = PlcArea.IB; return(true); // German // Marker case 'm' when singleElement: selector = PlcArea.FB; return(true); // English and German case 'M' when singleElement: selector = PlcArea.FB; return(true); // English and German // Ouputs case 'q' when singleElement: selector = PlcArea.QB; return(true); // English case 'a' when singleElement: selector = PlcArea.QB; return(true); // German case 'Q' when singleElement: selector = PlcArea.QB; return(true); // English case 'A' when singleElement: selector = PlcArea.QB; return(true); // German // Timer case 't' when singleElement: selector = PlcArea.TM; return(true); // English and German case 'T' when singleElement: selector = PlcArea.TM; return(true); // English and German // Counter case 'c' when singleElement: selector = PlcArea.CT; return(true); // English case 'z' when singleElement: selector = PlcArea.CT; return(true); // German case 'C' when singleElement: selector = PlcArea.CT; return(true); // English case 'Z' when singleElement: selector = PlcArea.CT; return(true); // German case 'd' when area.Length > 2: case 'D' when area.Length > 2: { // TODO: ReadOnlySpan<char> !!!! // Datablocks //if (Regex.IsMatch(area.ToString(), "^db\\d+$", RegexOptions.IgnoreCase)) if ((area[0] == 'D' || area[0] == 'd') && (area[1] == 'B' || area[1] == 'b')) { selector = PlcArea.DB; #if SPANSUPPORT db = ushort.Parse(area.Slice(2)); #else db = SpanToUShort(area.Slice(2)); #endif if (db <= 0) { break; } return(true); } } break; } selector = PlcArea.DB; return(false); }