Skip to content

Commit a14dc5b

Browse files
committed
Migrate out the remaining offset so result data can be trimmed
1 parent 92b7e5b commit a14dc5b

File tree

1 file changed

+23
-18
lines changed

1 file changed

+23
-18
lines changed

server/enip/logix.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,16 @@ def reply_elements( self, attribute, data, context ):
221221
# data will be trimmed elsewhere, according to the actual byte offset requested in the Read
222222
# Tag Fragmented request.
223223
begadvance = off // siz # Rounds down to the start of the element at offset
224+
offremains = off - begadvance * siz # off is how many bytes into beg element?
224225
log.info( "index: {index!r} beg: {beg}, cnt: {cnt}, elm: {elm}; endactual: {endactual}, begadvance: {begadvance}".format(
225226
index=index, beg=beg, cnt=cnt, elm=elm, endactual=endactual, begadvance=begadvance ))
226227
beg += begadvance
227228
if data.service in (self.RD_TAG_RPY, self.RD_FRG_RPY):
228-
# Return at least enough elements to satisfy max_size, beginning at offset 'off' We have
229-
# a 'beg' Element that contains the first byte at offset 'off'; compute the endmax that
230-
# contains the last byte at offset off+max_siz-1. The data may specify the (remaining)
231-
# .max_size payload available.
232-
off_rem = off - begadvance * siz # off is how many bytes into beg element?
233-
endadvance = max(( off_rem + max_size + siz - 1 ) // siz, 1 ) # rounds up
229+
# Return at least enough elements to satisfy max_size, beginning at offset 'off'. We
230+
# have a 'beg' Element that contains the first byte at offset 'off'; compute the endmax
231+
# that contains the last byte at offset off+max_siz-1. The data may specify the
232+
# (remaining) .max_size payload available.
233+
endadvance = max(( offremains + max_size + siz - 1 ) // siz, 1 ) # rounds up
234234
endmax = beg + endadvance
235235
else:
236236
endadvance = len( data[context].data )
@@ -239,13 +239,13 @@ def reply_elements( self, attribute, data, context ):
239239
"Attribute %s capacity exceeded; writing %d elements beginning at index %d" % (
240240
attribute, len( data[context].data ), beg )
241241
end = min( endactual, endmax )
242-
log.debug( "offset: {off:6d} siz: {siz:3d}, beg: {beg:3d}, end: {end:3d}, endmax: {endmax:3d}".format(
243-
off=off, siz=siz, beg=beg, end=end, endmax=endmax ))
242+
log.info( "offset: {off:6d} siz: {siz:3d}, beg: {beg:3d}, end: {end:3d}, endmax: {endmax:3d}, offremains: {offremains}".format(
243+
off=off, siz=siz, beg=beg, end=end, endmax=endmax, offremains=offremains ))
244244
assert 0 <= beg < cnt, \
245245
"Attribute %r initial element invalid: %r" % ( attribute, (beg, end) )
246246
assert beg < end, \
247247
"Attribute %r ending element before beginning: %r" % ( attribute, (beg, end) )
248-
return (beg,end,endactual)
248+
return (beg,end,endactual,offremains,max_size)
249249

250250
def request( self, data, addr=None ):
251251
"""Any exception should result in a reply being generated with a non-zero status."""
@@ -396,14 +396,14 @@ def request( self, data, addr=None ):
396396
# endactual. If a .offset (and optionally .max_size) is provided, these must be used to
397397
# constrain the actual payload bytes returned; the [beg,end) should index elements
398398
# containing the first byte to return (at .offset), up tothe last byte
399-
# (.offset+.max_size-1).
400-
beg,end,endactual = self.reply_elements( attribute, data, context )
399+
# (.offset+.max_size-1). Since we might have advances 'beg', we get back the adjusted
400+
# 'offremains', as well the target 'max_size'.
401+
beg,end,endactual,offremains,max_size \
402+
= self.reply_elements( attribute, data, context )
401403
log.debug( "Replying w/ elements [%3d-%-3d/%3d] for %r", beg, end, endactual, data )
402404
if data.service in (self.RD_TAG_RPY, self.RD_FRG_RPY):
403405
# Read Tag [Fragmented]
404406
recs = attribute[beg:end]
405-
off = data[context].get( 'offset' ) or 0 # nonexistent/None/0 --> 0
406-
max_size = data[context].get( 'max_size' ) or self.MAX_BYTES
407407
if attribute.parser.tag_type == STRUCT.tag_type:
408408
# Render the STRUCT UDTs to binary. Assume that each record has its data.input
409409
# representation.
@@ -413,19 +413,24 @@ def request( self, data, addr=None ):
413413
# For STRUCTs *only*, we support arbitrary .offset and max_size; trim it
414414
# here. (For other basic data types, we'll simply return the designated
415415
# elements, which may be less than, or slightly more than the .max_size /
416-
# self.MAX_BYTES by some portion of one element size) Trim it here.
417-
recs = dict( input=input[off:off+max_size] )
416+
# self.MAX_BYTES by some portion of one element size) Trim it here. If we've
417+
# returned the end element of the request, and all of its bytes, we're complete.
418+
trimmed = input[offremains:offremains+max_size]
419+
recs = dict( input=trimmed )
420+
completed = end == endactual and offremains+max_size >= len( input )
418421
else:
419422
# We don't presently support a non-zero .offset for indeterminately sized types
420423
# (eg. STRING/SSTRING, etc.), or a sub-element offset for basic data types.
421424
assert off == 0 or (
422425
attribute.parser.tag_type < STRING.tag_type
423426
and off % attribute.parser.struct_calcsize == 0 )
427+
completed = end == endactual
424428
data[context].data = recs
425-
log.detail( "%s Reading %3d elements %3d-%3d from %s: %r",
426-
self, end - beg, beg, end-1, attribute, data[context].data )
429+
log.detail( "%s Reading %3d elements %3d-%3d %s from %s: %r",
430+
self, end - beg, beg, end-1, "(done)" if completed else "(more)",
431+
attribute, data[context].data )
427432
# Final .status is 0x00 if all requested elements were shipped; 0x06 if not
428-
data.status = 0x00 if end == endactual else 0x06
433+
data.status = 0x00 if completed else 0x06
429434
data.pop( 'status_ext' ) # non-empty dotdict level; use pop instead of del
430435
else:
431436
# Write Tag [Fragmented]. We know the type is right.

0 commit comments

Comments
 (0)