Example #1
0
def create_persistence_objects(server, max_update_interval,
                               min_update_interval):
    env_var_db = server[ENVIRONMENTAL_DATA_POINT]
    module_db = server[FIRMWARE_MODULE]
    module_type_db = server[FIRMWARE_MODULE_TYPE]
    modules = {
        module_id: FirmwareModule(module_db[module_id])
        for module_id in module_db if not module_id.startswith('_')
    }
    module_types = {
        type_id: FirmwareModuleType(module_type_db[type_id])
        for type_id in module_type_db if not type_id.startswith("_")
    }
    modules = synthesize_firmware_module_info(modules, module_types)
    valid_vars = list(EnvVar.items.keys())
    for module_id, module_info in modules.items():
        for output_name, output_info in module_info["outputs"].items():
            if not output_info["variable"] in valid_vars:
                rospy.logwarn(
                    "Encountered a module output that references a "
                    'non-existant variable: Output "%s" of module "%s"',
                    output_name, module_id)
                continue
            topic = "/sensors/{}/{}/filtered".format(module_id, output_name)
            topic_type = resolve_message_type(output_info["type"])
            TopicPersistence(topic=topic,
                             topic_type=topic_type,
                             environment=module_info["environment"],
                             variable=output_info["variable"],
                             is_desired=False,
                             db=env_var_db,
                             max_update_interval=max_update_interval,
                             min_update_interval=min_update_interval)
def filter_all_topics(module_db, module_type_db):
    modules = {
        module_id: FirmwareModule(module_db[module_id])
        for module_id in module_db if not module_id.startswith('_')
    }
    module_types = {
        type_id: FirmwareModuleType(module_type_db[type_id])
        for type_id in module_type_db if not type_id.startswith("_")
    }
    modules = synthesize_firmware_module_info(modules, module_types)
    for module_id, module_info in modules.items():
        for output_name, output_info in module_info["outputs"].items():
            src_topic = "/sensors/{}/{}/raw".format(module_id, output_name)
            dest_topic = "/sensors/{}/{}/filtered".format(
                module_id, output_name)
            topic_type = get_message_class(output_info["type"])
            filter_topic(src_topic, dest_topic, topic_type)
Example #3
0
def update_module_types():
    """
    Download the repositories for all of the firmware_module_type records and
    update them using the `module.json` files from the repositories themselves.
    Currently only works for git repositories.
    """
    local_url = config["local_server"]["url"]
    server = Server(local_url)
    db = server[FIRMWARE_MODULE_TYPE]
    temp_folder = mkdtemp()
    for _id in db:
        if _id.startswith("_"):
            continue
        obj = db[_id]
        new_obj = update_record(FirmwareModuleType(obj), temp_folder)
        new_obj["_rev"] = obj["_rev"]
        if new_obj != obj:
            db[_id] = new_obj
    rmtree(temp_folder)
def connect_all_topics(module_db, module_type_db):
    modules = {
        module_id: FirmwareModule(module_db[module_id])
        for module_id in module_db if not module_id.startswith('_')
    }
    module_types = {
        type_id: FirmwareModuleType(module_type_db[type_id])
        for type_id in module_type_db if not type_id.startswith("_")
    }
    modules = synthesize_firmware_module_info(modules, module_types)
    for module_id, module_info in modules.items():
        for input_name, input_info in module_info["inputs"].items():
            if not "actuators" in input_info["categories"]:
                continue
            src_topic = "/environments/{}/{}/commanded".format(
                module_info["environment"], input_info["variable"])
            dest_topic = "/actuators/{}/{}".format(module_id, input_name)
            dest_topic_type = get_message_class(input_info["type"])
            src_topic_type = Float64
            connect_topics(src_topic,
                           dest_topic,
                           src_topic_type,
                           dest_topic_type,
                           multiplier=input_info.get("multiplier", 1),
                           deadband=input_info.get("deadband", 0))
        for output_name, output_info in module_info["outputs"].items():
            if not "sensors" in output_info["categories"]:
                continue
            src_topic = "/sensors/{}/{}/raw".format(module_id, output_name)
            dest_topic = "/environments/{}/{}/raw".format(
                module_info["environment"], output_info["variable"])
            src_topic_type = get_message_class(output_info["type"])
            dest_topic_type = Float64
            connect_topics(src_topic, dest_topic, src_topic_type,
                           dest_topic_type)
            src_info_topic = "/sensors/{}/{}/info".format(
                module_id, output_name)
            dest_info_topic = "/environments/{}/{}/info".format(
                module_info["environment"], output_info["variable"])
            connect_sensor_info_topics(src_info_topic, dest_info_topic)
Example #5
0
def connect_all_topics(module_db, module_type_db):
    modules = {
        module_id: FirmwareModule(module_db[module_id])
        for module_id in module_db if not module_id.startswith('_')
    }
    module_types = {
        type_id: FirmwareModuleType(module_type_db[type_id])
        for type_id in module_type_db if not type_id.startswith("_")
    }
    modules = synthesize_firmware_module_info(modules, module_types)
    for module_id, module_info in modules.items():
        for input_name, input_info in module_info["inputs"].items():
            if not "actuators" in input_info["categories"]:
                continue
            src_topic = "/{}/{}".format(module_info["environment"],
                                        input_info["variable"])
            dest_topic = "/actuators/{}/{}".format(module_id, input_name)
            dest_topic_type = resolve_message_type(input_info["type"])
            src_topic_type = Float64
            connect_topics(src_topic,
                           dest_topic,
                           src_topic_type,
                           dest_topic_type,
                           multiplier=input_info.get("direction", 1),
                           threshold=input_info.get("threshold", 0))
        for output_name, output_info in module_info["outputs"].items():
            if not "sensors" in output_info["categories"]:
                continue
            src_topic = "/sensors/{}/{}/filtered".format(
                module_id, output_name)
            dest_topic = "/{}/measured/{}".format(module_info["environment"],
                                                  output_info["variable"])
            src_topic_type = resolve_message_type(output_info["type"])
            dest_topic_type = Float64
            connect_topics(src_topic, dest_topic, src_topic_type,
                           dest_topic_type)

if __name__ == '__main__':
    rospy.init_node("sensor_info_publisher")

    # Connect to the DB server
    db_server = cli_config["local_server"]["url"]
    if not db_server:
        raise RuntimeError("No local server specified")
    server = Server(db_server)

    # Get info on all of the modules
    module_db = server[FIRMWARE_MODULE]
    module_type_db = server[FIRMWARE_MODULE_TYPE]
    modules = {
        module_id: FirmwareModule(module_db[module_id])
        for module_id in module_db if not module_id.startswith('_')
    }
    module_types = {
        type_id: FirmwareModuleType(module_type_db[type_id])
        for type_id in module_type_db if not type_id.startswith('_')
    }
    modules = synthesize_firmware_module_info(modules, module_types)

    for module_id, module_info in modules.items():
        for output_name, output_info in module_info["outputs"].items():
            if not "sensors" in output_info["categories"]:
                continue
            publish_sensor_info(module_id, output_name, output_info)
    rospy.spin()
    pub.publish(msg)

if __name__ == '__main__':
    rospy.init_node("sensor_info_publisher")

    # Connect to the DB server
    db_server = cli_config["local_server"]["url"]
    if not db_server:
        raise RuntimeError("No local server specified")
    server = Server(db_server)

    # Get info on all of the modules
    firmware_module = rospy.get_param("/firmware_module")
    firmware_module_type = rospy.get_param("/firmware_module_type")
    modules = {
        record["_id"]: FirmwareModule(record) for record in
        firmware_module if not record["_id"].startswith('_')
    }
    module_types = {
        record["_id"]: FirmwareModuleType(record) for record in
        firmware_module_type if not record["_id"].startswith('_')
    }
    modules = synthesize_firmware_module_info(modules, module_types)

    for module_id, module_info in modules.items():
        for output_name, output_info in module_info["outputs"].items():
            if not "sensors" in output_info["categories"]:
                continue
            publish_sensor_info(module_id, output_name, output_info)
    rospy.spin()
Example #8
0
def run_module(ctx, arguments, project_dir, board, **kwargs):
    """
    Run a single instance of this module. [ARGUMENTS] specifies a list of
    implementation-specific arguments to the module (for example, configuring
    Arduino pin numbers for the module).

    Example:

    \b
    openag firmware run_module -t upload 4

    This command fetches module definitions from CouchDB. CouchDB must be
    running on port 5984 and the firmware_module_type database populated with
    appropriate type records for this command to work. Loading the default
    fixture from openag_brain will populate a default set of
    firmware_module_type records.
    """
    # Read the module config
    here = os.path.abspath(project_dir)
    module_json_path = os.path.join(here, "module.json")
    try:
        with open(module_json_path) as f:
            doc = json.load(f)
            if not doc.get("_id"):
                # Patch in id if not present
                doc["_id"] = parent_dirname(module_json_path)
            module_type = FirmwareModuleType(doc)
    except IOError:
        raise click.ClickException("No module.json file found")

    # Create the build directory
    build_path = os.path.join(here, "_build")
    if not os.path.isdir(build_path):
        os.mkdir(build_path)
    kwargs["project_dir"] = build_path

    # Initialize an openag project in the build directory
    ctx.invoke(init, board=board, **kwargs)

    # Link the source files into the lib directory
    lib_path = os.path.join(build_path, "lib")
    module_path = os.path.join(lib_path, "module")
    if not os.path.isdir(module_path):
        os.mkdir(module_path)
    for file_name in os.listdir(here):
        file_path = os.path.join(here, file_name)
        if not os.path.isfile(file_path) or file_name.startswith("."):
            continue
        source = "../../../{}".format(file_name)
        link_name = os.path.join(module_path, file_name)
        if os.path.isfile(link_name):
            os.remove(link_name)
        os.symlink(source, link_name)

    # Parse the arguments based on the module type
    real_args = []
    for i in range(len(arguments)):
        if i >= len(module_type["arguments"]):
            raise click.ClickException(
                "Too many module arguments specified. (Got {}, expected "
                "{})".format(len(arguments), len(module_type["arguments"])))
        val = arguments[i]
        arg_info = module_type["arguments"][i]
        if arg_info["type"] == "int":
            val = int(val)
        elif arg_info["type"] == "float":
            val = float(val)
        elif arg_info["type"] == "bool":
            if val.lower() == "true":
                val = True
            elif val.lower() == "false":
                val = False
            else:
                raise click.BadParameter(
                    "Argument number {} should be a boolean value "
                    '("true" or "false")'.format(i))
        real_args.append(val)

    # Write the modules.json file
    modules = {
        FIRMWARE_MODULE: [
            FirmwareModule({
                "_id": "module_1",
                "type": "module",
                "arguments": list(real_args)
            })
        ]
    }
    modules_file = os.path.join(build_path, "modules.json")
    with open(modules_file, "w") as f:
        json.dump(modules, f)
    with open(modules_file, "r") as f:
        kwargs["modules_file"] = f
        # Run the project
        ctx.invoke(run, **kwargs)
Example #9
0
def run(categories, modules_file, project_dir, plugin, target,
        status_update_interval):
    """ Generate code for this project and run it """
    project_dir = os.path.abspath(project_dir)

    # Make sure the project has been initialized
    pio_config = os.path.join(project_dir, "platformio.ini")
    if not os.path.isfile(pio_config):
        raise click.ClickException(
            "Not an OpenAg firmware project. To initialize a new project "
            "please use the `openag firmware init` command")

    firmware_types = []
    firmware = []

    local_server = config["local_server"]["url"]
    server = Server(local_server) if local_server else None
    modules_json = json.load(modules_file) if modules_file else {}

    if modules_json.get(FIRMWARE_MODULE_TYPE):
        for module in modules_json[FIRMWARE_MODULE_TYPE]:
            click.echo(
                "Parsing firmware module type \"{}\" from modules file".format(
                    module["_id"]))
            firmware_types.append(FirmwareModuleType(module))
    elif server:
        db = server[FIRMWARE_MODULE_TYPE]
        for _id in db:
            if _id.startswith("_"):
                continue
            click.echo(
                "Parsing firmware module type \"{}\" from server".format(_id))
            firmware_types.append(FirmwareModuleType(db[_id]))

    # Check for working modules in the lib folder
    # Do this second so project-local values overwrite values from the server
    lib_path = os.path.join(project_dir, "lib")
    for dir_name in os.listdir(lib_path):
        dir_path = os.path.join(lib_path, dir_name)
        if not os.path.isdir(dir_path):
            continue
        config_path = os.path.join(dir_path, "module.json")
        if os.path.isfile(config_path):
            with open(config_path) as f:
                click.echo("Parsing firmware module type \"{}\" from lib "
                           "folder".format(dir_name))
                doc = json.load(f)
                if not doc.get("_id"):
                    # Patch in id if id isn't present
                    doc["_id"] = parent_dirname(config_path)
                firmware_types.append(FirmwareModuleType(doc))

    # Get the list of modules
    if modules_json.get(FIRMWARE_MODULE):
        for module in modules_json[FIRMWARE_MODULE]:
            click.echo(
                "Parsing firmware module \"{}\" from modules file".format(
                    module["_id"]))
            firmware.append(FirmwareModule(module))
    elif server:
        db = server[FIRMWARE_MODULE]
        for _id in db:
            if _id.startswith("_"):
                continue
            click.echo(
                "Parsing firmware module \"{}\" from server".format(_id))
            firmware.append(FirmwareModule(db[_id]))

    if len(firmware) == 0:
        click.echo("Warning: no modules specified for the project")

    module_types = index_by_id(firmware_types)
    modules = index_by_id(firmware)
    # Synthesize the module and module type dicts
    modules = synthesize_firmware_module_info(modules, module_types)
    # Update the module inputs and outputs using the categories
    modules = prune_unspecified_categories(modules, categories)

    # Generate src.ino
    src_dir = os.path.join(project_dir, "src")
    src_file_path = os.path.join(src_dir, "src.ino")
    # Load the plugins
    plugin_fns = (load_plugin(plugin_name) for plugin_name in plugin)
    # Run modules through each plugin
    plugins = [plugin_fn(modules) for plugin_fn in plugin_fns]

    # Generate the code
    codegen = CodeGen(modules=modules,
                      plugins=plugins,
                      status_update_interval=status_update_interval)
    pio_ids = (dep["id"] for dep in codegen.all_pio_dependencies())
    for _id in pio_ids:
        subprocess.call(["platformio", "lib", "install", str(_id)])
    lib_dir = os.path.join(project_dir, "lib")
    for dep in codegen.all_git_dependencies():
        url = dep["url"]
        branch = dep.get("branch", "master")
        dep_folder_name = make_dir_name_from_url(url)
        dep_folder = os.path.join(lib_dir, dep_folder_name)
        if os.path.isdir(dep_folder):
            click.echo('Updating "{}"'.format(dep_folder_name))
            subprocess.call(["git", "checkout", "--quiet", branch],
                            cwd=dep_folder)
            subprocess.call(["git", "pull"], cwd=dep_folder)
        else:
            click.echo('Downloading "{}"'.format(dep_folder_name))
            subprocess.call(["git", "clone", "-b", branch, url, dep_folder],
                            cwd=lib_dir)
    with open(src_file_path, "w+") as f:
        codegen.write_to(f)

    # Compile the generated code
    command = ["platformio", "run"]
    if target:
        command.append("-t")
        command.append(target)
    env = os.environ.copy()
    build_flags = []
    for c in categories:
        build_flags.append("-DOPENAG_CATEGORY_{}".format(c.upper()))
    env["PLATFORMIO_BUILD_FLAGS"] = " ".join(build_flags)
    if subprocess.call(command, cwd=project_dir, env=env):
        raise click.ClickException("Compilation failed")