Skip to content

Commit

Permalink
Added fragmented write for arrays larger than a packet
Browse files Browse the repository at this point in the history
  • Loading branch information
dmroeder committed Dec 17, 2019
1 parent c5e6840 commit ec37627
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 12 deletions.
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
12/17/19
- Added fragmented write for arrays larger than a packet

10/29/19
- Moved examples from pylogix to root directory
- Added comments about how data is returned
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, 4, 12)
__version_info__ = (0, 4, 13)
__version__ = '.'.join(str(x) for x in __version_info__)
92 changes: 81 additions & 11 deletions pylogix/eip.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def _writeTag(self, tag, value, dt):
"""
self.Offset = 0
writeData = []

if not self._connect():
return None

Expand All @@ -274,7 +274,8 @@ def _writeTag(self, tag, value, dt):
else:
elements = 1
value = [value]


# format the values
for v in value:
if dataType == 202 or dataType == 203:
writeData.append(float(v))
Expand All @@ -283,19 +284,37 @@ def _writeTag(self, tag, value, dt):
else:
writeData.append(int(v))

# write a bit of a word, boolean array or everything else
# save the number of values we are writing
element_count = len(writeData)

# convert writeData to packet sized lists
writeData = self._convert_write_data(b, dataType, writeData)

# parse the tag ioi
if BitofWord(tag):
tagData = self._buildTagIOI(tag, isBoolArray=False)
writeRequest = self._addWriteBitIOI(tag, tagData, writeData, dataType)
ioi = self._buildTagIOI(tag, isBoolArray=False)
elif dataType == 211:
tagData = self._buildTagIOI(tag, isBoolArray=True)
writeRequest = self._addWriteBitIOI(tag, tagData, writeData, dataType)
ioi = self._buildTagIOI(tag, isBoolArray=True)
else:
tagData = self._buildTagIOI(tag, isBoolArray=False)
writeRequest = self._addWriteIOI(tagData, writeData, dataType)
ioi = self._buildTagIOI(tag, isBoolArray=False)

# handle sending the write data
if len(writeData) > 1:
# write requires multiple packets
for w in writeData:
writeRequest = self._write_fragment_request(element_count, ioi, w, dataType)
eipHeader = self._buildEIPHeader(writeRequest)
status, retData = self._getBytes(eipHeader)
self.Offset += len(w)*self.CIPTypes[dataType][0]
else:
# write fits in one packet
if BitofWord(tag) or dataType == 211:
writeRequest = self._addWriteBitIOI(tag, ioi, writeData[0], dataType)
else:
writeRequest = self._addWriteIOI(ioi, writeData[0], dataType)

eipHeader = self._buildEIPHeader(writeRequest)
status, retData = self._getBytes(eipHeader)
eipHeader = self._buildEIPHeader(writeRequest)
status, retData = self._getBytes(eipHeader)

return Response(tag, value, status)

Expand Down Expand Up @@ -1180,6 +1199,35 @@ def _addWriteBitIOI(self, tag, tagIOI, writeData, dataType):

return writeIOI

def _write_fragment_request(self, count, ioi, write_data, data_type):
"""
Add the fragmented write command stuff to the tagIOI
"""
element_size = self.CIPTypes[data_type][0]
data_len = len(write_data)
path_size = int(len(ioi)/2)
service = 0x53
request = pack('<BB', service, path_size)
request += ioi

if data_type == 160:
request += pack('<BB', data_type, 0x02)
request += pack('<H', self.StructIdentifier)
else:
request += pack('<H', data_type)
request += pack('<H', count)
request += pack('<I', self.Offset)

for v in write_data:
try:
for i in range(len(v)):
el = v[i]
request += pack(self.CIPTypes[data_type][2], el)
except Exception:
request += pack(self.CIPTypes[data_type][2], v)

return request

def _buildEIPHeader(self, tagIOI):
"""
The EIP Header contains the tagIOI and the
Expand Down Expand Up @@ -1430,6 +1478,28 @@ def _initial_read(self, tag, baseTag, dt):
else:
return tag, None, status

def _convert_write_data(self, tag, data_type, write_values):
'''
In order to handle write requests that are larger than a single
packet, we'll break up the values to write into multiple lists
of values. The size of each list will be calculated based on the
connection size, length of the tag name and the data type.
'''
# packet header is always 110 bytes
packet_overhead = 110
# calculate number of bytes tag name will occupy
tag_length = len(tag) + len(tag) % 2
# calculate the available space (in bytes) for the write values
space_for_payload = self.ConnectionSize - packet_overhead - tag_length

# calculate how many bytes per value are required
bytes_per_value = self.CIPTypes[data_type][0]
# calculate the limit for values in each request
limit = int(space_for_payload / bytes_per_value)
# split the list up into multiple smaller lists
chunks = [write_values[x:x+limit] for x in range(0, len(write_values), limit)]
return chunks

def _wordsToBits(self, tag, value, count=0):
"""
Convert words to a list of true/false
Expand Down

0 comments on commit ec37627

Please sign in to comment.