diff --git a/src/Corsinvest.AllenBradley.PLC.Api.Test/Program.cs b/src/Corsinvest.AllenBradley.PLC.Api.Test/Program.cs index 53acded..bd2fe23 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api.Test/Program.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api.Test/Program.cs @@ -19,8 +19,6 @@ public class Test12 public class Program { - - private static void PrintChange(string @event, ResultOperation result) { Console.Out.WriteLine($"{@event} {result.Timestamp} Changed: {result.Tag.Name} {result.StatusCode}"); @@ -39,11 +37,30 @@ public static void Main(string[] args) { using (var controller = new Controller("10.155.128.192", "1, 0", CPUType.LGX)) { + controller.Timeout = 1000; + // controller.DebugLevel=3; Console.Out.WriteLine("Ping " + controller.Ping(true)); var grp = controller.CreateGroup(); - var tagBPLC1 = grp.CreateTagInt32("TKP_PLC_B_P1"); - tagBPLC1.Read(); + var tag12 = grp.CreateTagInt32("TKP_PLC_D_P1[10]"); + + var tagBPLC1 = grp.CreateTagInt32("TKP_PLC_B_P1"); + tagBPLC1.Read(); + + var tagOvenEnabled = grp.CreateTagInt32("TKP_PLC_B_OVEN"); + var oven = tagOvenEnabled.Read(); + Console.Out.WriteLine(oven.Tag.Value); + + System.Threading.Thread.Sleep(800); + + Console.Out.WriteLine("pippo"); + + oven = tagOvenEnabled.Read(); + Console.Out.WriteLine(oven.Tag.Value); + Console.Out.WriteLine(oven.Tag.ValueManager.GetBits()[0]); + Console.Out.WriteLine(oven.Tag.ValueManager.GetBitsArray()[0]); + Console.Out.WriteLine(oven.Tag.ValueManager.GetBitsString()); + // var tagBPC1 = grp.CreateTagInt32("TKP_PC_B_P1"); // var tagBarcode = grp.CreateTagString("TKP_PLC_S_P1"); @@ -57,6 +74,8 @@ public static void Main(string[] args) tag.Changed += TagChanged; var aa = tag.Read(); +Console.Out.WriteLine(aa); + var tag1 = grp.CreateTagType("Test"); tag.Changed += TagChanged; diff --git a/src/Corsinvest.AllenBradley.PLC.Api/Controller.cs b/src/Corsinvest.AllenBradley.PLC.Api/Controller.cs index 5d47bfc..ab80b3e 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/Controller.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/Controller.cs @@ -42,11 +42,32 @@ public Controller(string ipAddress, string path, CPUType cpuType) public bool FailOperationRaiseException { get; set; } = false; /// - /// Communication timeout + /// Automatic Write when using value. + /// + /// + public bool AutoReadValue { get; set; } = false; + + /// + /// Automatic Write when using value. + /// + /// + public bool AutoWriteValue { get; set; } = false; + + /// + /// Communication timeout millisec. /// /// public int Timeout { get; set; } = 5000; + /// + /// Optional allows the selection of varying levels of debugging output. + /// 1 shows only the more urgent problems. + /// 5 shows almost every action within the library and will generate a very large amount of output. + /// Generally 3 or 4 is most useful when debugging. + /// + /// + public int DebugLevel { get; set; } = 0; + /// /// Groups /// @@ -59,6 +80,13 @@ public Controller(string ipAddress, string path, CPUType cpuType) /// public IReadOnlyList Tags { get { return _tagGroups.SelectMany(a => a.Tags).Distinct().ToList().AsReadOnly(); } } + /// + /// Verify if exists tag with name and return. + /// + /// + /// + public ITag TagExists(string name) { return Tags.Where(a => a.Name == name).FirstOrDefault(); } + /// /// IP address of the gateway for this protocol. Could be the IP address of the PLC you want to access. /// diff --git a/src/Corsinvest.AllenBradley.PLC.Api/Corsinvest.AllenBradley.PLC.Api.csproj b/src/Corsinvest.AllenBradley.PLC.Api/Corsinvest.AllenBradley.PLC.Api.csproj index 1cdf309..e761067 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/Corsinvest.AllenBradley.PLC.Api.csproj +++ b/src/Corsinvest.AllenBradley.PLC.Api/Corsinvest.AllenBradley.PLC.Api.csproj @@ -2,7 +2,7 @@ netstandard2.0 true - 0.0.7 + 0.1.0 Corsinvest Srl Daniele Corsini Corsinvest Srl diff --git a/src/Corsinvest.AllenBradley.PLC.Api/ITag.cs b/src/Corsinvest.AllenBradley.PLC.Api/ITags.cs similarity index 85% rename from src/Corsinvest.AllenBradley.PLC.Api/ITag.cs rename to src/Corsinvest.AllenBradley.PLC.Api/ITags.cs index f7317c1..1dd6b7e 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/ITag.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/ITags.cs @@ -23,7 +23,7 @@ public interface ITag : IDisposable /// Handle creation Tag /// /// - IntPtr Handle { get; } + int Handle { get; } /// /// Controller reference. @@ -48,6 +48,12 @@ public interface ITag : IDisposable /// int Length { get; } + /// + /// Type value. + /// + /// + Type TypeValue { get; } + /// /// Old value tag. /// @@ -66,6 +72,12 @@ public interface ITag : IDisposable /// bool IsChangedValue { get; } + /// + /// Indicate if Tag is in read only.async Write raise exception. + /// + /// + bool ReadOnly { get; set; } + /// /// Value manager /// @@ -95,6 +107,12 @@ public interface ITag : IDisposable /// bool IsWrite { get; } + /// + /// Abort any outstanding IO to the PLC. + /// + /// + int Abort(); + /// /// Get size tag. /// diff --git a/src/Corsinvest.AllenBradley.PLC.Api/NativeMethod.cs b/src/Corsinvest.AllenBradley.PLC.Api/NativeMethod.cs index 844b119..0fdc4fb 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/NativeMethod.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/NativeMethod.cs @@ -8,72 +8,93 @@ internal static class NativeMethod private const string DLL_NAME = "plctag"; [DllImport(DLL_NAME, EntryPoint = "plc_tag_create", CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr plc_tag_create([MarshalAs(UnmanagedType.LPStr)] string lpString); + internal static extern int plc_tag_create([MarshalAs(UnmanagedType.LPStr)] string lpString, int timeout); [DllImport(DLL_NAME, EntryPoint = "plc_tag_destroy", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_destroy(IntPtr tag); + internal static extern int plc_tag_destroy(int tag); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_abort", CallingConvention = CallingConvention.Cdecl)] + internal static extern int plc_tag_abort(int tag); [DllImport(DLL_NAME, EntryPoint = "plc_tag_status", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_status(IntPtr tag); + internal static extern int plc_tag_status(int tag); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_size", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_get_size(IntPtr tag); + internal static extern int plc_tag_get_size(int tag); [DllImport(DLL_NAME, EntryPoint = "plc_tag_decode_error", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr plc_tag_decode_error(int error); [DllImport(DLL_NAME, EntryPoint = "plc_tag_read", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_read(IntPtr tag, int timeout); + internal static extern int plc_tag_read(int tag, int timeout); [DllImport(DLL_NAME, EntryPoint = "plc_tag_write", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_write(IntPtr tag, int timeout); + internal static extern int plc_tag_write(int tag, int timeout); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_uint16", CallingConvention = CallingConvention.Cdecl)] - internal static extern ushort plc_tag_get_uint16(IntPtr tag, int offset); + internal static extern ushort plc_tag_get_uint16(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_int16", CallingConvention = CallingConvention.Cdecl)] - internal static extern short plc_tag_get_int16(IntPtr tag, int offset); + internal static extern short plc_tag_get_int16(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_uint16", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_uint16(IntPtr tag, int offset, ushort val); + internal static extern int plc_tag_set_uint16(int tag, int offset, ushort val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_int16", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_int16(IntPtr tag, int offset, short val); + internal static extern int plc_tag_set_int16(int tag, int offset, short val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_uint8", CallingConvention = CallingConvention.Cdecl)] - internal static extern byte plc_tag_get_uint8(IntPtr tag, int offset); + internal static extern byte plc_tag_get_uint8(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_int8", CallingConvention = CallingConvention.Cdecl)] - internal static extern sbyte plc_tag_get_int8(IntPtr tag, int offset); + internal static extern sbyte plc_tag_get_int8(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_uint8", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_uint8(IntPtr tag, int offset, byte val); + internal static extern int plc_tag_set_uint8(int tag, int offset, byte val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_int8", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_int8(IntPtr tag, int offset, sbyte val); + internal static extern int plc_tag_set_int8(int tag, int offset, sbyte val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_float32", CallingConvention = CallingConvention.Cdecl)] - internal static extern float plc_tag_get_float32(IntPtr tag, int offset); + internal static extern float plc_tag_get_float32(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_float32", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_float32(IntPtr tag, int offset, float val); + internal static extern int plc_tag_set_float32(int tag, int offset, float val); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_float64", CallingConvention = CallingConvention.Cdecl)] + internal static extern double plc_tag_get_float64(int tag, int offset); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_float64", CallingConvention = CallingConvention.Cdecl)] + internal static extern int plc_tag_set_float64(int tag, int offset, double val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_uint32", CallingConvention = CallingConvention.Cdecl)] - internal static extern uint plc_tag_get_uint32(IntPtr tag, int offset); + internal static extern uint plc_tag_get_uint32(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_int32", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_get_int32(IntPtr tag, int offset); + internal static extern int plc_tag_get_int32(int tag, int offset); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_uint32", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_uint32(IntPtr tag, int offset, uint val); + internal static extern int plc_tag_set_uint32(int tag, int offset, uint val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_int32", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_set_int32(IntPtr tag, int offset, int val); + internal static extern int plc_tag_set_int32(int tag, int offset, int val); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_uint64", CallingConvention = CallingConvention.Cdecl)] + internal static extern ulong plc_tag_get_uint64(int tag, int offset); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_get_int64", CallingConvention = CallingConvention.Cdecl)] + internal static extern long plc_tag_get_int64(int tag, int offset); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_uint64", CallingConvention = CallingConvention.Cdecl)] + internal static extern int plc_tag_set_uint64(int tag, int offset, ulong val); + + [DllImport(DLL_NAME, EntryPoint = "plc_tag_set_int64", CallingConvention = CallingConvention.Cdecl)] + internal static extern int plc_tag_set_int64(int tag, int offset, long val); [DllImport(DLL_NAME, EntryPoint = "plc_tag_lock", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_lock(IntPtr tag); + internal static extern int plc_tag_lock(int tag); [DllImport(DLL_NAME, EntryPoint = "plc_tag_unlock", CallingConvention = CallingConvention.Cdecl)] - internal static extern int plc_tag_unlock(IntPtr tag); + internal static extern int plc_tag_unlock(int tag); } } \ No newline at end of file diff --git a/src/Corsinvest.AllenBradley.PLC.Api/ResultOperation.cs b/src/Corsinvest.AllenBradley.PLC.Api/ResultOperation.cs index 86fba80..4d734a6 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/ResultOperation.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/ResultOperation.cs @@ -54,5 +54,18 @@ public static ResultOperation Reduce(IEnumerable results) results.Sum(a => a.ExecutionTime), results.Sum(a => a.StatusCode) != 0 ? results.Max(a => a.StatusCode) : 0); } + + /// + /// Information result. + /// + /// + public override string ToString() + { + return $@"Tag Name: {Tag.Name} +Tag Value: {Tag.Value} +Timestamp: {Timestamp} +ExecutionTime: {ExecutionTime} +StatusCode: {StatusCode}"; + } } } \ No newline at end of file diff --git a/src/Corsinvest.AllenBradley.PLC.Api/StatusCodeOperation.cs b/src/Corsinvest.AllenBradley.PLC.Api/StatusCodeOperation.cs index b18adfe..070fcc6 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/StatusCodeOperation.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/StatusCodeOperation.cs @@ -18,186 +18,200 @@ public static class StatusCodeOperation public const int STATUS_OK = 0; /// - /// A null pointer was found during processing. Often this is returned when an argument is null. + /// The operation was aborted. /// - public const int ERR_NULL_PTR = -1; + public const int ERR_ERR_ABORT = -1; /// - /// An attempt to access a value outside of the allow limits was made. - /// Usually this is in conjunction with accessing a data word in a tag. + /// The operation failed due to incorrect configuration. + /// Usually returned from a remote system. /// - public const int ERR_OUT_OF_BOUNDS = -2; + public const int ERR_BAD_CONFIG = -2; /// - /// Unable to allocate memory. + /// The connection failed for some reason. + /// This can mean that the remote PLC was power cycled, for instance. /// - public const int ERR_NO_MEM = -3; + public const int ERR_BAD_CONNECTION = -3; /// - /// Unable to add to a linked list internally. + /// The data received from the remote PLC was undecipherable or otherwise not able to be processed. + /// Can also be returned from a remote system that cannot process the data sent to it. /// - public const int ERR_LL_ADD = -4; + public const int ERR_BAD_DATA = -4; /// - /// Illegal or unknown parameter value. + /// Usually returned from a remote system when something addressed does not exist. /// - public const int ERR_BAD_PARAM = -5; + public const int ERR_BAD_DEVICE = -5; /// - /// Error creating a tag or internal value. + /// Usually returned when the library is unable to connect to a remote system. /// - public const int ERR_CREATE = -6; + public const int ERR_BAD_GATEWAY = -6; /// - /// + /// A common error return when something is not correct with the tag creation attribute string. /// - public const int ERR_NOT_EMPTY = -7; + public const int ERR_BAD_PARAM = -7; /// - /// Error opening a socket or other OS-level item. + /// Usually returned when the remote system returned an unexpected response. /// - public const int ERR_OPEN = -8; + public const int ERR_BAD_REPLY = -8; /// - /// Error setting socket options or similar. + /// Usually returned by a remote system when something is not in a good state. /// - public const int ERR_SET = -9; + public const int ERR_BAD_STATUS = -9; /// - /// Error writing. + /// An error occurred trying to close some resource. /// - public const int ERR_WRITE = -10; + public const int ERR_CLOSE = -10; /// - /// Operation did not complete in the time allowed. + /// An error occurred trying to create some internal resource. /// - public const int ERR_TIMEOUT = -11; + public const int ERR_CREATE = -11; /// - /// Did not receive an ACK in the time allowed. + /// An error returned by a remote system when something is incorrectly duplicated + /// (i.e. a duplicate connection ID). /// - public const int ERR_TIMEOUT_ACK = -12; + public const int ERR_DUPLICATE = -12; /// - /// Exceeded allowed number of retries. + /// An error was returned when trying to encode some data such as a tag name. /// - public const int ERR_RETRIES = -13; + public const int ERR_ENCODE = -13; /// - /// Error reading. + /// An internal library error. + /// It would be very unusual to see this. /// - public const int ERR_READ = -14; + public const int ERR_MUTEX_DESTROY = -14; /// - /// Garbled or unexpected response from remote system. + /// An internal library error. + /// It would be very unusual to see this. /// - public const int ERR_BAD_DATA = -15; + public const int ERR_MUTEX_INIT = -15; /// - /// Unable to encode part of the transaction. + /// An internal library error. + /// It would be very unusual to see this. /// - public const int ERR_ENCODE = -16; + public const int ERR_MUTEX_LOCK = -16; /// - /// Unable to decode part of the returned transaction. + /// An internal library error. + /// It would be very unusual to see this. /// - public const int ERR_DECODE = -17; + public const int ERR_MUTEX_UNLOCK = -17; /// - /// Unsupported operation (i.e. tag type does not support the operation). + /// Often returned from the remote system when an operation is not permitted. /// - public const int ERR_UNSUPPORTED = -18; + public const int ERR_NOT_ALLOWED = -18; /// - /// Argument too long. Usually a string or name. + /// Often returned from the remote system when something is not found. /// - public const int ERR_TOO_LONG = -19; + public const int ERR_NOT_FOUND = -19; /// - /// Error closing a socket or similar OS construct. + /// Returned when a valid operation is not implemented. /// - public const int ERR_CLOSE = -20; + public const int ERR_NOT_IMPLEMENTED = -20; /// - /// Operation not permitted. + /// Returned when expected data is not present. /// - public const int ERR_NOT_ALLOWED = -21; + public const int ERR_NO_DATA = -21; /// - /// Unable to set up background thread. + /// Similar to NOT_FOUND. /// - public const int ERR_THREAD = -22; + public const int ERR_NO_MATCH = -22; /// - /// No data received. + /// Returned by the library when memory allocation fails. /// - public const int ERR_NO_DATA = -23; + public const int ERR_NO_MEM = -23; /// - /// Unable to join with thread. + /// Returned by the remote system when some resource allocation fails. /// - public const int ERR_THREAD_JOIN = -24; + public const int ERR_NO_RESOURCES = -24; /// - /// Unable to create thread. + /// Usually an internal error, but can be returned when an invalid handle is used with an API call. /// - public const int ERR_THREAD_CREATE = -25; + public const int ERR_NULL_PTR = -25; /// - /// Error while attempting to destroy OS-level mutex. + /// Returned when an error occurs opening a resource such as a socket. /// - public const int ERR_MUTEX_DESTROY = -26; + public const int ERR_OPEN = -26; /// - /// Error while attempting to unlock mutex. + /// Usually returned when trying to write a value into a tag outside of the tag data bounds. /// - public const int ERR_MUTEX_UNLOCK = -27; + public const int ERR_OUT_OF_BOUNDS = -27; /// - /// Error while attempting to initialize mutex. + /// Returned when an error occurs during a read operation. + /// Usually related to socket problems. /// - public const int ERR_MUTEX_INIT = -28; + public const int ERR_READ = -28; /// - /// Error while attempting to lock mutex. + /// An unspecified or untranslatable remote error causes this. /// - public const int ERR_MUTEX_LOCK = -29; + public const int ERR_REMOTE_ERR = -29; /// - /// Tag operation not implemented. + /// An internal library error. If you see this, it is likely that everything is about to crash. /// - public const int ERR_NOT_IMPLEMENTED = -30; + public const int ERR_THREAD_CREATE = -30; /// - /// Illegal or unknown value for device type (CPU). + /// Another internal library error. + /// It is very unlikely that you will see this. /// - public const int ERR_BAD_DEVICE = -31; + public const int ERR_THREAD_JOIN = -31; /// - /// Garbled or unknown gateway IP. + /// An operation took too long and timed out. /// - public const int ERR_BAD_GATEWAY = -32; + public const int ERR_TIMEOUT = -32; /// - /// Error reported from remote end. + /// More data was returned than was expected. /// - public const int ERR_REMOTE_ERR = -33; + public const int ERR_TOO_LARGE = -33; /// - /// Operation failed due to target object not found. + /// Insufficient data was returned from the remote system. /// - public const int ERR_NOT_FOUND = -34; + public const int ERR_TOO_SMALL = -34; /// - /// Operation aborted. + /// The operation is not supported on the remote system. /// - public const int ERR_ABORT = -35; + public const int ERR_UNSUPPORTED = -35; /// - /// (Windows only) Error initializing/terminating use of Windows sockets. + /// A Winsock-specific error occurred (only on Windows). /// public const int ERR_WINSOCK = -36; + /// + /// An error occurred trying to write, usually to a socket. + /// + public const int ERR_WRITE = -37; + /// /// Check code in error /// diff --git a/src/Corsinvest.AllenBradley.PLC.Api/Tag.cs b/src/Corsinvest.AllenBradley.PLC.Api/Tag.cs index 961d802..265bf68 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/Tag.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/Tag.cs @@ -29,31 +29,31 @@ private Tag() { } /// The size of an element in bytes. The tag is assumed to be composed of elements of the same size. /// For structure tags, use the total size of the structure. /// elements count: 1- single, n-array. - /// - internal Tag(Controller controller, string name, int size, int length = 1, int debugLevel = 0) + internal Tag(Controller controller, string name, int size, int length = 1) { Controller = controller; Name = name; Size = size; Length = length; ValueManager = new TagValueManager(this); + TypeValue = typeof(TType); var url = $"protocol=ab_eip&gateway={controller.IPAddress}"; if (!string.IsNullOrEmpty(controller.Path)) { url += $"&path={controller.Path}"; } url += $"&cpu={controller.CPUType}&elem_size={Size}&elem_count={Length}&name={Name}"; - if (debugLevel > 0) { url = $"&debug={debugLevel}"; } - - Value = TagHelper.CreateObject(Length); + if (controller.DebugLevel > 0) { url += $"&debug={controller.DebugLevel}"; } //create reference - Handle = NativeMethod.plc_tag_create(url); + Handle = NativeMethod.plc_tag_create(url, controller.Timeout); + + Value = TagHelper.CreateObject(Length); } /// /// Handle creation Tag /// /// - public IntPtr Handle { get; } + public int Handle { get; } /// /// Controller reference. @@ -78,6 +78,17 @@ internal Tag(Controller controller, string name, int size, int length = 1, int d /// public int Length { get; } + /// + /// Type value. + /// + public Type TypeValue { get; } + + /// + /// Indicate if Tag is in read only.async Write raise exception. + /// + /// + public bool ReadOnly { get; set; } = false; + /// /// Value manager /// @@ -97,11 +108,17 @@ internal Tag(Controller controller, string name, int size, int length = 1, int d /// public TType Value { - get => (TType)ValueManager.Get(_value, 0); + get + { + if (Controller.AutoReadValue) { Read(); } + return (TType)ValueManager.Get(_value, 0); + } + set { _value = value; ValueManager.Set(value, 0); + if (Controller.AutoWriteValue) { Write(); } } } @@ -208,6 +225,8 @@ private static T DeepClone(T obj) /// public ResultOperation Write() { + if (ReadOnly) { throw new InvalidOperationException("Tag is set read only!"); } + var timestamp = DateTime.Now; var watch = Stopwatch.StartNew(); var statusCode = NativeMethod.plc_tag_write(Handle, Controller.Timeout); @@ -225,6 +244,12 @@ public ResultOperation Write() return result; } + /// + /// Abort any outstanding IO to the PLC. + /// + /// + public int Abort() { return NativeMethod.plc_tag_abort(Handle); } + /// /// Get size tag read from PLC. /// diff --git a/src/Corsinvest.AllenBradley.PLC.Api/TagGroup.cs b/src/Corsinvest.AllenBradley.PLC.Api/TagGroup.cs index 2cf1f92..4829afc 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/TagGroup.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/TagGroup.cs @@ -145,7 +145,7 @@ public void RemoveTag(ITag tag) /// public Tag CreateTagType(string name) { - return CreateTagType(name, TagSize.GetSizeFromObject(TagHelper.CreateObject(1))); + return CreateTagType(name, TagSize.GetSizeObject(TagHelper.CreateObject(1))); } /// @@ -173,16 +173,14 @@ public Tag CreateTagType(string name, int size, int le /// Type to create /// public Tag CreateTagArray(string name, int length) + where TCustomType : IList { var type = typeof(TCustomType); if (!type.IsArray) { throw new ArgumentException("Is not array!"); } + if (length <= 0) { throw new ArgumentException("Length > 0!"); } - var obj = (Array)Activator.CreateInstance(type, length); - TagValueManager.FixStringNullToEmpty(obj); - - return CreateTagType(name, - TagSize.GetSizeFromObject(obj.GetValue(0)), - length); + var obj = TagHelper.CreateObject(length); + return CreateTagType(name, TagSize.GetSizeObject(obj[0]), length); } #endregion diff --git a/src/Corsinvest.AllenBradley.PLC.Api/TagHelper.cs b/src/Corsinvest.AllenBradley.PLC.Api/TagHelper.cs index f9f165e..7a160a2 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/TagHelper.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/TagHelper.cs @@ -1,5 +1,8 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Corsinvest.AllenBradley.PLC.Api { @@ -8,7 +11,13 @@ namespace Corsinvest.AllenBradley.PLC.Api /// public static class TagHelper { - internal static TType CreateObject(int length) + /// + /// Create object from Type. + /// + /// + /// + /// + public static TType CreateObject(int length) { var obj = default(TType); var typeTType = typeof(TType); @@ -26,11 +35,63 @@ internal static TType CreateObject(int length) obj = (TType)Activator.CreateInstance(typeTType); } - TagValueManager.FixStringNullToEmpty(obj); + FixStringNullToEmpty(obj); return obj; } + /// + /// Fix string null to empty. + /// + /// + private static void FixStringNullToEmpty(object obj) + { + var type = obj.GetType(); + if (type == typeof(string)) + { + if (obj == null) { obj = string.Empty; } + } + else if (type.IsArray && type.GetElementType() == typeof(string)) + { + var array = GetArray(obj); + for (int i = 0; i < array.Length; i++) + { + if (array.GetValue(i) == null) { array.SetValue(string.Empty, i); } + } + } + else if (type.IsClass && !type.IsAbstract) + { + foreach (var property in GetAccessableProperties(type)) + { + if (property.PropertyType == typeof(string)) + { + if (property.GetValue(obj) == null) { property.SetValue(obj, string.Empty); } + } + else + { + FixStringNullToEmpty(property.GetValue(obj)); + } + } + } + } + + internal static IEnumerable GetAccessableProperties(Type type) + { + return type.GetProperties(BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public) + .Where(p => p.GetSetMethod() != null); + } + + internal static Array GetArray(object value) + { + var array = (Array)value; + if (array.Length <= 0) + { + throw new Exception("Cannot determine size of class, " + + "because an array is defined which has no fixed size greater than zero."); + } + return array; + } + /// /// Performs Linear scaling conversion. /// @@ -76,7 +137,6 @@ public static double ScaleSquareRoot(this ITag tag, double minRaw, double maxRaw public static int BitsToNumber(BitArray bits) { if (bits == null) { throw new ArgumentNullException("binary"); } - if (bits.Length > 32) { throw new ArgumentException("must be at most 32 bits long"); } var result = new int[1]; bits.CopyTo(result, 0); diff --git a/src/Corsinvest.AllenBradley.PLC.Api/TagSize.cs b/src/Corsinvest.AllenBradley.PLC.Api/TagSize.cs index 18c03d1..07552d0 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/TagSize.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/TagSize.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace Corsinvest.AllenBradley.PLC.Api @@ -39,11 +40,26 @@ public static class TagSize /// public const int UINT32 = INT32; + /// + /// + /// + public const int INT64 = 8; + + /// + /// + /// + public const int UINT64 = INT64; + /// /// /// public const int FLOAT32 = 4; + /// + /// + /// + public const int FLOAT64 = 8; + /// /// /// @@ -54,7 +70,9 @@ public static class TagSize /// /// public static IReadOnlyDictionary NativeTypes { get; } = new Dictionary - { + { + { typeof(long), INT64 }, + { typeof(ulong), UINT64 }, { typeof(int), INT32 }, { typeof(uint), UINT32 }, { typeof(short), INT16 }, @@ -63,7 +81,7 @@ public static class TagSize { typeof(byte), UINT8 }, { typeof(string), STRING }, { typeof(float), FLOAT32 }, - { typeof(double), FLOAT32 }, + { typeof(double), FLOAT64 }, }; /// @@ -71,14 +89,14 @@ public static class TagSize /// /// /// - public static int GetSizeFromObject(object obj) + public static int GetSizeObject(object obj) { var size = 0; var type = obj.GetType(); if (type.IsArray) { - foreach (var el in TagValueManager.GetArray(obj)) { size += GetSizeFromObject(el); } + foreach (var el in TagHelper.GetArray(obj)) { size += GetSizeObject(el); } } else { @@ -86,10 +104,9 @@ public static int GetSizeFromObject(object obj) { if (type.IsClass && !type.IsAbstract) { - foreach (var property in TagValueManager.GetAccessableProperties(type)) - { - size += GetSizeFromObject(property.GetValue(obj)); - } + size += TagHelper.GetAccessableProperties(type) + .Select(a => GetSizeObject(a.GetValue(obj))) + .Sum(); } } } diff --git a/src/Corsinvest.AllenBradley.PLC.Api/TagValueManager.cs b/src/Corsinvest.AllenBradley.PLC.Api/TagValueManager.cs index 2992b57..781530b 100644 --- a/src/Corsinvest.AllenBradley.PLC.Api/TagValueManager.cs +++ b/src/Corsinvest.AllenBradley.PLC.Api/TagValueManager.cs @@ -23,8 +23,6 @@ public class TagValueManager internal TagValueManager(ITag tag) { _tag = tag; } - private TagValueManager() { } - /// /// Get local value /// @@ -36,25 +34,28 @@ internal object Get(object obj, int offset = 0) var type = obj.GetType(); if (type.IsArray) { - var array = GetArray(obj); + var array = TagHelper.GetArray(obj); for (int i = 0; i < array.Length; i++) { var el = array.GetValue(i); array.SetValue(Get(el, offset), i); - offset += TagSize.GetSizeFromObject(el); + offset += TagSize.GetSizeObject(el); } return array; } else { - if (type == typeof(int)) { return GetInt32(offset); } + if (type == typeof(long)) { return GetInt64(offset); } + else if (type == typeof(ulong)) { return GetUInt64(offset); } + else if (type == typeof(int)) { return GetInt32(offset); } else if (type == typeof(uint)) { return GetUInt32(offset); } else if (type == typeof(short)) { return GetInt16(offset); } else if (type == typeof(ushort)) { return GetUInt16(offset); } else if (type == typeof(sbyte)) { return GetInt8(offset); } else if (type == typeof(byte)) { return GetUInt8(offset); } + else if (type == typeof(float)) { return GetFloat32(offset); } + else if (type == typeof(double)) { return GetFloat64(offset); } else if (type == typeof(string)) { return GetString(offset); } - else if (type == typeof(float) || type == typeof(double)) { return GetFloat32(offset); } else if (type.IsClass && !type.IsAbstract) { return GetType(obj, offset); } else { throw new Exception("Error data type!"); } } @@ -70,22 +71,25 @@ internal void Set(object value, int offset = 0) var type = value.GetType(); if (type.IsArray) { - foreach (var el in GetArray(value)) + foreach (var el in TagHelper.GetArray(value)) { Set(el, offset); - offset += TagSize.GetSizeFromObject(el); + offset += TagSize.GetSizeObject(el); } } else { - if (type == typeof(int)) { SetInt32((int)value, offset); } + if (type == typeof(long)) { SetInt64((int)value, offset); } + else if (type == typeof(ulong)) { SetUInt64((uint)value, offset); } + else if (type == typeof(int)) { SetInt32((int)value, offset); } else if (type == typeof(uint)) { SetUInt32((uint)value, offset); } else if (type == typeof(short)) { SetInt16((short)value, offset); } else if (type == typeof(ushort)) { SetUInt16((ushort)value, offset); } else if (type == typeof(sbyte)) { SetInt8((sbyte)value, offset); } else if (type == typeof(byte)) { SetUInt8((byte)value, offset); } else if (type == typeof(string)) { SetString((string)value, offset); } - else if (type == typeof(float) || type == typeof(double)) { SetFloat32((float)value, offset); } + else if (type == typeof(float)) { SetFloat32((float)value, offset); } + else if (type == typeof(double)) { SetFloat64((float)value, offset); } else if (type.IsClass && !type.IsAbstract) { SetType(value, offset); } else { throw new Exception("Error data type!"); } } @@ -175,6 +179,35 @@ internal void Set(object value, int offset = 0) /// public void SetInt32(int value, int offset = 0) { NativeMethod.plc_tag_set_int32(_tag.Handle, offset, value); } + /// + /// Get local value UInt64 + /// + /// + /// + public ulong GetUInt64(int offset = 0) { return NativeMethod.plc_tag_get_uint64(_tag.Handle, offset); } + + /// + /// Set local value UInt64 + /// + /// + /// + public void SetUInt64(ulong value, int offset = 0) { NativeMethod.plc_tag_set_uint64(_tag.Handle, offset, value); } + + + /// + /// Get local value Int64 + /// + /// + /// + public long GetInt64(int offset = 0) { return NativeMethod.plc_tag_get_int64(_tag.Handle, offset); } + + /// + /// Set local value Int64 + /// + /// + /// + public void SetInt64(long value, int offset = 0) { NativeMethod.plc_tag_set_int64(_tag.Handle, offset, value); } + /// /// Get local value Float32 /// @@ -189,6 +222,20 @@ internal void Set(object value, int offset = 0) /// public void SetFloat32(float value, int offset = 0) { NativeMethod.plc_tag_set_float32(_tag.Handle, offset, value); } + /// + /// Get local value Float + /// + /// + /// + public double GetFloat64(int offset = 0) { return NativeMethod.plc_tag_get_float64(_tag.Handle, offset); } + + /// + /// Set local value Float + /// + /// + /// + public void SetFloat64(double value, int offset = 0) { NativeMethod.plc_tag_set_float64(_tag.Handle, offset, value); } + /// /// Get local value String /// @@ -227,10 +274,7 @@ public void SetString(string value, int offset = 0) } // pad with zeros - for (; strIdx < MAX_LENGT_STRING; strIdx++) - { - SetUInt8(0, offset + BYTE_HEADER_LENGTH_STRING + strIdx); - } + for (; strIdx < MAX_LENGT_STRING; strIdx++) { SetUInt8(0, offset + BYTE_HEADER_LENGTH_STRING + strIdx); } } /// @@ -238,15 +282,7 @@ public void SetString(string value, int offset = 0) /// /// /// - public bool GetBit(int index) - { - if (_tag.Size * 8 <= index) { throw new IndexOutOfRangeException("Index out of bound!"); } - if (_tag.Size == TagSize.INT32) { return (GetUInt32(0) & (1 << index)) != 0; } - else if (_tag.Size == TagSize.INT16) { return (GetUInt16(0) & (1 << index)) != 0; } - else if (_tag.Size == TagSize.INT8) { return (GetUInt8(0) & (1 << index)) != 0; } - - throw new Exception("Error data type!"); - } + public bool GetBit(int index) { return ((long)GetNumvericValue() & (1 << index)) != 0; } /// /// Set bit from index and value @@ -256,36 +292,32 @@ public bool GetBit(int index) public void SetBit(int index, bool value) { if (_tag.Size * 8 <= index) { throw new IndexOutOfRangeException("Index out of bound!"); } - var index2 = Math.Pow(2, index); + var index2 = (ulong)Math.Pow(2, index); - if (_tag.Size == TagSize.INT32) - { - var data = GetUInt32(0); - SetUInt32(value ? data | (uint)index2 : data ^ (uint)index2, 0); - } - else if (_tag.Size == TagSize.INT16) - { - var data = GetUInt16(0); - SetUInt16((ushort)(value ? data | (ushort)index2 : data ^ (ushort)index2), 0); - } - else if (_tag.Size == TagSize.INT8) - { - var data = GetUInt8(0); - SetUInt8((byte)(value ? data | (byte)index2 : data ^ (byte)index2), 0); - } + var currValue = GetNumvericValue(); + var newValue = value ? (ulong)currValue | index2 : (ulong)currValue ^ index2; + Set(Convert.ChangeType(newValue, currValue.GetType())); } /// /// Get bit array from value /// /// - public BitArray GetBits() - { - if (_tag.Size == TagSize.INT32) { return new BitArray(new[] { (int)GetUInt32(0) }); } - else if (_tag.Size == TagSize.INT16) { return new BitArray(new[] { (int)GetUInt16(0) }); } - else if (_tag.Size == TagSize.INT8) { return new BitArray(new[] { GetUInt8(0) }); } + public BitArray GetBits() { return new BitArray(new[] { (int)GetNumvericValue() }); } + + /// + /// Get bit array from value + /// + /// + public bool[] GetBitsArray() { return GetBits().Cast().ToArray(); } - throw new Exception("Error data type!"); + /// + /// Get bit string format + /// + /// + public string GetBitsString() + { + return new string(GetBits().Cast().Select(a => a ? '1' : '0').ToArray()); } /// @@ -295,8 +327,6 @@ public BitArray GetBits() public void SetBits(BitArray bits) { if (bits == null) { throw new ArgumentNullException("binary"); } - if (bits.Length > 32) { throw new ArgumentOutOfRangeException("must be at most 32 bits long"); } - for (int i = 0; i < _tag.Size * 8; i++) { SetBit(i, bits[i]); } } @@ -307,11 +337,11 @@ public void SetBits(BitArray bits) /// public void SetType(object obj, int offset = 0) { - foreach (var property in GetAccessableProperties(obj.GetType())) + foreach (var property in TagHelper.GetAccessableProperties(obj.GetType())) { var value = property.GetValue(obj); Set(value, offset); - offset += TagSize.GetSizeFromObject(value); + offset += TagSize.GetSizeObject(value); } } @@ -323,65 +353,35 @@ public void SetType(object obj, int offset = 0) /// public object GetType(object obj, int offset = 0) { - foreach (var property in GetAccessableProperties(obj.GetType())) + foreach (var property in TagHelper.GetAccessableProperties(obj.GetType())) { var value = property.GetValue(obj); property.SetValue(obj, Get(value, offset)); - offset += TagSize.GetSizeFromObject(value); + offset += TagSize.GetSizeObject(value); } return obj; } - internal static IEnumerable GetAccessableProperties(Type type) + private object GetNumvericValue(int offset = 0) { - return type.GetProperties(BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public) - .Where(p => p.GetSetMethod() != null); + if (IsNumericInteger()) { return Get(_tag.Value, offset); } + else { throw new Exception("Error data type!"); } } - internal static Array GetArray(object value) + private bool IsNumericInteger() { - var array = (Array)value; - if (array.Length <= 0) + switch (Type.GetTypeCode(_tag.TypeValue)) { - throw new Exception("Cannot determine size of class, " + - "because an array is defined which has no fixed size greater than zero."); - } - return array; - } - - /// - /// Fix string null to empty. - /// - /// - internal static void FixStringNullToEmpty(object obj) - { - var type = obj.GetType(); - if (type == typeof(string)) - { - if (obj == null) { obj = string.Empty; } - } - else if (type.IsArray && type.GetElementType() == typeof(string)) - { - var array = GetArray(obj); - for (int i = 0; i < array.Length; i++) - { - if (array.GetValue(i) == null) { array.SetValue(string.Empty, i); } - } - } - else if (type.IsClass && !type.IsAbstract) - { - foreach (var property in GetAccessableProperties(type)) - { - if (property.PropertyType == typeof(string)) - { - if (property.GetValue(obj) == null) { property.SetValue(obj, string.Empty); } - } - else - { - FixStringNullToEmpty(property.GetValue(obj)); - } - } + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: return true; + default: return false; } } } diff --git a/src/Corsinvest.AllenBradley.PLC.Api/libplctag.so b/src/Corsinvest.AllenBradley.PLC.Api/libplctag.so index 254ba04..ca19d4d 100755 Binary files a/src/Corsinvest.AllenBradley.PLC.Api/libplctag.so and b/src/Corsinvest.AllenBradley.PLC.Api/libplctag.so differ diff --git a/src/Corsinvest.AllenBradley.PLC.Api/plctag.dll b/src/Corsinvest.AllenBradley.PLC.Api/plctag.dll index 4d70960..63d1fae 100644 Binary files a/src/Corsinvest.AllenBradley.PLC.Api/plctag.dll and b/src/Corsinvest.AllenBradley.PLC.Api/plctag.dll differ