Skip to content

Commit

Permalink
Connection and error handling improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
dmroeder committed Mar 20, 2020
1 parent d2de73c commit 0c8a3de
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 64 deletions.
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
03/20/20
- Impoved lost connections and error handling, upped version to 0.6.3

02/28/20
- Fixed typo in lgxDevice.py
- Upped to version 0.6.2
Expand Down
2 changes: 1 addition & 1 deletion pylogix/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .lgxDevice import LGXDevice
from .eip import PLC
__version_info__ = (0, 6, 2)
__version_info__ = (0, 6, 3)
__version__ = '.'.join(str(x) for x in __version_info__)
117 changes: 57 additions & 60 deletions pylogix/eip.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Originally created by Burt Peterson
Updated and maintained by Dustin Roeder ([email protected])
Copyright 2019 Dustin Roeder
Copyright 2020 Dustin Roeder
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -26,7 +26,6 @@
from random import randrange
from struct import pack, unpack_from


class PLC:

def __init__(self, ip_address="", slot=0):
Expand Down Expand Up @@ -137,10 +136,7 @@ def GetTagList(self, allTags=True):
returns Response class (.TagName, .Value, .Status)
"""
tag_list = self._getTagList(allTags)
updated_list = self._getUDT(tag_list.Value)

return Response(None, updated_list, tag_list.Status)
return self._getTagList(allTags)

def GetProgramTagList(self, programName):
"""
Expand All @@ -149,6 +145,9 @@ def GetProgramTagList(self, programName):
returns Response class (.TagName, .Value, .Status)
"""
conn = self._connect()
if not conn[0]:
return Response(programName, None, conn[1])

# If ProgramNames is empty then _getTagList hasn't been called
if not self.ProgramNames:
Expand All @@ -163,7 +162,7 @@ def GetProgramTagList(self, programName):
program_tags = self._getUDT(program_tags.Value)
return Response(None, program_tags, status)
else:
return Response(None, None, 'Program not found, please check name!')
return Response(programName, None, 'Program not found, please check name!')

def GetProgramsList(self):
"""
Expand All @@ -173,6 +172,11 @@ def GetProgramsList(self):
returns Response class (.TagName, .Value, .Status)
"""

conn = self._connect()
if not conn[0]:
return Response(None, None, conn[1])

tags = ''
if not self.ProgramNames:
tags = self._getTagList(False)
Expand Down Expand Up @@ -217,12 +221,13 @@ def Close(self):

def _readTag(self, tag_name, elements, data_type):
"""
processes the read request
Processes the read request
"""
self.Offset = 0

if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return Response(tag_name, None, conn[1])

tag, base_tag, index = _parseTagName(tag_name, 0)
resp = self._initial_read(tag, base_tag, data_type)
Expand Down Expand Up @@ -266,8 +271,9 @@ def _writeTag(self, tag_name, value, data_type):
self.Offset = 0
write_data = []

if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return Response(tag_name, value, conn[1])

tag, base_tag, index = _parseTagName(tag_name, 0)
resp = self._initial_read(tag, base_tag, data_type)
Expand Down Expand Up @@ -329,8 +335,9 @@ def _multiRead(self, tags):
tag_count = len(tags)
self.Offset = 0

if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return [Response(None, None, conn[1])]

for tag in tags:
if isinstance(tag, (list, tuple)):
Expand Down Expand Up @@ -380,8 +387,9 @@ def _multiWrite(self, write_data):
tag_count = len(write_data)
self.Offset = 0

if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return [Response(None, value, conn[1])]

for wd in write_data:

Expand Down Expand Up @@ -426,16 +434,15 @@ def _multiWrite(self, write_data):
eip_header = self._buildEIPHeader(request)
status, ret_data = self._getBytes(eip_header)

#tags = [t[0] for t in stuff]

return self._multiWriteParser(write_data, ret_data)

def _getPLCTime(self, raw=False):
"""
Requests the PLC clock time
"""
if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return Response(None, None, conn[1])

AttributeService = 0x03
AttributeSize = 0x02
Expand Down Expand Up @@ -475,8 +482,9 @@ def _setPLCTime(self):
"""
Requests the PLC clock time
"""
if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return Response(None, None, conn[1])

AttributeService = 0x04
AttributeSize = 0x02
Expand Down Expand Up @@ -507,8 +515,9 @@ def _getTagList(self, allTags):
"""
Requests the controller tag list and returns a list of LgxTag type
"""
if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return Response(None, None, conn[1])

self.Offset = 0
tags = []
Expand Down Expand Up @@ -553,15 +562,17 @@ def _getTagList(self, allTags):
tags += self._extractTagPacket(ret_data, program_name)
else:
return Response(None, None, status)

return Response(None, tags, status)

updated_list = self._getUDT(tags)
return Response(None, updated_list, status)

def _getProgramTagList(self, programName):
"""
Requests tag list for a specific program and returns a list of LgxTag type
"""
if not self._connect():
return None
conn = self._connect()
if not conn[0]:
return Response(None, None, conn[1])

self.Offset = 0
tags = []
Expand All @@ -584,6 +595,7 @@ def _getProgramTagList(self, programName):
else:
return Response(None, None, status)

updated_list = self._getUDT(tags)
return Response(None, tags, status)

def _getUDT(self, tag_list):
Expand Down Expand Up @@ -629,10 +641,6 @@ def _getTemplateAttribute(self, instance):
"""
Get the attributes of a UDT
"""

if not self._connect():
return None

request = self._buildTemplateAttributes(instance)
eip_header = self._buildEIPHeader(request)
status, ret_data = self._getBytes(eip_header)
Expand All @@ -642,9 +650,6 @@ def _getTemplate(self, instance, dataLen):
"""
Get the members of a UDT so we can get it
"""
if not self._connect():
return None

request = self._readTemplateService(instance, dataLen)
eip_header = self._buildEIPHeader(request)
status, ret_data = self._getBytes(eip_header)
Expand Down Expand Up @@ -752,8 +757,9 @@ def _getModuleProperties(self, slot):
Request the properties of a module in a particular
slot. Returns LgxDevice
"""
if not self._connect(False):
return None
conn = self._connect(False)
if not conn[0]:
return Response(tag_name, value, conn[1])

AttributeService = 0x01
AttributeSize = 0x02
Expand Down Expand Up @@ -795,8 +801,9 @@ def _getDeviceProperties(self):
Request the properties of a device at the
specified IP address. Returns LgxDevice
"""
if not self._connect(False):
return None
conn = self._connect(False)
if not conn[0]:
return Response(None, LGXDevice(), conn[1])

AttributeService = 0x01
AttributeSize = 0x02
Expand Down Expand Up @@ -837,21 +844,21 @@ def _connect(self, connected=True):
# connection type changed, need to close so we can reconnect
self._closeConnection()
else:
return True
return (True, 'Success')

# Make sure the connection size is correct
if not 500 <= self.ConnectionSize <= 4000:
raise ValueError("ConnectionSize must be an integer between 500 and 4000")
return (False, 'ConnectionSize must be an integer between 500 and 4000')

try:
self.Socket = socket.socket()
self.Socket.settimeout(5.0)
self.Socket.connect((self.IPAddress, self.Port))
except(socket.error):
except socket.error as e:
self.SocketConnected = False
self.SequenceCounter = 1
self.Socket.close()
raise
return (False, e)

self.Socket.send(self._buildRegisterSession())
ret_data = self.recv_data()
Expand All @@ -860,28 +867,30 @@ def _connect(self, connected=True):
self._registered = True
else:
self.SocketConnected = False
raise Exception("Failed to register session")
return (False, 'Register session failed')

if connected:
self.Socket.send(self._buildForwardOpenPacket())
ret_data = self.recv_data()
try:
ret_data = self.recv_data()
except socket.timeout as e:
return (False, e)
sts = unpack_from('<b', ret_data, 42)[0]
if not sts:
self.OTNetworkConnectionID = unpack_from('<I', ret_data, 44)[0]
self._connected = True
else:
self.SocketConnected = False
raise Exception("Forward Open Failed")
return (False, 'Forward open failed')
self.SocketConnected = True
return self.SocketConnected
return (self.SocketConnected, 'Success')

def _closeConnection(self):
"""
Close the connection to the PLC (forward close, unregister session)
"""
self.SocketConnected = False


try:
if self._registered:
close_packet = self._buildForwardClosePacket()
Expand Down Expand Up @@ -1692,9 +1701,6 @@ def _multiWriteParser(self, write_data, data):

return reply




def _buildListIdentity(self):
"""
Build the list identity request for discovering Ethernet I/P
Expand Down Expand Up @@ -1770,7 +1776,6 @@ def _makeString(self, string):
work.append(0x00)
return work


def _getBitOfWord(tag, value):
"""
Takes a tag name, gets the bit from the end of
Expand All @@ -1793,7 +1798,6 @@ def _getBitOfWord(tag, value):
pass
return returnValue


def _getWordCount(start, length, bits):
"""
Get the number of words that the requested
Expand All @@ -1807,7 +1811,6 @@ def _getWordCount(start, length, bits):
totalWords = (newEnd-1) / bits
return totalWords + 1


def _parseTagName(tag, offset):
"""
parse the packet to get the base tag name
Expand Down Expand Up @@ -1837,7 +1840,6 @@ def _parseTagName(tag, offset):
except Exception:
return tag, bt, 0


def BitofWord(tag):
"""
Test if the user is trying to write to a bit of a word
Expand All @@ -1849,7 +1851,6 @@ def BitofWord(tag):
else:
return False


def BitValue(value, bitno):
"""
Returns the specific bit of a words value
Expand All @@ -1860,7 +1861,6 @@ def BitValue(value, bitno):
else:
return False


def _parseIdentityResponse(data):
# we're going to take the packet and parse all
# the data that is in it.
Expand Down Expand Up @@ -1893,7 +1893,6 @@ def _parseIdentityResponse(data):

return resp


def parseLgxTag(packet, programName):

t = LgxTag()
Expand Down Expand Up @@ -1973,7 +1972,6 @@ def __str__(self):

return '{} {} {}'.format(self.TagName, self.Value, self.Status)


def get_error_code(status):
"""
Get the CIP error code string, if the status is a string it will be returned
Expand All @@ -1994,7 +1992,6 @@ def get_error_code(status):
err = 'Unknown error {}'.format(status)
return err


cip_error_codes = {0x00: 'Success',
0x01: 'Connection failure',
0x02: 'Resource unavailable',
Expand Down
Loading

0 comments on commit 0c8a3de

Please sign in to comment.