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

186 lines
5.3 KiB
Python

#!/usr/bin/env python
"""
This simple TCP client application connects to a server and sends the text
entered in the console. There is no conversion from incoming streams of
content into a line or any other higher-layer concept of a packet.
"""
import os
from bacpypes.debugging import bacpypes_debugging, ModuleLogger, xtob
from bacpypes.core import run, stop, deferred
from bacpypes.task import TaskManager
from bacpypes.comm import PDU, Client, Server, bind, ApplicationServiceElement
from bacpypes.consolelogging import ArgumentParser
from bacpypes.console import ConsoleClient
from bacpypes.tcp import TCPClientDirector
# some debugging
_debug = 0
_log = ModuleLogger(globals())
# settings
SERVER_HOST = os.getenv('SERVER_HOST', '127.0.0.1')
SERVER_PORT = int(os.getenv('SERVER_PORT', 9000))
CONNECT_TIMEOUT = int(os.getenv('CONNECT_TIMEOUT', 0)) or None
IDLE_TIMEOUT = int(os.getenv('IDLE_TIMEOUT', 0)) or None
# globals
args = None
server_address = None
#
# MiddleMan
#
class MiddleMan(Client, Server):
"""
An instance of this class sits between the TCPClientDirector and the
console client. Downstream packets from a console have no concept of a
destination, so this is added to the PDUs before being sent to the
director. The source information in upstream packets is ignored by the
console client.
"""
def indication(self, pdu):
if _debug: MiddleMan._debug("indication %r", pdu)
global server_address
# no data means EOF, stop
if not pdu.pduData:
# ask the director (downstream peer) to close the connection
self.clientPeer.disconnect(server_address)
return
# pass it along
self.request(PDU(pdu.pduData, destination=server_address))
def confirmation(self, pdu):
if _debug: MiddleMan._debug("confirmation %r", pdu)
# check for errors
if isinstance(pdu, Exception):
if _debug: MiddleMan._debug(" - exception: %s", pdu)
return
# pass it along
self.response(pdu)
bacpypes_debugging(MiddleMan)
#
# MiddleManASE
#
class MiddleManASE(ApplicationServiceElement):
"""
An instance of this class is bound to the director, which is a
ServiceAccessPoint. It receives notifications of new actors connected
to a server, actors that are going away when the connections are closed,
and socket errors.
"""
def indication(self, add_actor=None, del_actor=None, actor_error=None, error=None):
if add_actor:
if _debug: MiddleManASE._debug("indication add_actor=%r", add_actor)
if del_actor:
if _debug: MiddleManASE._debug("indication del_actor=%r", del_actor)
# if there are no clients, quit
if not self.elementService.clients:
if _debug: MiddleManASE._debug(" - no clients, stopping")
stop()
if actor_error:
if _debug: MiddleManASE._debug("indication actor_error=%r error=%r", actor_error, error)
# tell the director to close
self.elementService.disconnect(actor_error.peer)
bacpypes_debugging(MiddleManASE)
#
# main
#
def main():
"""
Main function, called when run as an application.
"""
global args, server_address
# parse the command line arguments
parser = ArgumentParser(description=__doc__)
parser.add_argument(
"host", nargs='?',
help="address of host (default %r)" % (SERVER_HOST,),
default=SERVER_HOST,
)
parser.add_argument(
"port", nargs='?', type=int,
help="server port (default %r)" % (SERVER_PORT,),
default=SERVER_PORT,
)
parser.add_argument(
"--hello", action="store_true",
default=False,
help="send a hello message",
)
parser.add_argument(
"--connect-timeout", nargs='?', type=int,
help="idle connection timeout",
default=CONNECT_TIMEOUT,
)
parser.add_argument(
"--idle-timeout", nargs='?', type=int,
help="idle connection timeout",
default=IDLE_TIMEOUT,
)
args = parser.parse_args()
if _debug: _log.debug("initialization")
if _debug: _log.debug(" - args: %r", args)
# extract the server address and port
host = args.host
port = args.port
server_address = (host, port)
if _debug: _log.debug(" - server_address: %r", server_address)
# build the stack
this_console = ConsoleClient()
if _debug: _log.debug(" - this_console: %r", this_console)
this_middle_man = MiddleMan()
if _debug: _log.debug(" - this_middle_man: %r", this_middle_man)
this_director = TCPClientDirector(
connect_timeout=args.connect_timeout,
idle_timeout=args.idle_timeout,
)
if _debug: _log.debug(" - this_director: %r", this_director)
bind(this_console, this_middle_man, this_director)
bind(MiddleManASE(), this_director)
# create a task manager for scheduled functions
task_manager = TaskManager()
if _debug: _log.debug(" - task_manager: %r", task_manager)
# don't wait to connect
deferred(this_director.connect, server_address)
# send hello maybe
if args.hello:
deferred(this_middle_man.indication, PDU(b'Hello, world!\n'))
if _debug: _log.debug("running")
run()
if _debug: _log.debug("fini")
if __name__ == "__main__":
main()