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)
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)
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()
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)
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")