mikeav-soft / LogixTool

Tool for working with Allen-Bradley PLC (ControlLogix, CompactLogix) via EthernetIP connection
GNU General Public License v3.0
24 stars 11 forks source link

Simple Tag Reading / Writing #3

Closed csharpsithlord closed 9 months ago

csharpsithlord commented 12 months ago

How could I create, say a new winforms app, and add a controller then read / write data from / to tags? Using just the EIP library.

mikeav-soft commented 12 months ago

Good afternoon I understand that I haven’t described the library enough, but I’ll try to describe the idea step by step.

  1. Add an EIP project to your project, change the platform version in the EIP project if necessary.
  2. In order to automatically receive data from the controller, create a collection of LogixTagHandler tag handlers in the constructor by specifying their names, the required minimum update time and the method for obtaining data. LogixTagHandler is a container with received values, statuses and parameters for receiving or writing data.
  3. Create an object of the LogixDevice class to work with the device. Inside it there are many methods for interacting with the controller; they can be studied/added separately. Each type of tag has its own characteristics that complicate the process of obtaining data.
  4. In order to automate the retrieval of data in a separate background process, there is a LogixTask class.

Example:

        // Creating of Tag Handlers.
        List<LogixTagHandler> logixTagHandlers = new List<LogixTagHandler>();

        LogixTagHandler logixTagHandler1 = new LogixTagHandler("plc_tag_1");
        logixTagHandler1.ReadMethod = TagReadMethod.Simple;     // Is simple method from AB Manual.
        //logixTagHandler1.ReadMethod = TagReadMethod.Fragmet;  // Is simple method from AB Manual for Array reading.
        //logixTagHandler1.ReadMethod = TagReadMethod.Table;    // Is method use by AB Devices as HMI, is not described in manuals, was fully 
                                                                // studied by wireshark. Advanteges - high update rate for big count of tags.
        logixTagHandler1.ReadUpdateRate = 500;
        logixTagHandlers.Add(logixTagHandler1);

        LogixTagHandler logixTagHandler2 = new LogixTagHandler("plc_tag_2");
        logixTagHandler2.ReadMethod = TagReadMethod.Simple;
        logixTagHandler2.ReadUpdateRate = 1000;
        logixTagHandlers.Add(logixTagHandler2);

        // Creating of Device.
        LogixDevice logixDevice1 = new LogixDevice();
        logixDevice1.Name = "My_PLC_1";
        logixDevice1.Family = DeviceFamily.ControlLogix; // or  DeviceFamily.Micro800
        logixDevice1.Address = IPAddress.Parse( "192.168.1.10");

        // Creating of Task.
        LogixTask logixTask = new LogixTask(logixDevice1 );
        //logixTask.Message += LogixTask_Message;                           // Your events for Log Messages.
        //logixTask.TagsValueWasChanged += LogixTask_TagsValueWasChanged;   // Your events for New readed tag values (if changed).
        //logixTask.TagsValueWasReaded += LogixTask_TagsValueWasReaded;     // Your events for New readed tag values (if updated).

        logixTask.Begin(logixTagHandlers);

        // Reading status of Task.
        TaskProcessState taskProcessState = logixTask.ProcessState; // General Status of Task
        ServerState serverState = logixTask.ServerState;            // Status/step of process.

        // Reading status and values of Tag Handlers.
        long? actualUpdateRate1 = logixTagHandler1.ReadValue.Report.ActualUpdateRate;
        bool? operationResult = logixTagHandler1.ReadValue.Report.IsSuccessful;
        List<byte[]> rawBytes = logixTagHandler1.ReadValue.Report.Data; // List<> - is array containers, byte[] - is Item bytes of array.
                                                                        // For simple tag (int) List<> count == 1, byte[] Length == 4.
csharpsithlord commented 12 months ago

Thank you.

mikeav-soft commented 12 months ago

For writing also

        logixTagHandler1.WriteMethod = TagWriteMethod.Simple;
        logixTagHandler1.WriteValue.RequestedData = /* value for write */;
        logixTagHandler1.WriteUpdateRate = 0;   // Set 0 for writing once, if logixTagHandler1.WriteEnable == true
                                                // after writing logixTagHandler1.WriteEnable will reset.
                                                // For cycling Writing - set your update rate.
        logixTagHandler1.WriteEnable = true;    //
csharpsithlord commented 11 months ago

Is there a way to read / write without creating a LogixTask and adding LogixTagHandler to it? A way to directly use the LogixDevice ReadTag, ReadTags, WriteTag, WriteTags to perform a single read or write command?

mikeav-soft commented 11 months ago

Please try this. In my library I not used Exception conception because it was many time ago.

        // Creating of Tag Handlers.
        List<LogixTag> logixTags = new List<LogixTag>();

        LogixTag logixTag1 = new LogixTag();
        logixTag1.SymbolicEPath = new EIP.EthernetIP.EPath();
        logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("plc_tag_1")); // Your Controller Tag!
        logixTags.Add(logixTag1);

        LogixTag logixTag2 = new LogixTag();
        logixTag2.SymbolicEPath = new EIP.EthernetIP.EPath();
        logixTag2.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("plc_tag_2")); // Your Controller Tag!
        logixTags.Add(logixTag2);

        // Creating of Device.
        LogixDevice logixDevice1 = new LogixDevice();
        logixDevice1.Name = "My_PLC_1";
        logixDevice1.Family = DeviceFamily.ControlLogix; // or  DeviceFamily.Micro800
        logixDevice1.Address = IPAddress.Parse("192.168.1.10");

        //logixDevice1.Message += LogixDevice1_Message; // Your events for Log Messages.

        logixDevice1.Connect();
        logixDevice1.RegisterSession();
        logixDevice1.ForwardOpen();

        // **********************************************************************************************
        logixDevice1.ReadTag(logixTag1);    // Simple reading Tag

        bool? resultOfReading = logixTag1.ReadValue.Report.IsSuccessful;    // True -> is Success.
        List<byte[]> tagValue = logixTag1.ReadValue.Report.Data;            // Raw Bytes.

        // option : convert value to string;
        logixTag1.Radix = TagValueRadix.Decimal;
        string valueString1 = logixTag1.GetReadedValueText();               // Value as string.

        // **********************************************************************************************
        logixDevice1.ReadTags(logixTags);   // Simple reading Tags.

        // **********************************************************************************************
        logixTag1.WriteValue.RequestedData = new List<byte[]>() { BitConverter.GetBytes((int)1) };      // Writing Int32 tag value (you shure about type).
        logixDevice1.WriteTag(logixTag1);    // Simple write Tag
        bool? resultOfWriring = logixTag1.WriteValue.Report.IsSuccessful;

        // **********************************************************************************************
        logixDevice1.WriteTags(logixTags);   // Simple writing Tags.
csharpsithlord commented 11 months ago

Thank you very much. This is a great implementation of the protocol.

csharpsithlord commented 11 months ago

What has to be different to read a UDT tag like "HexConverters[0].Decimal_Value" or to read String data types?

mikeav-soft commented 11 months ago

Do I understand correctly that the question is how to set the path for UDT or String and how to get value for it?

csharpsithlord commented 11 months ago

That is correct. I tried the previously mentioned method and the data returns null.

mikeav-soft commented 11 months ago

Ok! I see! For UDT try this:

    logixTag1.SymbolicEPath = new EIP.EthernetIP.EPath();
    logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("HexConverters")); // Your UDT Tag !
    logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment(EIP.EthernetIP.EPathSegmentHeader.Local_MemberID, 0););
    logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("Decimal_Value"));

For String I will update little bit later.

csharpsithlord commented 9 months ago

Just checking back to see if you found anything for string type? Reading and writing.

mikeav-soft commented 9 months ago

Sorry for the long delay. To me, a string is a System UDT structure. Either way you will get a few bytes (ASCII encoding). I suggest reading it using the ReadTagFragment method. One note. I not checked is code, because I don't have any Allen Bradley device (PLC) now.

        LogixDevice logixDevice1 = new LogixDevice();
        logixDevice1.Address = IPAddress.Parse("192.168.1.1");
        logixDevice1.Name = "My_PLC_1";
        logixDevice1.Family = DeviceFamily.ControlLogix;

        //logixDevice1.Message += LogixDevice1_Message; // Your events for Log Messages.

        logixDevice1.Connect();
        logixDevice1.RegisterSession();
        logixDevice1.ForwardOpen();

        /* Reading of Tag 'MyUDT.MyString', where MyString - is String (for eaxmple Length = 8 bytes) */

        LogixTag logixTag1 = new LogixTag();
        logixTag1.Type.ArrayDimension.Dim0 = 8;     // Length of reading is 8 chars (Single dimension of array).

        logixTag1.SymbolicEPath = new EIP.EthernetIP.EPath();
        logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("MyUDT")); // Your UDT Tag !
        logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("MyString"));  // Your String member.
        logixTag1.SymbolicEPath.Segments.Add(new EIP.EthernetIP.EPathSegment("DATA"));  // Is Array with bytes as ASCII chars.

        logixDevice1.ReadTagFragment(logixTag1);

        if (logixTag1.ReadValue.Report.IsSuccessful == true)
        {
            var data = logixTag1.ReadValue.Report.Data;

            // Convert to string.
            if (data != null)
            {
                byte[] stringBytes = data.Select(x => x.First()).ToArray();
                string readedString = Encoding.ASCII.GetString(stringBytes);
            }

        }