From 515beff33a3b5a33d0e080ace789e0d89bae7981 Mon Sep 17 00:00:00 2001 From: timyhac Date: Fri, 26 Jul 2024 14:46:39 +1000 Subject: [PATCH] Revert removal, and mark obsolete/deprecated. Although issue #406 says to **remove** these classes, start the process by deprecating them instead. --- src/libplctag/DataTypes/BoolPlcMapper.cs | 74 +++++ src/libplctag/DataTypes/DintPlcMapper.cs | 22 ++ .../DataTypes/Extensions/ArrayExtensions.cs | 119 ++++++++ src/libplctag/DataTypes/IPlcMapper.cs | 57 ++++ src/libplctag/DataTypes/IntPlcMapper.cs | 22 ++ src/libplctag/DataTypes/LintPlcMapper.cs | 22 ++ src/libplctag/DataTypes/LrealPlcMapper.cs | 22 ++ src/libplctag/DataTypes/PlcMapperBase.cs | 85 ++++++ src/libplctag/DataTypes/RealPlcMapper.cs | 23 ++ src/libplctag/DataTypes/Simple/Definitions.cs | 104 +++++++ src/libplctag/DataTypes/SintPlcMapper.cs | 23 ++ src/libplctag/DataTypes/StringPlcMapper.cs | 40 +++ src/libplctag/DataTypes/TagInfoPlcMapper.cs | 102 +++++++ src/libplctag/DataTypes/TimerPlcMapper.cs | 84 ++++++ src/libplctag/DataTypes/UdtInfoPlcMapper.cs | 112 ++++++++ src/libplctag/ITag.cs | 52 ++++ src/libplctag/TagOfT.cs | 261 ++++++++++++++++++ 17 files changed, 1224 insertions(+) create mode 100644 src/libplctag/DataTypes/BoolPlcMapper.cs create mode 100644 src/libplctag/DataTypes/DintPlcMapper.cs create mode 100644 src/libplctag/DataTypes/Extensions/ArrayExtensions.cs create mode 100644 src/libplctag/DataTypes/IPlcMapper.cs create mode 100644 src/libplctag/DataTypes/IntPlcMapper.cs create mode 100644 src/libplctag/DataTypes/LintPlcMapper.cs create mode 100644 src/libplctag/DataTypes/LrealPlcMapper.cs create mode 100644 src/libplctag/DataTypes/PlcMapperBase.cs create mode 100644 src/libplctag/DataTypes/RealPlcMapper.cs create mode 100644 src/libplctag/DataTypes/Simple/Definitions.cs create mode 100644 src/libplctag/DataTypes/SintPlcMapper.cs create mode 100644 src/libplctag/DataTypes/StringPlcMapper.cs create mode 100644 src/libplctag/DataTypes/TagInfoPlcMapper.cs create mode 100644 src/libplctag/DataTypes/TimerPlcMapper.cs create mode 100644 src/libplctag/DataTypes/UdtInfoPlcMapper.cs create mode 100644 src/libplctag/ITag.cs create mode 100644 src/libplctag/TagOfT.cs diff --git a/src/libplctag/DataTypes/BoolPlcMapper.cs b/src/libplctag/DataTypes/BoolPlcMapper.cs new file mode 100644 index 0000000..e57ba0b --- /dev/null +++ b/src/libplctag/DataTypes/BoolPlcMapper.cs @@ -0,0 +1,74 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using libplctag.DataTypes.Extensions; +using System; +using System.Linq; + +namespace libplctag.DataTypes +{ + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class BoolPlcMapper : IPlcMapper, IPlcMapper, IPlcMapper, IPlcMapper + { + public int? ElementSize => 1; + + public PlcType PlcType { get; set; } + public int[] ArrayDimensions { get; set; } + + public int? GetElementCount() + { + if (ArrayDimensions == null) + return null; + + //TODO: Test -> I'm not confident that the overall bool count is packed as a 1D array and not packed by dimension. + //Multiply dimensions for total elements + var totalElements = ArrayDimensions.Aggregate(1, (x, y) => x * y); + return (int)Math.Ceiling((double)totalElements / 32.0); + } + + public int? SetArrayLength(int? elementCount) => (int)Math.Ceiling((double)elementCount.Value / 32.0); + + virtual protected bool[] DecodeArray(Tag tag) + { + if (ElementSize is null) + throw new ArgumentNullException($"{nameof(ElementSize)} cannot be null for array decoding"); + + var buffer = new bool[tag.ElementCount.Value * 32]; + for (int ii = 0; ii < tag.ElementCount.Value * 32; ii++) + { + buffer[ii] = tag.GetBit(ii); + } + return buffer; + } + + virtual protected void EncodeArray(Tag tag, bool[] values) + { + for (int ii = 0; ii < tag.ElementCount.Value * 32; ii++) + { + tag.SetBit(ii, values[ii]); + } + } + + bool IPlcMapper.Decode(Tag tag) => tag.GetUInt8(0) != 0; + + void IPlcMapper.Encode(Tag tag, bool value) => tag.SetUInt8(0, value == true ? (byte)255 : (byte)0); + + bool[] IPlcMapper.Decode(Tag tag) => DecodeArray(tag); + + void IPlcMapper.Encode(Tag tag, bool[] value) => EncodeArray(tag, value); + + bool[,] IPlcMapper.Decode(Tag tag) => DecodeArray(tag).To2DArray(ArrayDimensions[0], ArrayDimensions[1]); + + void IPlcMapper.Encode(Tag tag, bool[,] value) => EncodeArray(tag, value.To1DArray()); + + bool[,,] IPlcMapper.Decode(Tag tag) => DecodeArray(tag).To3DArray(ArrayDimensions[0], ArrayDimensions[1], ArrayDimensions[2]); + + void IPlcMapper.Encode(Tag tag, bool[,,] value) => EncodeArray(tag, value.To1DArray()); + + } +} diff --git a/src/libplctag/DataTypes/DintPlcMapper.cs b/src/libplctag/DataTypes/DintPlcMapper.cs new file mode 100644 index 0000000..48e7a7e --- /dev/null +++ b/src/libplctag/DataTypes/DintPlcMapper.cs @@ -0,0 +1,22 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class DintPlcMapper : PlcMapperBase + { + public override int? ElementSize => 4; + + override public int Decode(Tag tag, int offset) => tag.GetInt32(offset); + + override public void Encode(Tag tag, int offset, int value) => tag.SetInt32(offset, value); + + } +} diff --git a/src/libplctag/DataTypes/Extensions/ArrayExtensions.cs b/src/libplctag/DataTypes/Extensions/ArrayExtensions.cs new file mode 100644 index 0000000..4bf8076 --- /dev/null +++ b/src/libplctag/DataTypes/Extensions/ArrayExtensions.cs @@ -0,0 +1,119 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace libplctag.DataTypes.Extensions +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public static class ArrayExtensions + { + /// + /// Extension method to flatten a 2D array to a 1D array + /// + /// Array Type + /// 2D array to be flattened + /// 1D array + public static T[] To1DArray(this T[,] input) + { + // Step 1: get total size of 2D array, and allocate 1D array. + int size = input.Length; + T[] result = new T[size]; + + // Step 2: copy 2D array elements into a 1D array. + int write = 0; + for (int i = 0; i <= input.GetUpperBound(0); i++) + { + for (int z = 0; z <= input.GetUpperBound(1); z++) + { + result[write++] = input[i, z]; + } + } + // Step 3: return the new array. + return result; + } + + /// + /// Extension method to flatten a 3D array to a 1D array + /// + /// Array Type + /// 3D array to be flattened + /// 1D array + public static T[] To1DArray(this T[,,] input) + { + // Step 1: get total size of 3D array, and allocate 1D array. + int size = input.Length; + T[] result = new T[size]; + + // Step 2: copy 3D array elements into a 1D array. + int write = 0; + for (int i = 0; i <= input.GetUpperBound(0); i++) + { + for (int j = 0; j <= input.GetUpperBound(1); j++) + { + for (int k = 0; k < input.GetUpperBound(2); k++) + { + result[write++] = input[i, j, k]; + } + } + } + // Step 3: return the new array. + return result; + } + + /// + /// Extension method to reshape a 1D array into a 2D array + /// + /// Array Type + /// 1D array to be reshaped + /// Desired height (first index) of 2D array + /// Desired width (second index) of 2D array + /// 2D array + public static T[,] To2DArray(this T[] input, int height, int width) + { + T[,] output = new T[height, width]; + + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + output[i, j] = input[i * width + j]; + } + } + return output; + } + + /// + /// Extension method to reshape a 1D array into a 3D array + /// + /// Array Type + /// 1D array to be reshaped + /// Desired height (first index) of 3D array + /// Desired width (second index) of 3D array + /// Desired length (third index) of 3D array + /// #D array + public static T[,,] To3DArray(this T[] input, int height, int width, int length) + { + T[,,] output = new T[height, width, length]; + + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + for (int k = 0; k < length; k++) + { + output[i, j, k] = input[i * height * width + j * width + k]; + } + } + } + return output; + } + + } +} diff --git a/src/libplctag/DataTypes/IPlcMapper.cs b/src/libplctag/DataTypes/IPlcMapper.cs new file mode 100644 index 0000000..40e4e04 --- /dev/null +++ b/src/libplctag/DataTypes/IPlcMapper.cs @@ -0,0 +1,57 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public interface IPlcMapper + { + /// + /// You can define different marshalling behaviour for different types + /// The PlcType is injected during PlcMapper instantiation, and + /// will be available to you in your marshalling logic + /// + PlcType PlcType { get; set; } + + + /// + /// Provide an integer value for ElementSize if you + /// want to pass this into the tag constructor + /// + int? ElementSize { get; } + + /// + /// The dimensions of the array. Null if not an array. + /// + int[] ArrayDimensions { get; set; } + + /// + /// This is used to convert the number of array elements + /// into the raw element count, which is used by the library. + /// Most of the time, this will be the dimensions multiplied, but occasionally + /// it is not (e.g. BOOL arrays). + /// + int? GetElementCount(); + + /// + /// This is the method that reads/unpacks the underlying value of the tag + /// and returns it as a C# type + /// + /// Tag to be Decoded + /// C# value of tag + T Decode(Tag tag); + + /// + /// This is the method that transforms the C# type into the underlying value of the tag + /// + /// Tag to be encoded to + /// C# value to be transformed + void Encode(Tag tag, T value); + } +} \ No newline at end of file diff --git a/src/libplctag/DataTypes/IntPlcMapper.cs b/src/libplctag/DataTypes/IntPlcMapper.cs new file mode 100644 index 0000000..4525680 --- /dev/null +++ b/src/libplctag/DataTypes/IntPlcMapper.cs @@ -0,0 +1,22 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class IntPlcMapper : PlcMapperBase + { + public override int? ElementSize => 2; + + override public short Decode(Tag tag, int offset) => tag.GetInt16(offset); + + override public void Encode(Tag tag, int offset, short value) => tag.SetInt16(offset, value); + + } +} diff --git a/src/libplctag/DataTypes/LintPlcMapper.cs b/src/libplctag/DataTypes/LintPlcMapper.cs new file mode 100644 index 0000000..49643ed --- /dev/null +++ b/src/libplctag/DataTypes/LintPlcMapper.cs @@ -0,0 +1,22 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class LintPlcMapper : PlcMapperBase + { + public override int? ElementSize => 8; + + override public long Decode(Tag tag, int offset) => tag.GetInt64(offset); + + override public void Encode(Tag tag, int offset, long value) => tag.SetInt64(offset, value); + + } +} diff --git a/src/libplctag/DataTypes/LrealPlcMapper.cs b/src/libplctag/DataTypes/LrealPlcMapper.cs new file mode 100644 index 0000000..cb97021 --- /dev/null +++ b/src/libplctag/DataTypes/LrealPlcMapper.cs @@ -0,0 +1,22 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class LrealPlcMapper : PlcMapperBase + { + + override public int? ElementSize => 8; + + override public double Decode(Tag tag, int offset) => tag.GetFloat64(offset); + + override public void Encode(Tag tag, int offset, double value)=> tag.SetFloat64(offset, value); + } +} diff --git a/src/libplctag/DataTypes/PlcMapperBase.cs b/src/libplctag/DataTypes/PlcMapperBase.cs new file mode 100644 index 0000000..ea210a8 --- /dev/null +++ b/src/libplctag/DataTypes/PlcMapperBase.cs @@ -0,0 +1,85 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using libplctag.DataTypes.Extensions; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public abstract class PlcMapperBase : IPlcMapper, IPlcMapper, IPlcMapper, IPlcMapper + { + public PlcType PlcType { get; set; } + + abstract public int? ElementSize { get; } + + public int[] ArrayDimensions { get; set; } + + //Multiply all the dimensions to get total elements + virtual public int? GetElementCount() => ArrayDimensions?.Aggregate(1, (x, y) => x * y); + + virtual protected T[] DecodeArray(Tag tag) + { + if (ElementSize is null) + throw new ArgumentNullException($"{nameof(ElementSize)} cannot be null for array decoding"); + + + var buffer = new List(); + + var tagSize = tag.GetSize(); + + int offset = 0; + while (offset < tagSize) + { + buffer.Add(Decode(tag, offset)); + offset += ElementSize.Value; + } + + return buffer.ToArray(); + + } + + virtual protected void EncodeArray(Tag tag, T[] values) + { + if (ElementSize is null) + { + throw new ArgumentNullException($"{nameof(ElementSize)} cannot be null for array encoding"); + } + + int offset = 0; + foreach (var item in values) + { + Encode(tag, offset, item); + offset += ElementSize.Value; + } + } + + virtual public T Decode(Tag tag) => Decode(tag, 0); + public abstract T Decode(Tag tag, int offset); + + + virtual public void Encode(Tag tag, T value) => Encode(tag, 0, value); + public abstract void Encode(Tag tag, int offset, T value); + + virtual public void Encode(Tag tag, T[] value) => EncodeArray(tag, value); + + T[] IPlcMapper.Decode(Tag tag) => DecodeArray(tag); + + + T[,] IPlcMapper.Decode(Tag tag) => DecodeArray(tag).To2DArray(ArrayDimensions[0], ArrayDimensions[1]); + + void IPlcMapper.Encode(Tag tag, T[,] value) => EncodeArray(tag, value.To1DArray()); + + T[,,] IPlcMapper.Decode(Tag tag) => DecodeArray(tag).To3DArray(ArrayDimensions[0], ArrayDimensions[1], ArrayDimensions[2]); + + void IPlcMapper.Encode(Tag tag, T[,,] value) => EncodeArray(tag, value.To1DArray()); + } + +} diff --git a/src/libplctag/DataTypes/RealPlcMapper.cs b/src/libplctag/DataTypes/RealPlcMapper.cs new file mode 100644 index 0000000..fe77001 --- /dev/null +++ b/src/libplctag/DataTypes/RealPlcMapper.cs @@ -0,0 +1,23 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class RealPlcMapper : PlcMapperBase + { + + override public int? ElementSize => 4; + + override public float Decode(Tag tag, int offset) => tag.GetFloat32(offset); + + override public void Encode(Tag tag, int offset, float value) => tag.SetFloat32(offset, value); + + } +} diff --git a/src/libplctag/DataTypes/Simple/Definitions.cs b/src/libplctag/DataTypes/Simple/Definitions.cs new file mode 100644 index 0000000..87426ef --- /dev/null +++ b/src/libplctag/DataTypes/Simple/Definitions.cs @@ -0,0 +1,104 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace libplctag.DataTypes.Simple +{ + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagBool : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagBool1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagBool2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagBool3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagDint : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagDint1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagDint2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagDint3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagInt : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagInt1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagInt2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagInt3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLint : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLint1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLint2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLint3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLreal : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLreal1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLreal2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagLreal3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagReal : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagReal1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagReal2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagReal3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagSint : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagSint1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagSint2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagSint3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagString : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagString1D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagString2D : Tag { } + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagString3D : Tag { } + + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagTagInfo : Tag { } + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagTimer : Tag { } + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagUdtInfo : Tag { } + +} diff --git a/src/libplctag/DataTypes/SintPlcMapper.cs b/src/libplctag/DataTypes/SintPlcMapper.cs new file mode 100644 index 0000000..ba0a99e --- /dev/null +++ b/src/libplctag/DataTypes/SintPlcMapper.cs @@ -0,0 +1,23 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class SintPlcMapper : PlcMapperBase + { + + override public int? ElementSize => 1; + + override public sbyte Decode(Tag tag, int offset) => tag.GetInt8(offset); + + override public void Encode(Tag tag, int offset, sbyte value) => tag.SetInt8(offset, value); + + } +} diff --git a/src/libplctag/DataTypes/StringPlcMapper.cs b/src/libplctag/DataTypes/StringPlcMapper.cs new file mode 100644 index 0000000..b5da622 --- /dev/null +++ b/src/libplctag/DataTypes/StringPlcMapper.cs @@ -0,0 +1,40 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class StringPlcMapper : PlcMapperBase + { + + override public int? ElementSize + { + get + { + switch (PlcType) + { + case PlcType.ControlLogix: return 88; + case PlcType.Plc5: return 84; + case PlcType.Slc500: return 84; + case PlcType.LogixPccc: return 84; + case PlcType.Micro800: return 256; //To be Confirmed + case PlcType.MicroLogix: return 84; + default: throw new NotImplementedException(); + } + } + } + + + override public string Decode(Tag tag, int offset) => tag.GetString(offset); + override public void Encode(Tag tag, int offset, string value) => tag.SetString(offset, value); + + } +} \ No newline at end of file diff --git a/src/libplctag/DataTypes/TagInfoPlcMapper.cs b/src/libplctag/DataTypes/TagInfoPlcMapper.cs new file mode 100644 index 0000000..c26049b --- /dev/null +++ b/src/libplctag/DataTypes/TagInfoPlcMapper.cs @@ -0,0 +1,102 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace libplctag.DataTypes +{ + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagInfo + { + public uint Id { get; set; } + public ushort Type { get; set; } + public string Name { get; set; } + public ushort Length { get; set; } + public uint[] Dimensions { get; set; } + } + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TagInfoPlcMapper : IPlcMapper + { + + const int TAG_STRING_SIZE = 200; + + public PlcType PlcType { get; set; } + + //TODO: Is null appropriate since it's unknown? + public int? ElementSize => null; + public int[] ArrayDimensions { get => null; set => throw new NotImplementedException("This plcMapper can only be used to read Tag Information"); } + + public TagInfo Decode(Tag tag, int offset, out int elementSize) + { + + var tagInstanceId = tag.GetUInt32(offset); + var tagType = tag.GetUInt16(offset + 4); + var tagLength = tag.GetUInt16(offset + 6); + var tagArrayDims = new uint[] + { + tag.GetUInt32(offset + 8), + tag.GetUInt32(offset + 12), + tag.GetUInt32(offset + 16) + }; + + var apparentTagNameLength = (int)tag.GetUInt16(offset + 20); + var actualTagNameLength = Math.Min(apparentTagNameLength, TAG_STRING_SIZE * 2 - 1); + + var tagNameBytes = Enumerable.Range(offset + 22, actualTagNameLength) + .Select(o => tag.GetUInt8(o)) + .Select(Convert.ToByte) + .ToArray(); + + var tagName = Encoding.ASCII.GetString(tagNameBytes); + + elementSize = 22 + actualTagNameLength; + + return new TagInfo() + { + Id = tagInstanceId, + Type = tagType, + Name = tagName, + Length = tagLength, + Dimensions = tagArrayDims + }; + + } + + public TagInfo[] Decode(Tag tag) + { + var buffer = new List(); + + var tagSize = tag.GetSize(); + + int offset = 0; + while (offset < tagSize) + { + buffer.Add(Decode(tag, offset, out int elementSize)); + offset += elementSize; + } + + return buffer.ToArray(); + } + + public void Encode(Tag tag, TagInfo[] value) + { + throw new NotImplementedException("This plcMapper can only be used to read Tag Information"); + } + + public int? GetElementCount() + { + //TODO: We know this value after we decode once. SHould we trigger a decode or cache the value after first decode? + return null; + } + } + +} diff --git a/src/libplctag/DataTypes/TimerPlcMapper.cs b/src/libplctag/DataTypes/TimerPlcMapper.cs new file mode 100644 index 0000000..46547ea --- /dev/null +++ b/src/libplctag/DataTypes/TimerPlcMapper.cs @@ -0,0 +1,84 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace libplctag.DataTypes +{ + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class TimerPlcMapper : PlcMapperBase + { + + public override int? ElementSize => 12; + + public override AbTimer Decode(Tag tag, int offset) + { + + // Needed to look at RsLogix documentation for structure of TIMER + var DINT2 = tag.GetInt32(offset); + var DINT1 = tag.GetInt32(offset + 4); + var DINT0 = tag.GetInt32(offset + 8); + + // The third DINT packs a few BOOLs into it + var bitArray = new BitArray(new int[] { DINT2 }); + + var timer = new AbTimer + { + Accumulated = DINT0, // ACC + Preset = DINT1, // PRE + Done = bitArray[29], // DN + InProgress = bitArray[30], // TT + Enabled = bitArray[31] // EN + }; + + return timer; + + } + + public override void Encode(Tag tag, int offset, AbTimer value) + { + var DINT0 = value.Accumulated; + var DINT1 = value.Preset; + + var asdf = new BitArray(32); + asdf[29] = value.Done; + asdf[30] = value.InProgress; + asdf[31] = value.Enabled; + var DINT2 = BitArrayToInt(asdf); + + tag.SetInt32(offset, DINT2); + tag.SetInt32(offset + 4, DINT1); + tag.SetInt32(offset + 8, DINT0); + + } + + static int BitArrayToInt(BitArray binary) + { + if (binary == null) + throw new ArgumentNullException("binary"); + if (binary.Length > 32) + throw new ArgumentException("Must be at most 32 bits long"); + + var result = new int[1]; + binary.CopyTo(result, 0); + return result[0]; + } + } + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class AbTimer + { + public int Preset { get; set; } + public int Accumulated { get; set; } + public bool Enabled { get; set; } + public bool InProgress { get; set; } + public bool Done { get; set; } + } +} diff --git a/src/libplctag/DataTypes/UdtInfoPlcMapper.cs b/src/libplctag/DataTypes/UdtInfoPlcMapper.cs new file mode 100644 index 0000000..5b82ab9 --- /dev/null +++ b/src/libplctag/DataTypes/UdtInfoPlcMapper.cs @@ -0,0 +1,112 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace libplctag.DataTypes +{ + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class UdtFieldInfo + { + public string Name { get; set; } + public ushort Type { get; set; } + public ushort Metadata { get; set; } + public uint Offset { get; set; } + } + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class UdtInfo + { + public uint Size { get; set; } + public string Name { get; set; } + public ushort Id { get; set; } + public ushort NumFields { get; set; } + public ushort Handle { get; set; } + public UdtFieldInfo[] Fields { get; set; } + } + + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class UdtInfoPlcMapper : IPlcMapper + { + public PlcType PlcType { get; set; } + //TODO: Is null appropriate since it's unknown? + public int? ElementSize => null; + public int[] ArrayDimensions { get => null; set => throw new NotImplementedException("This plcMapper can only be used to read"); } + + public UdtInfo Decode(Tag tag) + { + + var template_id = tag.GetUInt16(0); + var member_desc_size = tag.GetUInt32(2); + var udt_instance_size = tag.GetUInt32(6); + var num_members = tag.GetUInt16(10); + var struct_handle = tag.GetUInt16(12); + + var udtInfo = new UdtInfo() + { + Fields = new UdtFieldInfo[num_members], + NumFields = num_members, + Handle = struct_handle, + Id = template_id, + Size = udt_instance_size + }; + + var offset = 14; + + for (int field_index = 0; field_index < num_members; field_index++) + { + ushort field_metadata = tag.GetUInt16(offset); + offset += 2; + + ushort field_element_type = tag.GetUInt16(offset); + offset += 2; + + ushort field_offset = tag.GetUInt16(offset); + offset += 4; + + var field = new UdtFieldInfo() + { + Offset = field_offset, + Metadata = field_metadata, + Type = field_element_type, + }; + + udtInfo.Fields[field_index] = field; + } + + var name_str = tag.GetString(offset).Split(';')[0]; + udtInfo.Name = name_str; + + offset += tag.GetStringTotalLength(offset); + + for (int field_index = 0; field_index < num_members; field_index++) + { + udtInfo.Fields[field_index].Name = tag.GetString(offset); + offset += tag.GetStringTotalLength(offset); + } + + return udtInfo; + + } + + public void Encode(Tag tag, UdtInfo value) + { + throw new NotImplementedException("This plcMapper can only be used to read"); + } + + public int? GetElementCount() + { + //TODO: We know this value after we decode once. SHould we trigger a decode or cache the value after first decode? + return null; + } + } + +} diff --git a/src/libplctag/ITag.cs b/src/libplctag/ITag.cs new file mode 100644 index 0000000..48c14a2 --- /dev/null +++ b/src/libplctag/ITag.cs @@ -0,0 +1,52 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace libplctag +{ + /// + /// An interface to represent any generic tag without + /// exposing its value + /// + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public interface ITag : IDisposable + { + int[] ArrayDimensions { get; set; } + string Gateway { get; set; } + string Name { get; set; } + string Path { get; set; } + PlcType? PlcType { get; set; } + Protocol? Protocol { get; set; } + int? ReadCacheMillisecondDuration { get; set; } + TimeSpan Timeout { get; set; } + bool? UseConnectedMessaging { get; set; } + bool? AllowPacking { get; set; } + TimeSpan? AutoSyncReadInterval { get; set; } + TimeSpan? AutoSyncWriteInterval { get; set; } + DebugLevel DebugLevel { get; set; } + + event EventHandler ReadStarted; + event EventHandler ReadCompleted; + event EventHandler WriteStarted; + event EventHandler WriteCompleted; + event EventHandler Aborted; + event EventHandler Destroyed; + + Status GetStatus(); + void Initialize(); + Task InitializeAsync(CancellationToken token = default); + object Read(); + Task ReadAsync(CancellationToken token = default); + void Write(); + Task WriteAsync(CancellationToken token = default); + + object Value { get; set; } + } +} \ No newline at end of file diff --git a/src/libplctag/TagOfT.cs b/src/libplctag/TagOfT.cs new file mode 100644 index 0000000..4021452 --- /dev/null +++ b/src/libplctag/TagOfT.cs @@ -0,0 +1,261 @@ +// Copyright (c) libplctag.NET contributors +// https://github.com/libplctag/libplctag.NET +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using libplctag.DataTypes; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace libplctag +{ + /// + /// A class that allows for strongly-typed objects tied to PLC tags + /// + /// A class that handles data conversion + /// The desired C# type of Tag.Value + [Obsolete("see - https://github.com/libplctag/libplctag.NET/issues/406")] + public class Tag : IDisposable, ITag where M : IPlcMapper, new() + { + + private readonly Tag _tag; + private readonly IPlcMapper _plcMapper; + + public Tag() + { + _plcMapper = new M(); + _tag = new Tag() + { + ElementSize = _plcMapper.ElementSize, + }; + + _tag.ReadStarted += (s, e) => ReadStarted?.Invoke(this, e); + _tag.ReadCompleted += (s, e) => + { + // If AutoSyncReadInterval is configured, then this event was almost certainly not triggered + // by a call to Read/ReadAsync - and therefore the data needs to be decoded. + if(AutoSyncReadInterval != null) DecodeAll(); + ReadCompleted?.Invoke(this, e); + }; + _tag.WriteStarted += (s, e) => WriteStarted?.Invoke(this, e); + _tag.WriteCompleted += (s, e) => WriteCompleted?.Invoke(this, e); + _tag.Aborted += (s, e) => Aborted?.Invoke(this, e); + _tag.Destroyed += (s, e) => Destroyed?.Invoke(this, e); + } + + /// + public Protocol? Protocol + { + get => _tag.Protocol; + set => _tag.Protocol = value; + } + + /// + public string Gateway + { + get => _tag.Gateway; + set => _tag.Gateway = value; + } + + /// + public string Path + { + get => _tag.Path; + set => _tag.Path = value; + } + + /// + public PlcType? PlcType + { + get => _tag.PlcType; + set + { + _tag.PlcType = value; + if(value.HasValue) + _plcMapper.PlcType = value.Value; + } + } + + /// + public string Name + { + get => _tag.Name; + set => _tag.Name = value; + } + + /// + public bool? UseConnectedMessaging + { + get => _tag.UseConnectedMessaging; + set => _tag.UseConnectedMessaging = value; + } + + /// + public bool? AllowPacking + { + get => _tag.AllowPacking; + set => _tag.AllowPacking = value; + } + + /// + public int? ReadCacheMillisecondDuration + { + get => _tag.ReadCacheMillisecondDuration; + set => _tag.ReadCacheMillisecondDuration = value; + } + + /// + public TimeSpan Timeout + { + get => _tag.Timeout; + set => _tag.Timeout = value; + } + + /// + public TimeSpan? AutoSyncReadInterval + { + get => _tag.AutoSyncReadInterval; + set => _tag.AutoSyncReadInterval = value; + } + + /// + public TimeSpan? AutoSyncWriteInterval + { + get => _tag.AutoSyncWriteInterval; + set => _tag.AutoSyncWriteInterval = value; + } + + /// + public DebugLevel DebugLevel + { + get => _tag.DebugLevel; + set => _tag.DebugLevel = value; + } + + /// + public uint? MaxRequestsInFlight + { + get => _tag.MaxRequestsInFlight; + set => _tag.MaxRequestsInFlight = value; + } + + /// + /// Dimensions of Value if it is an array + /// Ex. {2, 10} for a 2 column, 10 row array + /// Non-arrays can use null (default) + /// + public int[] ArrayDimensions + { + get => _plcMapper.ArrayDimensions; + set + { + _plcMapper.ArrayDimensions = value; + _tag.ElementCount = _plcMapper.GetElementCount(); + } + } + + /// + public void Initialize() + { + _tag.Initialize(); + DecodeAll(); + } + + /// + public async Task InitializeAsync(CancellationToken token = default) + { + await _tag.InitializeAsync(token).ConfigureAwait(false); + DecodeAll(); + } + + /// + public async Task ReadAsync(CancellationToken token = default) + { + await _tag.ReadAsync(token).ConfigureAwait(false); + DecodeAll(); + return Value; + } + + /// + public T Read() + { + _tag.Read(); + DecodeAll(); + return Value; + } + + object ITag.Read() => Read(); + + async Task ITag.ReadAsync(CancellationToken token) => await ReadAsync().ConfigureAwait(false); + + /// + public async Task WriteAsync(CancellationToken token = default) + { + if (!_tag.IsInitialized) + await _tag.InitializeAsync(token).ConfigureAwait(false); + + EncodeAll(); + await _tag.WriteAsync(token).ConfigureAwait(false); + } + + /// + public async Task WriteAsync(T value, CancellationToken token = default) + { + Value = value; + await WriteAsync(token).ConfigureAwait(false); + } + + /// + public void Write() + { + if (!_tag.IsInitialized) + _tag.Initialize(); + + EncodeAll(); + _tag.Write(); + } + + /// + public void Write(T value) + { + Value = value; + Write(); + } + + void DecodeAll() + { + Value = _plcMapper.Decode(_tag); + } + + void EncodeAll() + { + _plcMapper.Encode(_tag, Value); + } + + /// + public Status GetStatus() => _tag.GetStatus(); + + public void Dispose() => _tag.Dispose(); + + ~Tag() + { + Dispose(); + } + + /// + /// The local memory value that can be transferred to/from the PLC + /// + public T Value { get; set; } + object ITag.Value { get => Value; set => Value = (T)value; } + public event EventHandler ReadStarted; + public event EventHandler ReadCompleted; + public event EventHandler WriteStarted; + public event EventHandler WriteCompleted; + public event EventHandler Aborted; + public event EventHandler Destroyed; + + } +}