def main(): ## Parse command line arguments ## description = "Measure the MQTT message sending speed. Implemented using a 'Resource' from the SG framework." commandlineparser = argparse.ArgumentParser(description=description) commandlineparser.add_argument('-n', help="Number of MQTT messages to send. Defaults to %(default)s messages.", type=int, default=1000) commandline = commandlineparser.parse_args() assert commandline.n > 0, "You must send at least 1 message" ## Set up SG resource ## resource = sgframework.Resource(RESOURCE_NAME, MQTT_HOST) resource.register_outgoing_data(DATA_SIGNAL_NAME) resource.start(use_threaded_networking=True) ## Send data ## starttime = time.time() for i in range(commandline.n): resource.send_data(DATA_SIGNAL_NAME, "message"+str(i)) stoptime = time.time() ## Calculate statistics ## execution_time = stoptime - starttime messagerate = commandline.n / execution_time print("Done putting {} MQTT messages in the send queue.".format(commandline.n)) print("It took {:.1f} seconds, corresponding to {:.1f} MQTT messages per second".format( execution_time, messagerate)) ## Shutting down ## time.sleep(10) resource.stop()
def testLoopQos2(self): with open(self.OUTPUT_FILE_SUBSCRIBER, 'w') as subscriber_outputfile: subcriber = subprocess.Popen(['mosquitto_sub', '-v', '-t', '+/#'], stdout=subscriber_outputfile, stderr=subprocess.STDOUT) resource = sgframework.Resource('testresource', 'localhost') resource.register_outgoing_data('teststate') resource.qos = 2 resource.start(use_threaded_networking=True) # Send commands resource.send_command('remoteservice', 'remotestate', "run remote") time.sleep(2) # Terminate, and flush files resource.stop() subcriber.kill() time.sleep(0.2) subscriber_outputfile.flush() os.fsync(subscriber_outputfile.fileno()) # Verify that the resource has emitted the MQTT data messages, and availability info with open(self.OUTPUT_FILE_SUBSCRIBER, 'r') as subscriber_outputfile: lines = subscriber_outputfile.readlines() text = ' '.join(lines) self.assertIn("resourceavailable/testresource/presence True", text) self.assertIn("dataavailable/testresource/teststate True", text) self.assertIn("command/remoteservice/remotestate run remote", text) self.assertIn("resourceavailable/testresource/presence False", text)
def testRepr(self): on_remoteservice_data = unittest.mock.Mock() resource = sgframework.Resource('testresource', 'localhost') resource.register_outgoing_data('teststate') resource.register_outgoing_data('teststate2') resource.register_outgoing_data('teststate3') resource.register_outgoing_data('teststate4') resource.register_outgoing_data('teststate5') resource.register_incoming_data('remoteservice', 'remotestate', on_remoteservice_data) resource.register_incoming_data('remoteservice', 'remotestate2', on_remoteservice_data) output = repr(resource) self.assertEqual( "SG Resource: 'testresource', connecting to host 'localhost', port 1883. Has 2 incoming and 5 outgoing topics registered.", output)
def testLoopThreaded(self): on_testresource_command = unittest.mock.Mock(return_value=None) on_remoteservice_data = unittest.mock.Mock() def faulty_callback(): return 1 / 0 with open(self.OUTPUT_FILE_SUBSCRIBER, 'w') as subscriber_outputfile: subcriber = subprocess.Popen(['mosquitto_sub', '-v', '-t', '+/#'], stdout=subscriber_outputfile, stderr=subprocess.STDOUT) resource = sgframework.Resource('testresource', 'localhost') resource.register_incoming_data('remoteservice', 'remotestate', on_remoteservice_data) resource.register_incoming_command('teststate', on_testresource_command) resource.register_outgoing_data('teststate2') resource.on_broker_connectionstatus_info = faulty_callback resource.start(use_threaded_networking=True) time.sleep(1) resource.loop() # Should trigger a warning # Provoke command callback pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/teststate', '-m', 'run' ]) time.sleep(1) pub1.terminate() # Provoke data callback pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate', '-m', 'REMOTE' ]) time.sleep(1) pub1.terminate() # Send data resource.send_data('teststate2', 22) time.sleep(1) resource.send_data('teststateMissing', 51) # Not pre-registered time.sleep(1) # Send commands resource.send_command('remoteservice', 'remotestate2', "run remote") time.sleep(1) # Terminate, and flush files resource.stop() subcriber.kill() time.sleep(0.2) subscriber_outputfile.flush() os.fsync(subscriber_outputfile.fileno()) # Verify callbacks args = on_testresource_command.call_args[0] self.assertEqual(args[1], "command") self.assertEqual(args[2], "testresource") self.assertEqual(args[3], "teststate") self.assertEqual(args[4], "run") self.assertEqual(on_testresource_command.call_count, 1) args = on_remoteservice_data.call_args[0] self.assertEqual(args[1], "data") self.assertEqual(args[2], "remoteservice") self.assertEqual(args[3], "remotestate") self.assertEqual(args[4], "REMOTE") self.assertEqual(on_remoteservice_data.call_count, 1) # Verify that the resource has emitted the MQTT data messages, and availability info with open(self.OUTPUT_FILE_SUBSCRIBER, 'r') as subscriber_outputfile: lines = subscriber_outputfile.readlines() text = ' '.join(lines) self.assertIn("resourceavailable/testresource/presence True", text) self.assertIn("commandavailable/testresource/teststate True", text) self.assertIn("dataavailable/testresource/teststate True", text) self.assertIn("dataavailable/testresource/teststate2 True", text) self.assertIn("data/testresource/teststate run", text) # Echoed command self.assertIn("data/testresource/teststate2 22", text) self.assertIn("command/remoteservice/remotestate2 run remote", text) self.assertIn("resourceavailable/testresource/presence False", text)
def testLoop(self): on_testresource_command = unittest.mock.Mock(return_value=None) on_testresource_command_no_echo = unittest.mock.Mock(return_value=None) on_testresource_command_returnvalue = unittest.mock.Mock( return_value=505) on_connectionstatus = unittest.mock.Mock() on_remoteservice_data = unittest.mock.Mock() on_remoteservice_data_changeonly = unittest.mock.Mock() on_remoteservice_availability = unittest.mock.Mock() def faulty_callback(): return 1 / 0 with open(self.OUTPUT_FILE_SUBSCRIBER, 'w') as subscriber_outputfile: subcriber = subprocess.Popen(['mosquitto_sub', '-v', '-t', '+/#'], stdout=subscriber_outputfile, stderr=subprocess.STDOUT) resource = sgframework.Resource('testresource', 'localhost') resource.register_incoming_data('remoteservice', 'remotestate', on_remoteservice_data) resource.register_incoming_data('remoteservice', 'remotestate3', on_remoteservice_data_changeonly, callback_on_change_only=True) resource.register_incoming_data('remoteservice', 'remotestate4', faulty_callback) resource.register_incoming_availability( 'commandavailable', 'remoteservice', 'remotestate', on_remoteservice_availability) resource.register_incoming_command('teststate2', on_testresource_command, callback_on_change_only=True, defaultvalue=222) resource.register_incoming_command( 'teststate5', on_testresource_command_returnvalue, send_echo_as_retained=True) resource.register_incoming_command('teststate6', on_testresource_command_no_echo, echo=False) resource.register_outgoing_data('teststate3') resource.register_outgoing_data('teststate4', defaultvalue=101) resource.register_outgoing_data('teststate7', send_data_as_retained=True) resource.on_broker_connectionstatus_info = on_connectionstatus resource.start() for i in range(5): resource.loop() time.sleep(0.1) # Send data resource.send_data('teststate3', 31) for i in range(5): resource.loop() time.sleep(0.1) resource.send_data('teststate4', 41) for i in range(5): resource.loop() time.sleep(0.1) resource.send_data('teststate7', 77) for i in range(5): resource.loop() time.sleep(0.1) resource.send_data('teststateMissing', 51) # Not pre-registered # Send commands resource.send_command('remoteservice', 'remotestate2', "RUN NOW!") for i in range(5): resource.loop() time.sleep(0.1) resource.send_command('remoteservice', 'remotestate4', "RUN 4!", send_command_as_retained=True) for i in range(5): resource.loop() time.sleep(0.1) # Provoke availability callback pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'commandavailable/remoteservice/remotestate', '-m', 'True' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() # Provoke data callback pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate', '-m', '61' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate', '-m', '62' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate3', '-m', '71' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate3', '-m', '71' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate3', '-m', '73' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() # Provoke command callback pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/teststate2', '-m', 'Run!' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/teststate2', '-m', 'Run!' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/teststate2', '-m', 'Run!!!!' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/teststate5', '-m', 'Run again!' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/teststate6', '-m', 'R6' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() # Provoke faulty callback pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'data/remoteservice/remotestate4', '-m', '4444' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() # Unregistered input message (is not reaching the callback, as they not are subscribed to) pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/nonregisteredinput', '-m', '0000' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() pub1 = subprocess.Popen([ 'mosquitto_pub', '-t', 'command/testresource/nonregisteredinput/wrong/hierarchy', '-m', '00' ]) for i in range(5): resource.loop() time.sleep(0.1) pub1.terminate() # Terminate, and flush files resource.stop() subcriber.kill() time.sleep(0.2) subscriber_outputfile.flush() os.fsync(subscriber_outputfile.fileno()) # Verify callbacks args = on_connectionstatus.call_args[0] self.assertIn(args[1], [True, False]) self.assertGreaterEqual(on_connectionstatus.call_count, 1) args = on_testresource_command.call_args[0] self.assertEqual(args[1], "command") self.assertEqual(args[2], "testresource") self.assertEqual(args[3], "teststate2") self.assertEqual(args[4], "Run!!!!") self.assertEqual(on_testresource_command.call_count, 2) args = on_testresource_command_returnvalue.call_args[0] self.assertEqual(args[1], "command") self.assertEqual(args[2], "testresource") self.assertEqual(args[3], "teststate5") self.assertEqual(args[4], "Run again!") self.assertEqual(on_testresource_command_returnvalue.call_count, 1) args = on_testresource_command_no_echo.call_args[0] self.assertEqual(args[1], "command") self.assertEqual(args[2], "testresource") self.assertEqual(args[3], "teststate6") self.assertEqual(args[4], "R6") self.assertEqual(on_testresource_command_no_echo.call_count, 1) args = on_remoteservice_availability.call_args[0] self.assertEqual(args[1], "commandavailable") self.assertEqual(args[2], "remoteservice") self.assertEqual(args[3], "remotestate") self.assertEqual(args[4], "True") self.assertEqual(on_remoteservice_availability.call_count, 1) args = on_remoteservice_data.call_args[0] self.assertEqual(args[1], "data") self.assertEqual(args[2], "remoteservice") self.assertEqual(args[3], "remotestate") self.assertEqual(args[4], "62") self.assertEqual(on_remoteservice_data.call_count, 2) args = on_remoteservice_data_changeonly.call_args[0] self.assertEqual(args[1], "data") self.assertEqual(args[2], "remoteservice") self.assertEqual(args[3], "remotestate3") self.assertEqual(args[4], "73") self.assertEqual(on_remoteservice_data_changeonly.call_count, 2) # Verify that the resource has emitted the MQTT data messages, and availability info with open(self.OUTPUT_FILE_SUBSCRIBER, 'r') as subscriber_outputfile: lines = subscriber_outputfile.readlines() text = ' '.join(lines) self.assertIn("resourceavailable/testresource/presence True", text) self.assertIn("commandavailable/testresource/teststate2 True", text) self.assertIn("dataavailable/testresource/teststate2 True", text) self.assertIn("dataavailable/testresource/teststate3 True", text) self.assertIn("dataavailable/testresource/teststate4 True", text) self.assertIn("data/testresource/teststate4 101", text) self.assertIn("data/testresource/teststate2 222", text) self.assertIn("data/testresource/teststate3 31", text) self.assertIn("data/testresource/teststate4 41", text) self.assertIn("data/testresource/teststate7 77", text) self.assertIn("data/testresource/teststate2 Run!", text) # Echo of incoming command self.assertIn("data/testresource/teststate5 505", text) # Modified echo of incoming command self.assertNotIn("data/testresource/teststate6", text) # No echo for incoming command self.assertIn("command/remoteservice/remotestate2 RUN NOW!", text) self.assertIn("resourceavailable/testresource/presence False", text)
def testSendDataBeforeStart(self): resource = sgframework.Resource('testresource', 'localhost') resource.register_outgoing_data('teststate20') with self.assertRaises(ValueError): resource.send_data('teststate20', 123)
def testSendCommandBeforeStart(self): resource = sgframework.Resource('testresource', 'localhost') with self.assertRaises(ValueError): resource.send_command('remoteservice', 'remotesignalname', 123)
def testLoopBeforeStart(self): resource = sgframework.Resource('testresource', 'localhost') self.assertRaises(ValueError, resource.loop)
def testStartTwice(self): resource = sgframework.Resource('testresource', 'localhost') resource.start() resource.start()
def testConstructor(self): resource = sgframework.Resource('testresource', 'localhost') resource.start() time.sleep(0.1) resource.stop()
def init_taxisignservice(): """Initialize the taxi sign service. Returns .. """ ## Parse command line and set output verbosity ## epilog = DESCRIPTIVE_TEXT_TEMPLATE.format(sgframework.Resource.CA_CERTS, sgframework.Resource.CERTFILE, sgframework.Resource.KEYFILE) commandlineparser = argparse.ArgumentParser( epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter) commandlineparser.add_argument( '-v', action='count', default=0, help="Increase verbosity level. Can be repeated.") commandlineparser.add_argument( '-host', default='localhost', help="Broker host name. Defaults to %(default)s.") commandlineparser.add_argument( '-port', default=1883, help="Broker port number. Defaults to %(default)s.") commandlineparser.add_argument( '-cert', help= "Directory for certificate files. Defaults to not using certificates.") commandlineparser.add_argument( '-mode', choices=['hardware', 'commandline', 'graphical'], default='commandline', help= "Type of simulator to use. Depends on graphical display or taxi sign hardware. " + "Defaults to '%(default)s'.") commandline = commandlineparser.parse_args() if commandline.v == 1: loglevel = logging.INFO elif commandline.v >= 2: loglevel = logging.DEBUG else: loglevel = logging.WARNING logging.basicConfig(level=loglevel) ## Initialize taxisign ## logging.info("Initializing the taxi sign in mode: {}".format( commandline.mode)) if commandline.mode == 'hardware': if taxisign_driver is None: raise ImportError( "The driver for the taxi sign hardware is not properly installed." ) sign = taxisign_driver.Taxisign() elif commandline.mode == 'graphical': sign = GraphicalDummyTaxisign() else: sign = CommandlineDummyTaxisign() sign.state = False ## Initialize Secure Gateway resource framework ## resource = sgframework.Resource(RESOURCENAME, commandline.host, commandline.port, commandline.cert) resource.userdata = sign resource.on_broker_connectionstatus_info = on_broker_connectionstatus_info resource.register_incoming_command(TAXISIGN_COMMANDNAME, on_taxisign_state_command, defaultvalue=resource.PAYLOAD_FALSE, echo=True, send_echo_as_retained=True) resource.start() return resource
import time import sgframework def on_taxisign_state_command(resource, messagetype, servicename, commandname, commandpayload): if commandpayload.strip() == 'True': print("Turning on my taxi sign.", flush=True) return 'True' else: print("Turning off my taxi sign.", flush=True) return 'False' resource = sgframework.Resource('taxisignservice', 'localhost') resource.register_incoming_command('state', on_taxisign_state_command, defaultvalue='False', send_echo_as_retained=True) resource.start(use_threaded_networking=True) while True: time.sleep(1)
def init_canadapter(): """Initialize the canadapter. Returns a 'resource' in the sgframework terminology. """ # Parse command line and set output verbosity # epilog = DESCRIPTIVE_TEXT_TEMPLATE.format(sgframework.Resource.CA_CERTS, sgframework.Resource.CERTFILE, sgframework.Resource.KEYFILE) commandlineparser = argparse.ArgumentParser(epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter) commandlineparser.add_argument('kcdfile', help="File name for CAN bus definition (in KCD file format).") commandlineparser.add_argument('-mqttfile', default=None, help="File name for mqtt-and-CAN signal name and permission translation (JSON). " + "Defaults to listen to all CAN signals (no JSON file used).") commandlineparser.add_argument('-v', action='count', default=0, help="Increase verbosity level. Can be repeated.") commandlineparser.add_argument('-version', action='version', version="Version of sgframework is {}. Using can4python {}.". format(sgframework.__version__, can4python.__version__)) commandlineparser.add_argument('-i', dest="interface", default="vcan0", help="CAN interface name. Defaults to '%(default)s'.") commandlineparser.add_argument('-host', default='localhost', help="Broker host name. Defaults to '%(default)s'.") commandlineparser.add_argument('-port', default=1883, help="Broker port number. Defaults to %(default)s.") commandlineparser.add_argument('-qos', type=int, choices=[0, 1, 2], default=0, help="MQTT quality-of-service setting. Defaults to '%(default)s'.") commandlineparser.add_argument('-k', default=10, type=int, dest='keepalive', help="Set keepalive time for MQTT communication to the broker, in seconds. " + "Defaults to %(default)s seconds.") commandlineparser.add_argument('-cert', help="Directory for certificate files. Defaults to not using certificates.") commandlineparser.add_argument('-busname', default=None, help="CAN Bus name in the KCD file. " + "Defaults to use the first busname (alphabetically).") commandlineparser.add_argument('-mqttname', default='canadapter', help="Resource name for MQTT topics. Defaults to '%(default)s'.") commandlineparser.add_argument('-listentoallcan', action='store_true', help="Listen to all CAN signals, and send them on MQTT using the same signalname. " + "Will be overrided by -mqttfile option.") commandlineparser.add_argument('-bcm', action='store_true', help="Use broadcast manager (BCM) for periodic sending of CAN frames by the Linux kernel. " + "Defaults to not using the broadcast manager. See documentation for can4python. " + "Requires Python 3.4 or later.") commandlineparser.add_argument('-t', dest='throttlingtime', default=None, type=int, help="Set throttling time (max update rate) for incoming frames, in milliseconds. " + "Is automatically setting the '-bcm' option. " + "Defaults to not throttle incoming frame rate.") commandlineparser.add_argument('-ego', nargs='+', default=["1"], help="Set ego node id (string), for defining which of the frames in the " + "KCD file should be sent. By default other frames are received. Several ids can be given. " "Defaults to '%(default)s'. " + "See KCD file definition documentation.") commandline = commandlineparser.parse_args() if commandline.v == 1: loglevel = logging.INFO elif commandline.v >= 2: loglevel = logging.DEBUG else: loglevel = logging.WARNING logging.basicConfig(level=loglevel) if commandline.throttlingtime is not None: commandline.bcm = True if commandline.throttlingtime < 0 or \ commandline.throttlingtime > can4python.constants.MAX_FRAME_CYCLETIME_MILLISECONDS: logging.error("Throttling time out ouf range. Given: {} ms".format(commandline.throttlingtime)) exit(EXIT_CODE_WRONG_COMMANDLINE_ARGUMENTS) if commandline.keepalive < 0: logging.error("Keepalive time out ouf range. Given: {} s".format(commandline.keepalive)) exit(EXIT_CODE_WRONG_COMMANDLINE_ARGUMENTS) if commandline.mqttfile is None and not commandline.listentoallcan: logging.error("You must give the translation file name, or the listentoallcan flag.") exit(EXIT_CODE_WRONG_COMMANDLINE_ARGUMENTS) # Set up CAN bus # logging.info(" ") logging.info(" ") logging.info(" ***** Starting canadapter on CAN interface: {!r} *****".format( commandline.interface)) logging.info("CAN file (KCD): {}".format(commandline.kcdfile)) logging.info("Busname in CAN file (KCD): {}".format(commandline.busname)) logging.info("Signaldefinition file CAN-to-MQTT (JSON): {}".format(commandline.mqttfile)) logging.info("MQTT resource name: {}".format(commandline.mqttname)) logging.info(" ") can_config = can4python.FilehandlerKcd.read(filename=commandline.kcdfile, busname=commandline.busname) can_config.ego_node_ids = commandline.ego if commandline.throttlingtime is not None: for frame_id, framedef in can_config.framedefinitions.items(): if not framedef.is_outbound(can_config.ego_node_ids): framedef.throttle_time = commandline.throttlingtime canbus = can4python.CanBus(can_config, interfacename=commandline.interface, timeout=CAN_TIMEOUT, use_bcm=commandline.bcm) logging.debug(canbus.get_descriptive_ascii_art()) # Arrange signal name conversion info # # We know that if no mqtt file is given it implies commandline.listentoallcan. converter = canadapterlib.Converter(can_config, commandline.mqttfile) logging.debug(converter.get_descriptive_ascii_art()) # Initialize Secure Gateway (MQTT) resource framework # resource = sgframework.Resource(commandline.mqttname, commandline.host, commandline.port, commandline.cert) resource.keepalive = commandline.keepalive resource.qos = commandline.qos resource.userdata = (canbus, converter) # Register incoming MQTT commands for args in converter.get_definitions_incoming_mqtt_command(): args['callback'] = on_send_can_data resource.register_incoming_command(**args) # Register outgoing MQTT data for args in converter.get_definitions_outgoing_mqtt_data(): resource.register_outgoing_data(**args) logging.debug(resource.get_descriptive_ascii_art()) resource.start(use_threaded_networking=True) canbus.init_reception() return resource