CIP EthernetIP protocol implementation#187
Conversation
|
|
||
| Then, EtherNet/IP related parameters required in `send()` and `receive()` like tags, datatype etc., will be mentioned while starting the server. Please refer this [example](https://github.com/lucifercr07/liota/blob/can_bus/examples/canbus/simulated_canbus.py) which reads the data from the server and send it to DCC. | ||
|
|
||
| In SampleProp.conf, the EtherNetIP should be changed to the IP of the server. |
|
|
||
| ## Using EtherNetIP_DeviceComms | ||
|
|
||
| Initially run the writer program which keeps writing data to the server, check this [simulator]( ) for the code. |
| ## Using EtherNetIP_DeviceComms | ||
|
|
||
| Initially run the writer program which keeps writing data to the server, check this [simulator]( ) for the code. | ||
| Change the host to the IP of the machine where server is running in line number 6 in this [simulator]( ). |
| from cpppo.server.enip.getattr import attribute_operations | ||
| import json | ||
|
|
||
| HOST = "host" |
There was a problem hiding this comment.
I guess this needs to be changed every time someone wants to run the code. Instead, assume all 3 components (simulator, liota and server) running on the same machine. Use 127.0.0.1 in sampleProp.conf and here. In Readme, provide the instruction of where to change it, if every component is running on different machine.
| def receive(self): | ||
| with self.conn: | ||
| try: | ||
| request_ = self.conn.read("Scada[0]") |
There was a problem hiding this comment.
I think "Scada" is the name/tag of the queue. Correct me if i am wrong. It seems to be hard-coded between liota and simulator. Make this also a configurable parameter in sampleProp.conf like IP address.
There was a problem hiding this comment.
Yes, it is the tag. I will change it.
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class CIPEthernetIP: |
There was a problem hiding this comment.
CipEthernetIp instead of CIPEthernetIP.
| @@ -0,0 +1,90 @@ | |||
| # -*- coding: utf-8 -*- | |||
There was a problem hiding this comment.
package name convention? cip_socket_graphite? I think even directory name needs to be changed. Check that.
|
|
||
| self.config = read_user_config(config_path + '/sampleProp.conf') | ||
|
|
||
| self.ethernetIP_conn = CipEtherNetIpDeviceComms(host=self.config['CipEtherNetIp'], port, timeout, dialect, |
There was a problem hiding this comment.
I don't see a reason why we are declaring all these parameters. Just a simple call CipEtherNetIpDeviceComms(host=self.config['CipEtherNetIp']) should work. Isn't it?
There was a problem hiding this comment.
Yes that would work, I will change it.
| @@ -0,0 +1,22 @@ | |||
| # Using CIP EtherNet Industrial Protocol as Transport in LIOTA | |||
|
|
|||
| LIOTA offers CIP EtherNet Industrial protocol as transport at Device end via [EtherNetIP_DeviceComms](https://github.com/lucifercr07/liota/blob/can_bus/liota/device_comms/canbus_device_comms.py) | |||
There was a problem hiding this comment.
EtherNetIP_DeviceComms? Correct the name.
|
|
||
| ## Starting the server | ||
|
|
||
| To get started with the CIP protocol, a server must be started in any linux machine. Install cpppo in that machine using |
There was a problem hiding this comment.
Should we provide a link to cpppo library?
| #### [CIP EthernetIP Parameters] #### | ||
|
|
||
| CipEtherNetIp = "127.0.0.1" | ||
| Tag = "Scada[0]" |
There was a problem hiding this comment.
I think this should just be "Scada". The value from the tag is read at [0] position. That should be done by the program.
| req = conn.write( tag, elements=elements, data=data, | ||
| tag_type=tagType) | ||
|
|
||
| except socket.error as exc: |
There was a problem hiding this comment.
Now what happens when other errors are received? Program stops? Should we have a catch all exceptions block and print the Exception?
There was a problem hiding this comment.
I am not sure what has to be done here. Should I catch all exceptions?
There was a problem hiding this comment.
I checked the client file in cpppo library, shutdown is not required, closing the connection is sufficient. so changed accordingly.
| def clean_up(self): | ||
| for metric in self.metrics: | ||
| metric.stop_collecting() | ||
| self.ethernetIP_conn._disconnect() |
There was a problem hiding this comment.
Did you check the indent? Did you try unloading the package?
There was a problem hiding this comment.
I did unload it, when I see the program, I don't find this indentation mistake.
| ## Using cip_ethernet_ip_device_comms | ||
|
|
||
| Initially run the writer program which keeps writing data to the server, check this [simulator](https://github.com/NithyaElango/liota/blob/CipEthernetIP/tests/cipethernetip/cip_ethernet_ip_simulator.py) for the code. | ||
| Modify the variable "host" in this [simulator](https://github.com/NithyaElango/liota/blob/CipEthernetIP/tests/cipethernetip/cip_ethernet_ip_simulator.py) if the server is running in a different machine. If not, the server,simulator and liota program are running in the same machine, then nothing needs to be changed. |
There was a problem hiding this comment.
The variable name you have used is HOST, not "host".
| try: | ||
| self.conn.shutdown(socket.SHUT_WR) | ||
| except: | ||
| pass |
| response = next(self.conn) | ||
| data = response['enip']['CIP']['send_data']['CPF']['item'][1]['unconnected_send']['request']['read_frag']['data'][0] | ||
| except AssertionError as error: | ||
| log.error("Failed to receive reply") |
| except AssertionError as exc: | ||
| log.info("Response timed out!!") | ||
| except socket.error as exc: | ||
| log.error("Couldn't send command: %s" % (exc)) |
| name=ethernet_device_metric_name, | ||
| unit=None, | ||
| interval=5, | ||
| sampling_function=lambda: read_value(self.ethernetIP_conn,self.tag) |
|
|
||
| def connect(self): | ||
| with client.connector(host=self.host) as self.conn: | ||
| if(self.conn): |
There was a problem hiding this comment.
Do we need the if statement here? If we are inside with, it will print the log.
| @@ -0,0 +1,109 @@ | |||
| # -*- coding: utf-8 -*- | |||
There was a problem hiding this comment.
rename cip_device_comms.py
There was a problem hiding this comment.
CIP could be implemented over four different networks.
I have done it over ethernet, so It is known as EtherNet/IP, and thus me and Piyush named it as cip_ethernet_ip.
Is it necessary to rename?
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class CipEtherNetIpDeviceComms(DeviceComms): |
| def receive(self, tag, index): | ||
| data = self.client.receive(tag, index) | ||
| return data | ||
|
|
| self.broadcast = broadcast | ||
| self.source_address = source_address | ||
|
|
||
| def connect(self): |
There was a problem hiding this comment.
Is there no credentials support?
There was a problem hiding this comment.
No, there is no credentials support
| try: | ||
| req = self.conn.write(self.tag, elements=self.elements, data=self.data, | ||
| tag_type=self.tag_type) | ||
| except AssertionError as exc: |
There was a problem hiding this comment.
Reason for assertion error?
There was a problem hiding this comment.
In cpppo library, which is being used here, the write() function calls another function "unconnected_send()" - where there is an assert statement https://github.com/pjkundert/cpppo/blob/master/server/enip/client.py#L768 and that is the reason for the assertion error.
| @@ -0,0 +1,62 @@ | |||
| # -*- coding: utf-8 -*- | |||
There was a problem hiding this comment.
There should be a unit test case as well.
There was a problem hiding this comment.
I don't understand how this has to be done.
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # | ||
| # THE POSSIBILITY OF SUCH DAMAGE. # | ||
| # ----------------------------------------------------------------------------# | ||
|
|
There was a problem hiding this comment.
Can you add comments over here what are you trying to do in this particular test case?
| log.exception("Instance has no such attribute") | ||
| time.sleep(5) | ||
| i = i + 1 | ||
|
|
| from liota.device_comms.cip_ethernet_ip_device_comms import CipEtherNetIpDeviceComms | ||
| from liota.entities.devices.simulated_device import SimulatedDevice | ||
|
|
||
|
|
There was a problem hiding this comment.
Can you add comments over here to explain the flow?
| return value | ||
|
|
||
|
|
||
| class PackageClass(LiotaPackage): |
There was a problem hiding this comment.
There should also be a normal Liota example with the package example on the usage of this protocol as we haven't moved away or neither decided to discard the current existing examples.
There was a problem hiding this comment.
You want me to write an liota example program other than a liota package for the usage of this protocol? And it should be under liota/examples right?
There was a problem hiding this comment.
I wrote an example program for the same usage and placed it under liota/examples.
CIP protocol support for Liota