Example #1
0
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
Example #2
0
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
Example #3
0
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")
Example #4
0
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
Example #5
0
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