1
0
mirror of https://github.com/JoelBender/bacpypes synced 2025-09-28 22:15:23 +08:00

sync py34 with py27

This commit is contained in:
Joel Bender 2016-07-03 00:08:07 -04:00
parent 54a5104d35
commit f78a8c9d67
3 changed files with 284 additions and 129 deletions

View File

@ -68,11 +68,19 @@ def decode_max_apdu_segments(arg):
# encode_max_apdu_response/decode_max_apdu_response
#
_max_apdu_response_encoding = {0:50, 1:128, 2:206, 3:480, 4:1024, 5:1476}
def encode_max_apdu_response(arg):
return {50:0, 128:1, 206:2, 480:3, 1024:4, 1476:5}.get(arg)
encodings = _max_apdu_response_encoding.items()
encodings.sort(lambda x, y: y[1] - x[1])
for i, v in encodings:
if (v <= arg):
return i
raise ValueError("invalid max APDU response encoding: {0}".format(arg))
def decode_max_apdu_response(arg):
return {0:50, 1:128, 2:206, 3:480, 4:1024, 5:1476}.get(arg)
return _max_apdu_response_encoding.get(arg)
#
# APCI

View File

@ -4,10 +4,10 @@
Application Module
"""
from .debugging import bacpypes_debugging, ModuleLogger
from .debugging import bacpypes_debugging, DebugContents, ModuleLogger
from .comm import ApplicationServiceElement, bind
from .pdu import Address
from .pdu import Address, LocalStation, RemoteStation
from .primitivedata import Atomic, Date, Null, ObjectIdentifier, Time, Unsigned
from .constructeddata import Any, Array, ArrayOf
@ -38,6 +38,152 @@ from .apdu import \
_debug = 0
_log = ModuleLogger(globals())
#
# DeviceInfo
#
@bacpypes_debugging
class DeviceInfo(DebugContents):
_debug_contents = (
'deviceIdentifier',
'address',
'maxApduLengthAccepted',
'segmentationSupported',
'vendorID',
'maxNpduLength',
'maxSegmentsAccepted',
)
def __init__(self):
# this information is from an IAmRequest
self.deviceIdentifier = None # device identifier
self.address = None # LocalStation or RemoteStation
self.maxApduLengthAccepted = 1024 # maximum APDU device will accept
self.segmentationSupported = 'noSegmentation' # normally no segmentation
self.vendorID = None # vendor identifier
self.maxNpduLength = 1497 # maximum we can send in transit
self.maxSegmentsAccepted = None # value for proposed/actual window size
#
# DeviceInfoCache
#
@bacpypes_debugging
class DeviceInfoCache:
def __init__(self):
if _debug: DeviceInfoCache._debug("__init__")
# empty cache
self.cache = {}
def has_device_info(self, key):
"""Return true iff cache has information about the device."""
if _debug: DeviceInfoCache._debug("has_device_info %r", key)
return key in self.cache
def add_device_info(self, apdu):
"""Create a device information record based on the contents of an
IAmRequest and put it in the cache."""
if _debug: DeviceInfoCache._debug("add_device_info %r", apdu)
# get the existing cache record by identifier
info = self.get_device_info(apdu.iAmDeviceIdentifier[1])
if _debug: DeviceInfoCache._debug(" - info: %r", info)
# update existing record
if info:
if (info.address == apdu.pduSource):
return
info.address = apdu.pduSource
else:
# get the existing record by address (creates a new record)
info = self.get_device_info(apdu.pduSource)
if _debug: DeviceInfoCache._debug(" - info: %r", info)
info.deviceIdentifier = apdu.iAmDeviceIdentifier[1]
# update the rest of the values
info.maxApduLengthAccepted = apdu.maxApduLengthAccepted
info.segmentationSupported = apdu.segmentationSupported
info.vendorID = apdu.vendorID
# say this is an updated record
self.update_device_info(info)
def get_device_info(self, key):
"""Return the known information about the device. If the key is the
address of an unknown device, build a generic device information record
add put it in the cache."""
if _debug: DeviceInfoCache._debug("get_device_info %r", key)
if isinstance(key, int):
current_info = self.cache.get(key, None)
elif not isinstance(key, Address):
raise TypeError("key must be integer or an address")
elif key.addrType not in (Address.localStationAddr, Address.remoteStationAddr):
raise TypeError("address must be a local or remote station")
else:
current_info = self.cache.get(key, None)
if not current_info:
current_info = DeviceInfo()
current_info.address = key
current_info._cache_keys = (None, key)
self.cache[key] = current_info
if _debug: DeviceInfoCache._debug(" - current_info: %r", current_info)
return current_info
def update_device_info(self, info):
"""The application has updated one or more fields in the device
information record and the cache needs to be updated to reflect the
changes. If this is a cached version of a persistent record then this
is the opportunity to update the database."""
if _debug: DeviceInfoCache._debug("update_device_info %r", info)
cache_id, cache_address = info._cache_keys
if (cache_id is not None) and (info.deviceIdentifier != cache_id):
if _debug: DeviceInfoCache._debug(" - device identifier updated")
# remove the old reference, add the new one
del self.cache[cache_id]
self.cache[info.deviceIdentifier] = info
cache_id = info.deviceIdentifier
if (cache_address is not None) and (info.address != cache_address):
if _debug: DeviceInfoCache._debug(" - device address updated")
# remove the old reference, add the new one
del self.cache[cache_address]
self.cache[info.address] = info
cache_address = info.address
# update the keys
info._cache_keys = (cache_id, cache_address)
def release_device_info(self, info):
"""This function is called by the segmentation state machine when it
has finished with the device information."""
if _debug: DeviceInfoCache._debug("release_device_info %r", info)
cache_id, cache_address = info._cache_keys
if cache_id is not None:
del self.cache[cache_id]
if cache_address is not None:
del self.cache[cache_address]
#
# CurrentDateProperty
#
@ -146,13 +292,19 @@ class LocalDeviceObject(DeviceObject):
@bacpypes_debugging
class Application(ApplicationServiceElement):
def __init__(self, localDevice, localAddress, aseID=None):
if _debug: Application._debug("__init__ %r %r aseID=%r", localDevice, localAddress, aseID)
def __init__(self, localDevice, localAddress, deviceInfoCache=None, aseID=None):
if _debug: Application._debug("__init__ %r %r deviceInfoCache=%r aseID=%r", localDevice, localAddress, deviceInfoCache, aseID)
ApplicationServiceElement.__init__(self, aseID)
# keep track of the local device
self.localDevice = localDevice
# use the provided cache or make a default one
if deviceInfoCache:
self.deviceInfoCache = deviceInfoCache
else:
self.deviceInfoCache = DeviceInfoCache()
# bind the device object to this application
localDevice._app = self
@ -610,9 +762,9 @@ class Application(ApplicationServiceElement):
@bacpypes_debugging
class BIPSimpleApplication(Application):
def __init__(self, localDevice, localAddress, aseID=None):
if _debug: BIPSimpleApplication._debug("__init__ %r %r aseID=%r", localDevice, localAddress, aseID)
Application.__init__(self, localDevice, localAddress, aseID)
def __init__(self, localDevice, localAddress, deviceInfoCache=None, aseID=None):
if _debug: BIPSimpleApplication._debug("__init__ %r %r deviceInfoCache=%r aseID=%r", localDevice, localAddress, deviceInfoCache, aseID)
Application.__init__(self, localDevice, localAddress, deviceInfoCache, aseID)
# include a application decoder
self.asap = ApplicationServiceAccessPoint()
@ -621,6 +773,10 @@ class BIPSimpleApplication(Application):
# can know if it should support segmentation
self.smap = StateMachineAccessPoint(localDevice)
# the segmentation state machines need access to the same device
# information cache as the application
self.smap.deviceInfoCache = self.deviceInfoCache
# a network service access point will be needed
self.nsap = NetworkServiceAccessPoint()

View File

@ -22,38 +22,6 @@ from .apdu import AbortPDU, AbortReason, ComplexAckPDU, \
_debug = 0
_log = ModuleLogger(globals())
#
# DeviceInfo
#
class DeviceInfo(DebugContents):
_debug_contents = (
'address',
'maxNpduLength',
'segmentationSupported',
'maxApduLengthAccepted',
'maxSegmentsAccepted',
)
def __init__(
self, address=None,
maxNpduLength=1024,
segmentationSupported='noSegmentation',
maxApduLengthAccepted=1024,
maxSegmentsAccepted=None,
):
if not isinstance(address, (None.__class__, Address, LocalStation, RemoteStation)):
raise TypeError("address")
self.address = address # LocalStation or RemoteStation
self.maxNpduLength = maxNpduLength # maximum we can send in transit
self.segmentationSupported = segmentationSupported # normally no segmentation
self.maxApduLengthAccepted = maxApduLengthAccepted # how big to divide up apdu's
self.maxSegmentsAccepted = maxSegmentsAccepted # limit on how many segments to recieve
#----------------------------------------------------------------------
#
# SSM - Segmentation State Machine
#
@ -76,26 +44,27 @@ class SSM(OneShotTask, DebugContents):
, 'SEGMENTED_RESPONSE', 'SEGMENTED_CONFIRMATION', 'COMPLETED', 'ABORTED'
]
_debug_contents = ('ssmSAP', 'remoteDevice', 'invokeID'
_debug_contents = ('ssmSAP', 'localDevice', 'remoteDevice', 'invokeID'
, 'state', 'segmentAPDU', 'segmentSize', 'segmentCount', 'maxSegmentsAccepted'
, 'retryCount', 'segmentRetryCount', 'sentAllSegments', 'lastSequenceNumber'
, 'initialSequenceNumber', 'actualWindowSize', 'proposedWindowSize'
)
def __init__(self, sap):
def __init__(self, sap, localDevice, remoteDevice):
"""Common parts for client and server segmentation."""
if _debug: SSM._debug("__init__ %r", sap)
if _debug: SSM._debug("__init__ %r %r %r", sap, localDevice, remoteDevice)
OneShotTask.__init__(self)
self.ssmSAP = sap # reference to the service access point
self.remoteDevice = None # remote device
self.ssmSAP = sap # service access point
self.localDevice = localDevice # local device information, DeviceObject
self.remoteDevice = remoteDevice # remote device information, a DeviceInfo instance
self.invokeID = None # invoke ID
self.state = IDLE # initial state
self.segmentAPDU = None # refers to request or response
self.segmentSize = None # how big the pieces are
self.segmentCount = None
self.maxSegmentsAccepted = None # maximum number of segments client will accept
self.maxSegmentsAccepted = None # maximum number of segments
self.retryCount = None
self.segmentRetryCount = None
@ -105,6 +74,14 @@ class SSM(OneShotTask, DebugContents):
self.actualWindowSize = None
self.proposedWindowSize = None
# look for our segmentation parameters from our device object
if localDevice:
self.maxApduLengthAccepted = localDevice.maxApduLengthAccepted
self.segmentationSupported = localDevice.segmentationSupported
else:
self.maxApduLengthAccepted = 1024
self.segmentationSupported = 'noSegmentation'
def start_timer(self, msecs):
if _debug: SSM._debug("start_timer %r", msecs)
@ -179,15 +156,12 @@ class SSM(OneShotTask, DebugContents):
segAPDU = ConfirmedRequestPDU(self.segmentAPDU.apduService)
segAPDU.apduMaxSegs = self.maxSegmentsAccepted
segAPDU.apduMaxResp = self.ssmSAP.maxApduLengthAccepted
segAPDU.apduMaxResp = self.maxApduLengthAccepted
segAPDU.apduInvokeID = self.invokeID;
# segmented response accepted?
segAPDU.apduSA = ((self.ssmSAP.segmentationSupported == 'segmentedBoth') \
or (self.ssmSAP.segmentationSupported == 'segmentedReceive'))
if _debug:
SSM._debug(" - segmented response accepted: %r", segAPDU.apduSA)
SSM._debug(" - self.ssmSAP.segmentationSupported: %r", self.ssmSAP.segmentationSupported)
segAPDU.apduSA = self.segmentationSupported in ('segmentedReceive', 'segmentedBoth')
if _debug: SSM._debug(" - segmented response accepted: %r", segAPDU.apduSA)
elif self.segmentAPDU.apduType == ComplexAckPDU.pduType:
if _debug: SSM._debug(" - complex ack context")
@ -262,9 +236,9 @@ class SSM(OneShotTask, DebugContents):
@bacpypes_debugging
class ClientSSM(SSM):
def __init__(self, sap):
if _debug: ClientSSM._debug("__init__ %s", sap)
SSM.__init__(self, sap)
def __init__(self, sap, localDevice, remoteDevice):
if _debug: ClientSSM._debug("__init__ %s %r %r", sap, localDevice, remoteDevice)
SSM.__init__(self, sap, localDevice, remoteDevice)
# initialize the retry count
self.retryCount = 0
@ -278,8 +252,15 @@ class ClientSSM(SSM):
# completed or aborted, remove tracking
if (newState == COMPLETED) or (newState == ABORTED):
if _debug: ClientSSM._debug(" - remove from active transactions")
self.ssmSAP.clientTransactions.remove(self)
if _debug: ClientSSM._debug(" - release device information")
self.ssmSAP.deviceInfoCache.release_device_info(self.remoteDevice)
# help along the garbage collector
self.ssmSAP = self.localDevice = self.remoteDevice = None
def request(self, apdu):
"""This function is called by client transaction functions when it wants
to send a message to the device."""
@ -304,24 +285,20 @@ class ClientSSM(SSM):
# save the request and set the segmentation context
self.set_segmentation_context(apdu)
# get information about the device
self.remoteDevice = self.ssmSAP.get_device_info(apdu.pduDestination)
if _debug: ClientSSM._debug(" - remoteDevice: %r", self.remoteDevice)
# the segment size is the minimum of the maximum size I can transmit
# (assumed to have no local buffer limitations), the maximum conveyable
# by the internetwork to the remote device, and the maximum APDU size
# accepted by the remote device.
self.segmentSize = min(self.remoteDevice.maxNpduLength, self.remoteDevice.maxApduLengthAccepted)
# the segment size is the minimum of the maximum size I can transmit,
# the maximum conveyable by the internetwork to the remote device, and
# the maximum APDU size accepted by the remote device.
self.segmentSize = min(
self.maxApduLengthAccepted,
self.remoteDevice.maxNpduLength,
self.remoteDevice.maxApduLengthAccepted,
)
if _debug: ClientSSM._debug(" - segment size: %r", self.segmentSize)
# save the maximum number of segments acceptable in the reply
# the maximum number of segments acceptable in the reply
if apdu.apduMaxSegs is not None:
# this request overrides the default
self.maxSegmentsAccepted = apdu.apduMaxSegs
else:
# use the default in the device definition
self.maxSegmentsAccepted = self.ssmSAP.maxSegmentsAccepted
# save the invoke ID
self.invokeID = apdu.apduInvokeID
@ -340,12 +317,12 @@ class ClientSSM(SSM):
# make sure we support segmented transmit if we need to
if self.segmentCount > 1:
if (self.ssmSAP.segmentationSupported != 'segmentedTransmit') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'):
if self.segmentationSupported not in ('segmentedTransmit', 'segmentedBoth'):
if _debug: ClientSSM._debug(" - local device can't send segmented requests")
abort = self.abort(AbortReason.segmentationNotSupported)
self.response(abort)
return
if (self.remoteDevice.segmentationSupported != 'segmentedReceive') and (self.remoteDevice.segmentationSupported != 'segmentedBoth'):
if self.remoteDevice.segmentationSupported not in ('segmentedReceive', 'segmentedBoth'):
if _debug: ClientSSM._debug(" - remote device can't receive segmented requests")
abort = self.abort(AbortReason.segmentationNotSupported)
self.response(abort)
@ -544,7 +521,7 @@ class ClientSSM(SSM):
self.set_state(COMPLETED)
self.response(apdu)
elif (self.ssmSAP.segmentationSupported != 'segmentedReceive') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'):
elif self.segmentationSupported not in ('segmentedReceive', 'segmentedBoth'):
if _debug: ClientSSM._debug(" - local device can't receive segmented messages")
abort = self.abort(AbortReason.segmentationNotSupported)
self.response(abort)
@ -671,9 +648,9 @@ class ClientSSM(SSM):
@bacpypes_debugging
class ServerSSM(SSM):
def __init__(self, sap):
if _debug: ServerSSM._debug("__init__ %s", sap)
SSM.__init__(self, sap)
def __init__(self, sap, localDevice, remoteDevice):
if _debug: ServerSSM._debug("__init__ %s %r %r", sap, localDevice, remoteDevice)
SSM.__init__(self, sap, localDevice, remoteDevice)
def set_state(self, newState, timer=0):
"""This function is called when the client wants to change state."""
@ -684,8 +661,15 @@ class ServerSSM(SSM):
# completed or aborted, remove tracking
if (newState == COMPLETED) or (newState == ABORTED):
if _debug: ServerSSM._debug(" - remove from active transactions")
self.ssmSAP.serverTransactions.remove(self)
if _debug: ServerSSM._debug(" - release device information")
self.ssmSAP.deviceInfoCache.release_device_info(self.remoteDevice)
# help along the garbage collector
self.ssmSAP = self.localDevice = self.remoteDevice = None
def request(self, apdu):
"""This function is called by transaction functions to send
to the application."""
@ -704,7 +688,7 @@ class ServerSSM(SSM):
if _debug: ServerSSM._debug("indication %r", apdu)
if self.state == IDLE:
self.Idle(apdu)
self.idle(apdu)
elif self.state == SEGMENTED_REQUEST:
self.segmented_request(apdu)
elif self.state == AWAIT_RESPONSE:
@ -783,19 +767,23 @@ class ServerSSM(SSM):
# make sure we support segmented transmit if we need to
if self.segmentCount > 1:
if _debug: ServerSSM._debug(" - segmentation required, %d segemnts", self.segmentCount)
if _debug: ServerSSM._debug(" - segmentation required, %d segments", self.segmentCount)
if (self.ssmSAP.segmentationSupported != 'segmentedTransmit') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'):
# make sure we support segmented transmit
if self.segmentationSupported not in ('segmentedTransmit', 'segmentedBoth'):
if _debug: ServerSSM._debug(" - server can't send segmented responses")
abort = self.abort(AbortReason.segmentationNotSupported)
self.request(abort)
return
if (self.remoteDevice.segmentationSupported != 'segmentedReceive') and (self.remoteDevice.segmentationSupported != 'segmentedBoth'):
abort = self.abort(AbortReason.segmentationNotSupported)
self.request(abort)
self.reponse(abort)
return
### check to make sure the client can receive that many
### look at apduMaxSegs
# make sure client supports segmented receive
if self.remoteDevice.segmentationSupported not in ('segmentedReceive', 'segmentedBoth'):
if _debug: ServerSSM._debug(" - client can't receive segmented responses")
abort = self.abort(AbortReason.segmentationNotSupported)
self.response(abort)
return
### check for APDUTooLong?
# initialize the state
self.segmentRetryCount = 0
@ -846,8 +834,8 @@ class ServerSSM(SSM):
# return an abort APDU
return AbortPDU(True, self.invokeID, reason)
def Idle(self, apdu):
if _debug: ServerSSM._debug("Idle %r", apdu)
def idle(self, apdu):
if _debug: ServerSSM._debug("idle %r", apdu)
# make sure we're getting confirmed requests
if not isinstance(apdu, ConfirmedRequestPDU):
@ -857,36 +845,29 @@ class ServerSSM(SSM):
self.invokeID = apdu.apduInvokeID
if _debug: ServerSSM._debug(" - invoke ID: %r", self.invokeID)
# get information about the client
self.remoteDevice = remote_device = self.ssmSAP.get_device_info(apdu.pduSource)
if _debug: ServerSSM._debug(" - remoteDevice: %r", self.remoteDevice)
# make sure the device information is synced with the request
if apdu.apduSA:
if remote_device.segmentationSupported == 'noSegmentation':
if _debug: ServerSSM._debug(" - client supports segmented receive")
remote_device.segmentationSupported = 'segmentedReceive'
if self.remoteDevice.segmentationSupported == 'noSegmentation':
if _debug: ServerSSM._debug(" - client actually supports segmented receive")
self.remoteDevice.segmentationSupported = 'segmentedReceive'
elif remote_device.segmentationSupported == 'segmentedTransmit':
if _debug: ServerSSM._debug(" - client supports both segmented transmit and receive")
remote_device.segmentationSupported = 'segmentedBoth'
elif self.remoteDevice.segmentationSupported == 'segmentedTransmit':
if _debug: ServerSSM._debug(" - client actually supports both segmented transmit and receive")
self.remoteDevice.segmentationSupported = 'segmentedBoth'
elif remote_device.segmentationSupported == 'segmentedReceive':
elif self.remoteDevice.segmentationSupported == 'segmentedReceive':
pass
elif remote_device.segmentationSupported == 'segmentedBoth':
elif self.remoteDevice.segmentationSupported == 'segmentedBoth':
pass
else:
raise RuntimeError("invalid segmentation supported in device info")
if apdu.apduMaxSegs != remote_device.maxSegmentsAccepted:
if _debug: ServerSSM._debug(" - updating maximum segments accepted")
remote_device.maxSegmentsAccepted = apdu.apduMaxSegs
if apdu.apduMaxResp != remote_device.maxApduLengthAccepted:
if _debug: ServerSSM._debug(" - updating maximum max APDU length accepted")
remote_device.maxApduLengthAccepted = apdu.apduMaxResp
if apdu.apduMaxSegs != self.remoteDevice.maxSegmentsAccepted:
if _debug: ServerSSM._debug(" - update maximum segments accepted?")
if apdu.apduMaxResp != self.remoteDevice.maxApduLengthAccepted:
if _debug: ServerSSM._debug(" - update maximum max APDU length accepted?")
# save the number of segments the client is willing to accept in the ack
self.maxSegmentsAccepted = apdu.apduMaxSegs
@ -898,7 +879,7 @@ class ServerSSM(SSM):
return
# make sure we support segmented requests
if (self.ssmSAP.segmentationSupported != 'segmentedReceive') and (self.ssmSAP.segmentationSupported != 'segmentedBoth'):
if self.segmentationSupported not in ('segmentedReceive', 'segmentedBoth'):
abort = self.abort(AbortReason.segmentationNotSupported)
self.response(abort)
return
@ -1073,31 +1054,34 @@ class ServerSSM(SSM):
#
@bacpypes_debugging
class StateMachineAccessPoint(DeviceInfo, Client, ServiceAccessPoint):
class StateMachineAccessPoint(Client, ServiceAccessPoint):
def __init__(self, device, sap=None, cid=None):
if _debug: StateMachineAccessPoint._debug("__init__ %r sap=%r cid=%r", device, sap, cid)
def __init__(self, localDevice=None, deviceInfoCache=None, sap=None, cid=None):
if _debug: StateMachineAccessPoint._debug("__init__ localDevice=%r deviceInfoCache=%r sap=%r cid=%r", localDevice, deviceInfoCache, sap, cid)
# basic initialization
DeviceInfo.__init__(self)
Client.__init__(self, cid)
ServiceAccessPoint.__init__(self, sap)
# device information from the device object
self.segmentationSupported = device.segmentationSupported # normally no segmentation
self.segmentTimeout = device.apduSegmentTimeout # how long to wait for a segAck
self.maxApduLengthAccepted = device.maxApduLengthAccepted # how big to divide up apdu's
self.maxSegmentsAccepted = device.maxSegmentsAccepted # limit on how many segments to recieve
# save a reference to the local device object and the cache
self.localDevice = localDevice
self.deviceInfoCache = deviceInfoCache
# client settings
self.clientTransactions = []
self.retryCount = device.numberOfApduRetries # how many times to repeat the request
self.retryTimeout = device.apduTimeout # how long between retrying the request
self.nextInvokeID = 1
self.clientTransactions = []
# server settings
self.serverTransactions = []
self.applicationTimeout = device.apduTimeout # how long the application has to respond
# segmentation settings
self.retryTimeout = 3000
self.segmentTimeout = 1500
self.maxSegmentsAccepted = 8
# how long the state machine is willing to wait for the application
# layer to form a response and send it
self.applicationTimeout = 3000
def get_next_invoke_id(self, addr):
"""Called by clients to get an unused invoke ID."""
@ -1120,13 +1104,6 @@ class StateMachineAccessPoint(DeviceInfo, Client, ServiceAccessPoint):
return invokeID
def get_device_info(self, addr):
"""get the segmentation supported and max APDU length accepted for a device."""
if _debug: StateMachineAccessPoint._debug("get_device_info %r", addr)
# return a generic info object
return DeviceInfo(addr)
def confirmation(self, pdu):
"""Packets coming up the stack are APDU's."""
if _debug: StateMachineAccessPoint._debug("confirmation %r", pdu)
@ -1148,8 +1125,11 @@ class StateMachineAccessPoint(DeviceInfo, Client, ServiceAccessPoint):
if (apdu.pduSource == tr.remoteDevice.address) and (apdu.apduInvokeID == tr.invokeID):
break
else:
# find the remote device information
remoteDevice = self.deviceInfoCache.get_device_info(apdu.pduSource)
# build a server transaction
tr = ServerSSM(self)
tr = ServerSSM(self, self.localDevice, remoteDevice)
# add it to our transactions to track it
self.serverTransactions.append(tr)
@ -1244,8 +1224,13 @@ class StateMachineAccessPoint(DeviceInfo, Client, ServiceAccessPoint):
if (apdu.pduDestination.addrType != Address.localStationAddr) and (apdu.pduDestination.addrType != Address.remoteStationAddr):
StateMachineAccessPoint._warning("%s is not a local or remote station", apdu.pduDestination)
# find the remote device information
remoteDevice = self.deviceInfoCache.get_device_info(apdu.pduDestination)
if _debug: StateMachineAccessPoint._debug(" - remoteDevice: %r", remoteDevice)
# create a client transaction state machine
tr = ClientSSM(self)
tr = ClientSSM(self, self.localDevice, remoteDevice)
if _debug: StateMachineAccessPoint._debug(" - client segmentation state machine: %r", tr)
# add it to our transactions to track it
self.clientTransactions.append(tr)
@ -1359,6 +1344,12 @@ class ApplicationServiceAccessPoint(ApplicationServiceElement, ServiceAccessPoin
# forward the encoded packet
self.request(xpdu)
# if the upper layers of the application did not assign an invoke ID,
# copy the one that was assigned on its way down the stack
if isinstance(apdu, ConfirmedRequestPDU) and apdu.apduInvokeID is None:
if _debug: ApplicationServiceAccessPoint._debug(" - pass invoke ID upstream %r", xpdu.apduInvokeID)
apdu.apduInvokeID = xpdu.apduInvokeID
def confirmation(self, apdu):
if _debug: ApplicationServiceAccessPoint._debug("confirmation %r", apdu)