def dump_handler(duration=2, monitor_mode=0, auto_format=False, protocol=None, baudrate=None, verify=False, file=None, description=None): """ Dumps all messages from OBD bus to screen or file. """ ret = {} # Ensure protocol conn.ensure_protocol(protocol, baudrate=baudrate, verify=verify) # Play sound to indicate recording has begun __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/bleep.wav") try: res = conn.monitor_all(duration=duration, mode=monitor_mode, auto_format=auto_format) finally: # Play sound to indicate recording has ended __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/beep.wav") # Write result to file if specified if file != None: path = abs_file_path(file, "/opt/autopi/obd") __salt__["file.mkdir"](os.path.dirname(path)) protocol = conn.protocol(verify=verify) # Use config parser to write file config_parser = ConfigParser.RawConfigParser(allow_no_value=True) # Add header section config_parser.add_section("header") config_parser.set("header", "timestamp", datetime.datetime.utcnow().isoformat()) config_parser.set("header", "duration", duration) config_parser.set("header", "protocol", protocol["id"]) config_parser.set("header", "baudrate", protocol["baudrate"]) config_parser.set("header", "count", len(res)) if description: config_parser.set("header", "description", description) # Add message lines in data section config_parser.add_section("data") for line in res: config_parser.set("data", line) with open(path, "w") as f: config_parser.write(f) ret["file"] = f.name else: ret["data"] = res return ret
def dump_handler(duration=2, monitor_mode=0, filtering=False, auto_format=False, protocol=None, baudrate=None, verify=False, file=None, description=None): """ Dumps all messages from bus to screen or file. Optional arguments: - duration (int): How many seconds to record data? Default value is '2' seconds. - file (str): Write data to a file with the given name. - description (str): Additional description to the file. - protocol (str): ID of specific protocol to be used to receive the data. If none is specifed the current protocol will be used. - baudrate (int): Specific protocol baudrate to be used. If none is specifed the current baudrate will be used. - verify (bool): Verify that OBD-II communication is possible with the desired protocol? Default value is 'False'. """ ret = {} # Ensure protocol conn.ensure_protocol(protocol, baudrate=baudrate, verify=verify) # Play sound to indicate recording has begun __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/bleep.wav") try: res = conn.monitor(duration=duration, mode=monitor_mode, filtering=filtering, auto_format=auto_format) finally: # Play sound to indicate recording has ended __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/beep.wav") # Write result to file if specified if file != None: path = abs_file_path(file, home_dir) __salt__["file.mkdir"](os.path.dirname(path)) protocol = conn.protocol(verify=verify) # Use config parser to write file config_parser = ConfigParser.RawConfigParser(allow_no_value=True) # Add header section config_parser.add_section("header") config_parser.set("header", "timestamp", datetime.datetime.utcnow().isoformat()) config_parser.set("header", "duration", duration) config_parser.set("header", "protocol", protocol["id"]) config_parser.set("header", "baudrate", protocol["baudrate"]) config_parser.set("header", "count", len(res)) if description: config_parser.set("header", "description", description) # Add message lines in data section config_parser.add_section("data") for line in res: config_parser.set("data", line) with open(path, "w") as f: config_parser.write(f) ret["file"] = f.name else: ret["data"] = res return ret
def start(**settings): try: if log.isEnabledFor(logging.DEBUG): log.debug("Starting OBD manager with settings: {:}".format(settings)) # Give process higher priority - this can lead to process starvation on RPi Zero (single core) psutil.Process(os.getpid()).nice(settings.get("process_nice", -2)) # Setup battery critical level settings battery_critical_level = settings.get("battery_critical_level", {}) context["battery"]["event_thresholds"][battery_util.CRITICAL_LEVEL_STATE] = battery_critical_level.get("duration", 180) context["battery"]["critical_limit"] = battery_critical_level.get("voltage", battery_util.DEFAULT_CRITICAL_LIMIT) if log.isEnabledFor(logging.DEBUG): log.debug("Battery critical limit is {:}V in {:} sec(s)".format(context["battery"]["critical_limit"], context["battery"]["event_thresholds"][battery_util.CRITICAL_LEVEL_STATE])) # Initialize message processor edmp.init(__salt__, __opts__, hooks=settings.get("hooks", []), workers=settings.get("workers", [])) edmp.measure_stats = settings.get("measure_stats", False) # Configure OBD connection conn.on_status = lambda status, data: edmp.trigger_event(data, "system/stn/{:s}".format(status)) conn.on_closing = lambda: edmp.worker_threads.do_all_for("*", lambda t: t.pause(), force_wildcard=True) # Pause all worker threads conn.setup(protocol=settings.get("protocol", {}), **settings["serial_conn"]) # Start ELM327 proxy if configured if "elm327_proxy" in settings: try: # Setup file logging if defined if "logging" in settings["elm327_proxy"]: try: file = abs_file_path(settings["elm327_proxy"]["logging"].pop("file", "elm327_proxy.log"), "/var/log/salt") add_rotating_file_handler_to(elm327_proxy.log, file, **settings["elm327_proxy"]["logging"]) except: log.exception("Failed to setup dedicated file log handler for ELM327 proxy") def on_connect(addr): edmp.trigger_event({"client": "{:}:{:}".format(*addr)}, "system/elm327_proxy/connected") if settings["elm327_proxy"].get("pause_workers", True): threads = edmp.worker_threads.do_all_for("*", lambda t: t.pause()) log.info("Paused {:} worker(s) while ELM327 proxy is in use".format(len(threads))) def on_disconnect(addr): edmp.trigger_event({"client": "{:}:{:}".format(*addr)}, "system/elm327_proxy/disconnected") if conn.is_open() and not proxy._stop: # No reason to run when shutting down if settings["elm327_proxy"].get("reset_after_use", True): connection_handler(reset="cold") log.info("Performed cold reset after ELM327 proxy has been in use") if settings["elm327_proxy"].get("pause_workers", True): threads = edmp.worker_threads.do_all_for("*", lambda t: t.resume()) log.info("Resumed {:} worker(s) after ELM327 proxy has been in use".format(len(threads))) proxy.on_command = _relay_handler proxy.on_connect = on_connect proxy.on_disconnect = on_disconnect proxy.start( host=settings["elm327_proxy"].get("host", "0.0.0.0"), port=settings["elm327_proxy"].get("port", 35000) ) except: log.exception("Failed to start ELM327 proxy") # Run message processor edmp.run() except Exception: log.exception("Failed to start OBD manager") raise finally: log.info("Stopping OBD manager") # First stop ELM327 proxy if running if proxy.is_alive(): try: proxy.stop() except: log.exception("Failed to stop ELM327 proxy") # Then close OBD connection if open if conn.is_open(): try: conn.close() except: log.exception("Failed to close OBD connection")
def dump_handler(duration=1, range=8, rate=50, decimals=4, timestamp=True, sound=True, interrupt_driven=True, file=None): """ Dumps raw XYZ readings to screen or file. Optional arguments: - duration (int): How many seconds to record data? Default value is '1'. - file (str): Write data to a file with the given name. - range (int): Maximum number of g-forces being measured. Default value is '8'. - rate (float): How many Hz (samples per second)? Default value is '50'. - decimals (int): How many decimals to calculate? Default value is '4'. - timestamp (bool): Add timestamp to each sample? Default value is 'True'. - sound (bool): Play sound when starting and stopping recording? Default value is 'True'. - interrupt_driven (bool): Await hardware data ready signal before reading a sample? Default value is 'True'. """ # Validate input if duration > 300: raise ValueError("Maximum duration is 300 seconds") if duration * rate > 100 and file == None: raise ValueError( "Too much data to return - please adjust parameters 'duration' and 'rate' or instead specify a file to write to" ) file_path = abs_file_path(file, "/opt/autopi/acc", ext="json") if file != None else None if file_path != None and os.path.isfile(file_path): raise ValueError("File already exists: {:}".format(file_path)) ret = { "range": range, "rate": rate, "decimals": decimals, } if interrupt_driven: ret["interrupt_timeouts"] = 0 data = [] # Remember settings to restore when done orig_range = conn._range orig_rate = conn._data_rate try: # Play sound to indicate recording has begun if sound: __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/bleep.wav") # Apply specified settings conn.configure(range=range, data_rate=rate) # Run for specified duration start = timer() stop = start + duration while timer() < stop: if interrupt_driven: # Wait for data ready interrupt if not already set if not interrupt_event.wait(timeout=2 / conn._data_rate): ret["interrupt_timeouts"] += 1 interrupt_event.clear() res = conn.xyz(decimals=decimals) if timestamp: res["_stamp"] = datetime.datetime.utcnow().isoformat() data.append(res) ret["duration"] = timer() - start ret["samples"] = len(data) finally: # Restore original settings conn.configure(range=orig_range, data_rate=orig_rate) # Play sound to indicate recording has ended if sound: __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/beep.wav") # Write data to file if requested if file_path != None: # Ensure folders are created __salt__["file.mkdir"](os.path.dirname(file_path)) with open(file_path, "w") as f: res = ret.copy() res["data"] = data json.dump(res, f, indent=4) ret["file"] = f.name else: ret["data"] = data return ret
def dump_handler(duration=1, range=8, rate=50, decimals=4, timestamp=True, sound=True, interrupt_driven=False, file=None): """ Dumps raw XYZ readings to screen or file. """ # Validate input if duration > 300: raise ValueError("Maximum duration is 300 seconds") if duration * rate > 100 and file == None: raise ValueError( "Too much data to return - please adjust parameters 'duration' and 'rate' or instead specify a file to write to" ) file_path = abs_file_path(file, "/opt/autopi/acc", ext="json") if file != None else None if file_path != None and os.path.isfile(file_path): raise ValueError("File already exists: {:}".format(file_path)) ret = { "range": range, "rate": rate, "decimals": decimals, } if interrupt_driven: ret["interrupt_timeouts"] = 0 data = [] # Remember settings to restore when done orig_range = conn._range orig_rate = conn._data_rate try: # Play sound to indicate recording has begun if sound: __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/bleep.wav") # Apply specified settings conn.config({ "range": range, "data_rate": rate, }) # Run for specified duration start = timer() stop = start + duration while timer() < stop: if interrupt_driven: # Wait for data ready interrupt if not already set if not interrupt_event.wait(timeout=2 / conn._data_rate): ret["interrupt_timeouts"] += 1 interrupt_event.clear() res = conn.xyz(decimals=decimals) if timestamp: res["_stamp"] = datetime.datetime.utcnow().isoformat() data.append(res) ret["duration"] = timer() - start ret["samples"] = len(data) finally: # Restore original settings conn.config({ "range": orig_range, "data_rate": orig_rate, }) # Play sound to indicate recording has ended if sound: __salt__["cmd.run"]("aplay /opt/autopi/audio/sound/beep.wav") # Write data to file if requested if file_path != None: # Ensure folders are created __salt__["file.mkdir"](os.path.dirname(file_path)) with open(file_path, "w") as f: res = ret.copy() res["data"] = data json.dump(res, f, indent=4) ret["file"] = f.name else: ret["data"] = data return ret