Skip to content
This repository was archived by the owner on Mar 15, 2021. It is now read-only.

EtherNetIP protocol support #182

Closed
NithyaElango wants to merge 31 commits into
vmware-archive:masterfrom
NithyaElango:EtherNet-IP
Closed

EtherNetIP protocol support #182
NithyaElango wants to merge 31 commits into
vmware-archive:masterfrom
NithyaElango:EtherNet-IP

Conversation

@NithyaElango

Copy link
Copy Markdown

Added EtherNetIP protocol support for liota, which uses cpppo library internally.

@pmasrani pmasrani left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the package which you have written as well. Without the package, nobody can try out this code.

Also, cpppo library needs to be added to requirements.txt

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Pep 8 convention for naming the module. https://www.python.org/dev/peps/pep-0008/#package-and-module-names
Not only for this module and class, but for all of your code.

log = logging.getLogger(__name__)


class EtherNetDeviceComms(DeviceComms):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be CipEtherentDeviceComms. Ethernet is a generic protocol.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EtherNet/IP (EtherNet Industrial Protocol) means CIP implemented over ethernet.
So I changed the class name to EtherNetIPDeviceComms which I thought would be more suitable.
Or should I make it CipEtherentDeviceComms?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://en.wikipedia.org/wiki/EtherNet/IP is a specific implementation of CIP. I think we should call this: CipEtherNetIpDeviceComms.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

import logging

from liota.device_comms.device_comms import DeviceComms
from liota.lib.transports.ENIP import EtherNetIP

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename ENIP

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed as EtherNetIP



if host is None:
log.error("Host can't be none")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove log statement. Once you raise an exception, it will automatically get logged.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the log statement.

data = self.client.read()
return data

def send(self, message):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we implement send() and receive()? Whatever functionality you have in write() has to be part of send() and read() has to be part of receive()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made these changes. Now, Send() contains write() and receive () contains read().

Comment thread liota/lib/transports/ENIP.py Outdated
assert self.conn.readable( timeout=1.0 ), "Failed to receive reply"
rpy = next( self.conn )
data = rpy['enip']['CIP']['send_data']['CPF']['item'][1]['unconnected_send']['request']['read_frag']['data'][0]
print(data)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove print, log if necessary

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed all print statements

Comment thread liota/lib/transports/ENIP.py Outdated
req = self.conn.read("Scada[0]")
assert self.conn.readable( timeout=1.0 ), "Failed to receive reply"
rpy = next( self.conn )
data = rpy['enip']['CIP']['send_data']['CPF']['item'][1]['unconnected_send']['request']['read_frag']['data'][0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are these magic strings coming from? Is it always the same? What if one of them is None?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to fetch the data from the response got from the server, which is in json format.
It is the same always.

Comment thread liota/lib/transports/ENIP.py Outdated


def shutdown( self ):
if not self.udp:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if it is not UDP?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not able to figure this out, could you please take a look at this - https://github.com/pjkundert/cpppo/blob/master/server/enip/client.py#L401

DeviceComms for EtherNetIP protocol
"""

def __init__(self, host=None, port=None, timeout=None, dialect=None, profiler=None, udp=False, broadcast=False, source_address=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the default value of host None, if we are immediately checking for it to be not none?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the localhost as default, if host is empty, as mentioned in https://github.com/pjkundert/cpppo/blob/master/server/enip/client.py#L300

def __init__(self, host=None, port=None, timeout=None, dialect=None, profiler=None, udp=False, broadcast=False, source_address=None):

self.host = host
self.port = port

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when port is none? where does it connect?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When port is None, I think the default port will be used. I want you to check this once - https://github.com/pjkundert/cpppo/blob/master/server/enip/client.py#L326
Also, it is mentioned if default port is not used, use 44818 - https://github.com/pjkundert/cpppo/blob/master/server/enip/client.py#L302

As of now I have changed the port as 44818 in the code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. Thanks for checking it out. If the underlying library accepts None, then yes we should accept None. Let us not hard-code it to 44818, as it is a non-standard port, and if library implementation changes the port, we will get an error.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@pmasrani pmasrani requested a review from KohliDev November 14, 2017 05:02
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a README file with instructions how to use this protocol.

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a simulation. Let us name it as CipSocketGraphite.py to indicate it is have Cip as DeviceComms, Socket as DCCComms and Graphite as DCC.

Also add the writer code as "simulator", so that someone can test this package. In Readme, also add the complete steps of how to run the simulator and load this package.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay




def clean_up(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disconnect the DeviceComms as well after stopping the metric collection.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay

self.config = read_user_config(config_path + '/sampleProp.conf')


ethernetIP_conn = CipEtherNetIpDeviceComms(host=self.config['EtherNetIP'],port=None,timeout=None, dialect=None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None values need not be specified here. So remove them.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, will remove them



def _connect(self):
self.client = EtherNetIP(self.host,self.port,self.timeout,self.dialect,self.profiler,self.udp,self.broadcast,self.source_address)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run autopep8 to correct the formatting.

@@ -0,0 +1,28 @@
import time

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright missing.

tag_type=tag_type)

except AssertionError:
print "Response timed out!! Tearing Connection and Reconnecting!!!!!"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you actually reconnecting anywhere??

elements = len (data)
tag_type = client.enip.DINT.tag_type
tag = "Scada"
req = conn.write( tag, elements=elements, data=data,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting? use autopep8

import logging

from liota.device_comms.device_comms import DeviceComms
from liota.lib.transports.EtherNetIP import EtherNetIP

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename EtherNetIP to CipEtherNetIp

if data is None:
raise TypeError("Data can't be none")
else:
self.client.send(tag,elements,data,tagType)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting.

Comment thread packages/sampleProp.conf
LivingRoomLightTopic = "home/living-room/light"

#### [EtherNet/IP] ####
EtherNetIP = "EtherNet/IP IP"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to CipEtherNetIp

Comment thread packages/sampleProp.conf
LivingRoomHumidityTopic = "home/living-room/humidity"
LivingRoomLightTopic = "home/living-room/light"

#### [EtherNet/IP] ####

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide better comment like CIP Etherent/IP Device Comms parameters

@pmasrani

Copy link
Copy Markdown

Closing this pull request, as this was incorrectly created and a new one (#187) is raised.

@pmasrani pmasrani closed this Nov 16, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants