forked from dmroeder/pylogix
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Connection and error handling improvements
- Loading branch information
Showing
4 changed files
with
64 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -26,7 +26,6 @@ | |
from random import randrange | ||
from struct import pack, unpack_from | ||
|
||
|
||
class PLC: | ||
|
||
def __init__(self, ip_address="", slot=0): | ||
|
@@ -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): | ||
""" | ||
|
@@ -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: | ||
|
@@ -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): | ||
""" | ||
|
@@ -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) | ||
|
@@ -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) | ||
|
@@ -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) | ||
|
@@ -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)): | ||
|
@@ -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: | ||
|
||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 = [] | ||
|
@@ -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 = [] | ||
|
@@ -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): | ||
|
@@ -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) | ||
|
@@ -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) | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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() | ||
|
@@ -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() | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -1793,7 +1798,6 @@ def _getBitOfWord(tag, value): | |
pass | ||
return returnValue | ||
|
||
|
||
def _getWordCount(start, length, bits): | ||
""" | ||
Get the number of words that the requested | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -1849,7 +1851,6 @@ def BitofWord(tag): | |
else: | ||
return False | ||
|
||
|
||
def BitValue(value, bitno): | ||
""" | ||
Returns the specific bit of a words value | ||
|
@@ -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. | ||
|
@@ -1893,7 +1893,6 @@ def _parseIdentityResponse(data): | |
|
||
return resp | ||
|
||
|
||
def parseLgxTag(packet, programName): | ||
|
||
t = LgxTag() | ||
|
@@ -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 | ||
|
@@ -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', | ||
|
Oops, something went wrong.