mirror of
https://github.com/JoelBender/modpypes
synced 2025-10-12 21:15:10 +08:00
use the IOContoller API for clients
This commit is contained in:
parent
d8294b1367
commit
3ca637b70e
|
@ -17,6 +17,7 @@ from bacpypes.consolelogging import ArgumentParser
|
|||
|
||||
from bacpypes.comm import Client, bind
|
||||
from bacpypes.core import run
|
||||
from bacpypes.iocb import IOCB, IOController, IOQController
|
||||
|
||||
from .pdu import ExceptionResponse, \
|
||||
ReadCoilsRequest, ReadCoilsResponse, \
|
||||
|
@ -26,29 +27,128 @@ from .pdu import ExceptionResponse, \
|
|||
WriteSingleCoilRequest, WriteSingleCoilResponse, \
|
||||
WriteSingleRegisterRequest, WriteSingleRegisterResponse, \
|
||||
ModbusStruct
|
||||
from .app import ModbusClient, ModbusException
|
||||
from .app import ModbusClient
|
||||
|
||||
# some debugging
|
||||
_debug = 0
|
||||
_log = ModuleLogger(globals())
|
||||
|
||||
#
|
||||
# QController
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class QController(IOQController):
|
||||
|
||||
def __init__(self, request_fn, address):
|
||||
"""Initialize an application controller. To process requests it only
|
||||
needs the function to call that sends an APDU down the stack, the address
|
||||
parameter is to help with debugging."""
|
||||
if _debug: QController._debug("__init__ %r %r", request_fn, address)
|
||||
IOQController.__init__(self, str(address))
|
||||
|
||||
# save a reference to the request function
|
||||
self.request_fn = request_fn
|
||||
self.address = address
|
||||
|
||||
def process_io(self, iocb):
|
||||
"""Called to start processing a request. This is called immediately
|
||||
when the controller is idle, otherwise this is called for the next IOCB
|
||||
when the current request has been satisfied."""
|
||||
if _debug: QController._debug("process_io %r", iocb)
|
||||
|
||||
# this is now an active request
|
||||
self.active_io(iocb)
|
||||
|
||||
# send the request
|
||||
self.request_fn(iocb.args[0])
|
||||
|
||||
#
|
||||
# Controller
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class Controller(Client, IOController):
|
||||
|
||||
def __init__(self):
|
||||
if _debug: Controller._debug("__init__")
|
||||
Client.__init__(self)
|
||||
IOController.__init__(self)
|
||||
|
||||
# controllers for each address
|
||||
self.controllers = {}
|
||||
|
||||
def process_io(self, iocb):
|
||||
if _debug: Controller._debug("process_io %r", iocb)
|
||||
|
||||
# get the destination address from the pdu
|
||||
destination_address = iocb.args[0].pduDestination
|
||||
if _debug: Controller._debug(" - destination_address: %r", destination_address)
|
||||
|
||||
# look up the controller
|
||||
controller = self.controllers.get(destination_address, None)
|
||||
if not controller:
|
||||
controller = QController(self.request, destination_address)
|
||||
self.controllers[destination_address] = controller
|
||||
if _debug: Controller._debug(" - controller: %r", controller)
|
||||
|
||||
# ask the controller to process or queue the request
|
||||
controller.request_io(iocb)
|
||||
|
||||
def request(self, pdu):
|
||||
if _debug: Controller._debug("request %r", pdu)
|
||||
|
||||
# send it downstream
|
||||
super(Controller, self).request(pdu)
|
||||
|
||||
def confirmation(self, pdu):
|
||||
if _debug: Controller._debug("confirmation %r", pdu)
|
||||
|
||||
# get the source address
|
||||
source_address = pdu.pduSource
|
||||
if _debug: Controller._debug(" - source_address: %r", source_address)
|
||||
|
||||
# look up the controller
|
||||
controller = self.controllers.get(source_address, None)
|
||||
if not controller:
|
||||
Controller._debug("no controller for %r" % (source_address,))
|
||||
return
|
||||
if _debug: Controller._debug(" - controller: %r", controller)
|
||||
|
||||
# make sure it has an active iocb
|
||||
if not controller.active_iocb:
|
||||
Controller._debug("no active request for %r" % (source_address,))
|
||||
return
|
||||
|
||||
# complete or abort the request
|
||||
if isinstance(pdu, ExceptionResponse):
|
||||
controller.abort_io(controller.active_iocb, pdu)
|
||||
else:
|
||||
controller.complete_io(controller.active_iocb, pdu)
|
||||
|
||||
# if the queue is empty and idle, forget about the controller
|
||||
if not controller.ioQueue.queue and not controller.active_iocb:
|
||||
if _debug: Controller._debug(" - controller queue is empty")
|
||||
del self.controllers[source_address]
|
||||
|
||||
|
||||
#
|
||||
# ConsoleClient
|
||||
#
|
||||
|
||||
@bacpypes_debugging
|
||||
class ConsoleClient(ConsoleCmd, Client):
|
||||
class ConsoleClient(ConsoleCmd):
|
||||
|
||||
"""
|
||||
Console Client
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, controller):
|
||||
if _debug: ConsoleClient._debug("__init__")
|
||||
ConsoleCmd.__init__(self)
|
||||
|
||||
# no current request
|
||||
self.req = None
|
||||
# save the controller
|
||||
self.controller = controller
|
||||
|
||||
def do_read(self, args):
|
||||
"""read <addr> <unitID> <register> [ <count> ]
|
||||
|
@ -130,11 +230,43 @@ class ConsoleClient(ConsoleCmd, Client):
|
|||
req.mpduUnitID = unitID
|
||||
if _debug: ConsoleClient._debug(" - req: %r", req)
|
||||
|
||||
# save the request
|
||||
self.req = req
|
||||
# make an IOCB
|
||||
iocb = IOCB(req)
|
||||
if _debug: ConsoleClient._debug(" - iocb: %r", iocb)
|
||||
|
||||
# submit the request
|
||||
self.controller.request_io(iocb)
|
||||
|
||||
# wait for the response
|
||||
iocb.wait()
|
||||
iocb.debug_contents()
|
||||
|
||||
# exceptions
|
||||
if iocb.ioError:
|
||||
print(iocb.ioError)
|
||||
|
||||
# read responses
|
||||
elif isinstance(iocb.ioResponse, ReadCoilsResponse):
|
||||
print(" ::= " + str(iocb.ioResponse.bits))
|
||||
|
||||
elif isinstance(iocb.ioResponse, ReadDiscreteInputsResponse):
|
||||
print(" ::= " + str(iocb.ioResponse.bits))
|
||||
|
||||
elif isinstance(iocb.ioResponse, ReadInputRegistersResponse):
|
||||
print(" ::= " + str(iocb.ioResponse.registers))
|
||||
|
||||
elif isinstance(iocb.ioResponse, ReadMultipleRegistersResponse):
|
||||
print(" ::= " + str(iocb.ioResponse.registers))
|
||||
|
||||
for dtype, codec in ModbusStruct.items():
|
||||
try:
|
||||
value = codec.unpack(iocb.ioResponse.registers)
|
||||
print(" " + dtype + " ::= " + str(value))
|
||||
except Exception as err:
|
||||
if _debug: ConsoleClient._debug("unpack exception %r: %r", codec, err)
|
||||
else:
|
||||
raise TypeError("unsupported response")
|
||||
|
||||
# send it along
|
||||
self.request(req)
|
||||
|
||||
def do_write(self, args):
|
||||
"""write <addr> <unitID> <register> <value>
|
||||
|
@ -213,49 +345,26 @@ class ConsoleClient(ConsoleCmd, Client):
|
|||
req.mpduUnitID = unitID
|
||||
if _debug: ConsoleClient._debug(" - req: %r", req)
|
||||
|
||||
# save the request
|
||||
self.req = req
|
||||
# make an IOCB
|
||||
iocb = IOCB(req)
|
||||
if _debug: ConsoleClient._debug(" - iocb: %r", iocb)
|
||||
|
||||
# send it along
|
||||
self.request(req)
|
||||
# submit the request
|
||||
self.controller.request_io(iocb)
|
||||
|
||||
def confirmation(self, pdu):
|
||||
"""Prints out the contents of the response from the
|
||||
device.
|
||||
"""
|
||||
|
||||
if _debug: ConsoleClient._debug("confirmation %r", pdu)
|
||||
# wait for the response
|
||||
iocb.wait()
|
||||
|
||||
# exceptions
|
||||
if isinstance(pdu, ExceptionResponse):
|
||||
print(ModbusException(pdu.exceptionCode))
|
||||
|
||||
# read responses
|
||||
elif isinstance(pdu, ReadCoilsResponse):
|
||||
print(" ::=" + str(pdu.bits))
|
||||
|
||||
elif isinstance(pdu, ReadDiscreteInputsResponse):
|
||||
print(" ::=" + str(pdu.bits))
|
||||
|
||||
elif isinstance(pdu, ReadInputRegistersResponse):
|
||||
print(" ::=" + str(pdu.registers))
|
||||
|
||||
elif isinstance(pdu, ReadMultipleRegistersResponse):
|
||||
print(" ::=" + str(pdu.registers))
|
||||
|
||||
for dtype, codec in ModbusStruct.items():
|
||||
try:
|
||||
value = codec.unpack(pdu.registers)
|
||||
print(" " + dtype + " ::= " + str(value))
|
||||
except Exception as err:
|
||||
if _debug: ConsoleClient._debug("unpack exception %r: %r", codec, err)
|
||||
if iocb.ioError:
|
||||
print(iocb.ioError)
|
||||
|
||||
# write responses
|
||||
elif isinstance(pdu, WriteSingleCoilResponse):
|
||||
print(" ::=" + str(pdu.bits))
|
||||
elif isinstance(iocb.ioResponse, WriteSingleCoilResponse):
|
||||
print(" ::= " + str(iocb.ioResponse.bits))
|
||||
|
||||
elif isinstance(pdu, WriteSingleRegisterResponse):
|
||||
print(" ::=" + str(pdu.bits))
|
||||
elif isinstance(iocb.ioResponse, WriteSingleRegisterResponse):
|
||||
print(" ::= " + str(iocb.ioResponse.value))
|
||||
|
||||
else:
|
||||
raise TypeError("unsupported response")
|
||||
|
@ -274,8 +383,15 @@ def main():
|
|||
if _debug: _log.debug("initialization")
|
||||
if _debug: _log.debug(" - args: %r", args)
|
||||
|
||||
# make a controller
|
||||
this_controller = Controller()
|
||||
if _debug: _log.debug(" - this_controller: %r", this_controller)
|
||||
|
||||
this_console = ConsoleClient(this_controller)
|
||||
if _debug: _log.debug(" - this_console: %r", this_console)
|
||||
|
||||
# local IO functions
|
||||
bind(ConsoleClient(), ModbusClient())
|
||||
bind(this_controller, ModbusClient())
|
||||
|
||||
_log.debug("running")
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user