nimbuscontrols / EIPScanner

Free implementation of EtherNet/IP in C++
https://eipscanner.readthedocs.io/en/latest/
MIT License
233 stars 93 forks source link

Implicit Messaging - Yaskawa MP3300iec #30

Closed Eske closed 4 years ago

Eske commented 4 years ago

Hello,

I am new to Ethernet/IP and I am trying to use this library to create an Ethernet/IP Scanner to talk to a Yaskawa MP3300iec which I setup to be an Ethernet/IP Adapter.

I first tested the Discovery Manager example and was able to confirm that the Identity Object had the correct vendor ID for Yaskawa (0x2C). Now I am trying to use the Implicit Messaging example. I believe I need to use implicit messaging to talk to the adapter as it has input and output assemblies. However, I am running into an error when using the example.

Here is the output that is being displayed from the logger:

[DEBUG] Opened socket fd=3
[DEBUG] Connecting to 192.168.1.2:44818
[INFO] Registered session 52
[INFO] Send request: service=0x54 epath=[classId=6 objectId=1]
[ERROR] Message Router error=0x4 additional statuses [0x0]
[WARNING] Attempt to close an already closed connection
[INFO] Unregistered session 52
[DEBUG] Close socket fd=3

According to GeneralStatusCodes.h, the General Status Code error 0x04 is a Path Segment Error. (https://eipscanner.readthedocs.io/en/latest/api/program_listing_file_src_cip_GeneralStatusCodes.h.html?highlight=0x04#program-listing-for-file-generalstatuscodes-h)

I'll attach the connectionPath below with the rest of the example I've modified. However, I'd love to learn more about how the connectionPath should be formatted so I can understand why my path is wrong.

    // Implicit Messaging Example
    ConnectionManager connectionManager;
    ConnectionParameters parameters;

    parameters.connectionPath = {0x20, 0x04, 0x2C, 101, 0x2C, 111};  // output Assm101, input Assm111
    parameters.o2tRealTimeFormat = true;
    parameters.originatorVendorId = 342;
    parameters.originatorSerialNumber = 32423;
    parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P;
    parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
    parameters.t2oNetworkConnectionParams |= 128; //size of Assm111 = 128 bytes
    parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P;
    parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
    parameters.o2tNetworkConnectionParams |= 128; //size of Assm101 = 128 bytes
    parameters.originatorSerialNumber = 0x12345;
    parameters.o2tRPI = 1000000;
    parameters.t2oRPI = 1000000;
    parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1;

    auto io = connectionManager.forwardOpen(si, parameters);

    if (auto ptr = io.lock()) {
            ptr->setDataToSend(std::vector<uint8_t>(128));

Thanks for your time, Joseph

jadamroth commented 4 years ago

Hi Joseph,

Thanks for using the library.

We haven't done a lot of testing with Implicit messaging since our app uses mostly explicit messaging.

A few things I'd like to know first before providing more information:

  1. Are you controlling the EtherNet/IP adapter in real time or just reading/writing values in a given poll rate? I don't know what your main goal is, but please look at the parameter object, and it will be much easier to implement - https://github.com/nimbuscontrols/EIPScanner/blob/master/examples/ParameterObjectExample.cpp
  2. If you've determined that you definitely need Implicit Messaging, then you'll first need to look at the EDS file from your adapter to figure out the path. You can download it with this example - https://github.com/nimbuscontrols/EIPScanner/blob/master/examples/FileObjectExample.cpp
Eske commented 4 years ago

Thanks for the response.

The goal is to control the EtherNet/IP adapter with several boolean values used as both input and output variables. In our case, the adapter is a PLC which controls servos and motors. Because of this, how quickly the variables can be passed around is the most important factor. However, if the poll rate could be around 50 ms that would also probably be acceptable.

I was able to get the EDS file off of their website. I'll attach it below in case you are interested. I had to change the file extension from .eds to .txt in order to attach it to this post. From what I can understand, I modified the ExplicitMessaging example to fit the path provided, but I am still getting the path segment error.

    // Implicit Messaging
    ConnectionManager connectionManager;
    ConnectionParameters parameters;

    parameters.connectionPath = {0x20, 0x04, 0x2C, 0x6F, 0x2C, 0x65};  // input Assm111, output Assm101
    parameters.o2tRealTimeFormat = true;
    parameters.originatorVendorId = 342;
    parameters.originatorSerialNumber = 32423;
    parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::MULTICAST;
    parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
    parameters.t2oNetworkConnectionParams |= 128; //size of Assm111 = 128
    parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::FIXED;
    parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P;
    parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
    parameters.o2tNetworkConnectionParams |= 128; //size of Assm101 = 128
    parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::FIXED;
    parameters.originatorSerialNumber = 0x12345;
    parameters.o2tRPI = 1000000;
    parameters.t2oRPI = 1000000;
    parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1;

    auto io = connectionManager.forwardOpen(si, parameters);

I'm all for going down an easier route if you would rather me try to implement the parameter object route. Unfortunately, that example is also giving me a path segment error on the first sendRequest.

Thank you again, Joseph

MP3300iec_EDS.txt

Eske commented 4 years ago

Just wanted to give an update,

Using Ethernet IP Explorer as a scanner (https://sourceforge.net/projects/enipexplorer/), I was able to do both an implicit message and a GetAttributeSingle message and receive the expected response from my adapter using the AssemblyObject. I captured the traffic with Wireshark and will be examining the packets to see where I'm going wrong when using the EIPScanner. I'll attach the two captures below of the successful messages using Ethernet IP Explorer.

I'll examine what I'm doing wrong and hopefully be able to find a solution, Joseph

(change the file extension from .txt to .pcapng) WireShark_GetAttributeSingle.txt

WireShark_ImplicitMessage.txt

Eske commented 4 years ago

Hi again,

The issue for the Parameter Object example turned out to be that the Class, Instance, and Attribute Segment were 16-bits long. Once I changed cip/EPath.h and cip/EPath.cpp to handle 8-bits, I was able to successfully execute a GetAttributeSingle command. Does EIPScanner have a way to change if I want to use 8-bits instead of 16-bits for those segments without having to modify the source code like I did?

Thanks! Joseph

jadamroth commented 4 years ago

If I understand correctly, Class, Instance, and Attribute should be 16-bits long. For example, we're connecting a powerflex 525 VFD, and the instanceID goes all the way up to 731, so an 8-bit instanceID would not work here

Eske commented 4 years ago

I see, I think I misspoke. My apologies. So in cip/EPath.cpp you have the enum EPathSegmentTypes

        enum class EPathSegmentTypes : CipUsint  {
                CLASS_8_BITS = 0x20,
                CLASS_16_BITS = 0x21,
                INSTANCE_8_BITS = 0x24,
                INSTANCE_16_BITS = 0x25,
                ATTRIBUTE_8_BITS = 0x30,
                ATTRIBUTE_16_BITS = 0x31,
        };

For your adapter, you need to use the 16-bit Segment Types. However, the adapter I'm talking to seems to only support the 8-bit Segment Types, as I get the Invalid Path Segment error when using the 16-bit Segment type. I'm guessing it's an issue with my adapter then because ideally it would support both Segment Types yes?

Joseph

jadamroth commented 4 years ago

For explicit messaging, you should be able to use a simple example like this and replace EPath(0x01, 1, 1) with classID, instanceID, attributeID.

If you're retrieving parameter attributes, you should be able to get them directly here in this example. If you're not sure which parameter is which, you can also scan parameters in that example (typically cip ParameterObject is classID 15).

However for your requirements, it does seem that you'll need implicit messaging if you're controlling at 50ms poll rates.

Unfortunately, I don't have much time right now to look at the wireshark dumps and eds in detail. I will try to review when I can, but please keep me updated with progress.

Another initiative of this repo is to document all sensors, drives, and end-devices that we come across. We'd love for you to contribute your code you used to connect in an example or create custom classes for the devices you encounter here

Eske commented 4 years ago

Thanks for the response.

After talking with the engineers on the machine, I learned that they are planning on handle the motion code through the PLC and actually just want the Ethernet/IP to control variables to start, stop, and monitor faults on the machine. As such, I can poll at a much slower rate than I originally anticipated. Sorry about that!

I don't mind pushing my example I used at all! Give me a few days to push my code used to connect to the mp3300iec. I'll post an update here when I've pushed it. Feel free to give me pointers or have me change things so they better align with how you want the library to look like.

Thanks again for your help and for this wonderful library.

Joseph

jadamroth commented 4 years ago

I'm not sure of the specifics of your device but some drives are primarily controlled by implicit messaging regardless of poll rate.

For monitoring faults, often times there's a fault object you can use with explicit messaging. In the Powerflex family, it's classID = 0x97. These are usually in the parameter object too, but the fault object provides more detail.

For some drives, such as the Powerflex, there's a Register Object (0x07) which appears it can provide some control/fault features using explicit messaging. I have not personally tested this myself

Eske commented 4 years ago

Thanks for that information.

I've been pretty happy with my results reading and writing to the Assembly Object of the Yaskawa device. Watching the packets with wireshark, I can see I can perform a read and write operation within 800 microseconds, which is plenty fast for me.

I have created a commit with my changes and example provided. Would you like me to just push my commit to the master branch?

Joseph

(WireShark -- change file extension .txt to .pcapng) WireShark_YaskawaExample.txt

jadamroth commented 4 years ago

Please create a new branch, and we'll merge into master from a pull request.

Thank you and glad to hear that it's working well

Eske commented 4 years ago

Hi again,

I am getting a permission Issue when trying to push to a new branch on the repository:

git push --set-upstream origin yaskawa_example 
ERROR: Permission to nimbuscontrols/EIPScanner.git denied to Eske.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Do I need to be a collaborator in order to push? Or am I doing something wrong?

Thanks,

Joseph

jadamroth commented 4 years ago

Try forking the branch and pushing changes there. Then create a pull request from the forked branch.

However, if you think you'll be contributing code to the library semi-frequently, then I can add you as a collaborator.

Eske commented 4 years ago

That worked for me, thanks!

Created a pull request: #33

jadamroth commented 4 years ago

Thanks for the contribution @Eske

I'm going to close this issue for now. We can reopen if anything changes