""" Autodoc functions """ import logging import os import sys __PATH__ = "." def __sig__(obj, label, type): return (obj, label, type) def __edge__(src, trg): return (src, trg) def __sig_name__(name): return name.replace(".", "_") def __type_bytes__(mtype): if mtype.startswith("uint"): return int(mtype[4:]) // 8 if mtype.startswith("int"): return int(mtype[3:]) // 8 if mtype.startswith("float"): return int(mtype[5:]) // 8 if mtype == "bool": return 1 logging.debug(f"Type `{mtype}` unknow, size 1 byte") return 1 def __type_size__(mtype): return __type_bytes__(mtype[0]) * mtype[1] def __num_bytes__(sig): noe = 1 if "NumberOfElements" not in sig else int(sig["NumberOfElements"]) nod = 1 if "NumberOfDimensions" not in sig else int(sig["NumberOfDimensions"]) bytes = __type_bytes__(sig["Type"]) return noe * nod * bytes def __doc_app_state__(state, tab=" │ "): res = "" if "+Threads" in state: for i, tname in enumerate(state["+Threads"]): if tname[0] == "+": ch = "├─●" if i < len(state["+Threads"]) - 1 else "╰─●" res += f"{tab} {ch} {tname[1:]}\n" return res def __doc_obj__(label, obj, id): node = f' {id} [label="{label}\n{obj["Class"]} | ' return node def __sig_type__(sig): type_name = sig["Type"] noe = sig["NumberOfElements"] if "NumberOfElements" in sig else 1 nod = sig["NumberOfDimensions"] if "NumberOfDimensions" in sig else 1 dim = int(noe) * int(nod) return (type_name, dim) def __sig_alias__(name, signal): return name if "Alias" not in signal else signal["Alias"] def __doc_common_gam__(name, obj, edges): inputs = "" outputs = "" if "InputSignals" in obj: for i, (l, sig) in enumerate(obj["InputSignals"].items()): alias = __sig_alias__(l, sig) mtype = __sig_type__(sig) datasrc = sig["DataSource"] edges.append( __edge__( __sig__(datasrc, alias, mtype), __sig__(name, l, mtype), ) ) if i != 0: inputs += " | " inputs += f" {l}" if "OutputSignals" in obj: for i, (l, sig) in enumerate(obj["OutputSignals"].items()): alias = __sig_alias__(l, sig) mtype = __sig_type__(sig) datasrc = sig["DataSource"] edges.append( __edge__( __sig__(name, l, mtype), __sig__(datasrc, alias, mtype), ) ) if i != 0: outputs += " | " outputs += f" {l}" return "{ {" + inputs + " } | { " + outputs + "} }", edges def __doc_triggered_iogam__(name, obj, edges): node = "" if "InputSignals" in obj: for i, (l, sig) in enumerate(obj["InputSignals"].items()): alias = __sig_alias__(l, sig) mtype = __sig_type__(sig) datasrc = sig["DataSource"] edges.append( __edge__( __sig__(datasrc, alias, mtype), __sig__(name, l, mtype), ) ) if i > 1: node += " | " node += f" " if i == 0: node += "Trigger | { { " else: node += f"{l}" node += " } | { " if "OutputSignals" in obj: for i, (l, sig) in enumerate(obj["OutputSignals"].items()): alias = __sig_alias__(l, sig) mtype = __sig_type__(sig) datasrc = sig["DataSource"] edges.append( __edge__( __sig__(name, l, mtype), __sig__(datasrc, alias, mtype), ) ) if i != 0: node += " | " node += f" {l} " node += " } }" return node, edges def __doc_iogam__(id, obj, edges): node = "" if "InputSignals" in obj and "OutputSignals" in obj: inputs, outputs = obj["InputSignals"], obj["OutputSignals"] output_names = list(outputs.keys()) input_names = list(inputs.keys()) j = 0 isize = 0 osize = 0 i = 0 irow = "" orow = "" while i < len(inputs): in_label = input_names[i] input = inputs[in_label] if irow: irow += " | " irow += f" {in_label}" isize += __num_bytes__(input) i += 1 alias = __sig_alias__(in_label, input) mtype = __sig_type__(input) datasrc = input["DataSource"] edges.append( __edge__( __sig__(datasrc, alias, mtype), __sig__(id, in_label, mtype), ) ) while osize < isize and j < len(outputs): out_label = output_names[j] output = outputs[out_label] osize += __num_bytes__(output) if orow: orow += " | " orow += f" {out_label}" j += 1 alias = __sig_alias__(out_label, output) mtype = __sig_type__(output) datasrc = output["DataSource"] edges.append( __edge__( __sig__(id, out_label, mtype), __sig__(datasrc, alias, mtype), ) ) if isize == osize and irow and orow: if node: node += " | " node += "{ {" + irow + "} | {" + orow + "} }" irow = "" orow = "" if irow or orow: if node: node += " | " node += "{ {" + irow + "} | {" + orow + "} }" return node, edges def __doc_constgam__(id, obj, edges): node = "" if "OutputSignals" in obj: for i, (l, sig) in enumerate(obj["OutputSignals"].items()): datasrc = sig["DataSource"] alias = __sig_alias__(l, sig) mtype = __sig_type__(sig) edges.append( __edge__(__sig__(id, l, mtype), __sig__(datasrc, alias, mtype)) ) if i != 0: node += " | " node += f' {sig["Default"]} ' node += "" return node, edges def __doc_gam__(label, obj, edges): id = f"fn{label}" cls = obj["Class"] node = f' {id} [label="{label}\n{obj["Class"]} | ' if cls == "ConstantGAM": sigs, datasources = __doc_constgam__(id, obj, edges) elif cls == "IOGAM": sigs, datasources = __doc_iogam__(id, obj, edges) elif cls == "TriggeredIOGAM": sigs, datasources = __doc_triggered_iogam__(id, obj, edges) else: sigs, datasources = __doc_common_gam__(id, obj, edges) if sigs: node += sigs + '"];' else: node = "" return node, datasources def __process_gamds__(name, signals): node = "" for i, sig_name in enumerate(signals): sig_type = signals[sig_name] node += f'{name}_{__sig_name__(sig_name)}[ label=" {name}::{sig_name} ({sig_type[0]}[{sig_type[1]}])", color=blue];\n' return node def __process_ds__(name, obj, edges): cls = obj["Class"] sigs = {} node = "" for edge in edges: if edge[0][0] == name: sigs[edge[0][1]] = edge[0][2] if edge[1][0] == name: sigs[edge[1][1]] = edge[1][2] if sigs: if cls == "GAMDataSource": node = __process_gamds__(name, sigs) else: order = [] if "Signals" in obj: for sig in obj["Signals"]: if sig in sigs: order.append(sig) else: order = list(sigs.keys()) node = f'{name} [label="{name}\n{cls} | ' for i, sig_name in enumerate(order): sig_type = sigs[sig_name] if i > 0: node += " | " node += f"<{__sig_name__(sig_name)}> {sig_name} ({sig_type[0]}[{sig_type[1]}])" node += '", color=blue];\n' return node def __process_edges__(edges, datasources): arrows = "" for source, target in edges: src_obj = source[0] src_sig = __sig_name__(source[1]) trg_obj = target[0] trg_sig = __sig_name__(target[1]) a = f"{src_obj}" b = f"{trg_obj}" if src_obj in datasources: if datasources[src_obj] == "GAMDataSource": a += f"_{src_sig}" else: a += f":{src_sig}" else: a += f":out_{src_sig}" if trg_obj in datasources: if datasources[trg_obj] == "GAMDataSource": b += f"_{trg_sig}" else: b += f":{trg_sig}" else: b += f":in_{trg_sig}" arrows += f"{a} -> {b};\n" return arrows def __doc_state__(label, state, app): mdstate = "" graph = f"digraph {label[1:]}" + "{\n" graph += "grapn [ranksep=4];\n" graph += "rankdir = LR;\n" graph += "beautify = true;\n" graph += "node [shape=record];\n" graph += "\n" arrows = "" ext = "" edges = [] datasources = {} if "+Threads" in state: for tname, obj in state["+Threads"].items(): if tname[0] == "+": # graph += f"subgraph cluster_{tname[1:]}" + "{\n" if "Functions" in obj: fns = obj["Functions"] for fn in fns: obj = app["+Functions"][f"+{fn}"] node, edges = __doc_gam__(fn, obj, edges) graph += f"{node}\n" # graph += f' label = "{tname[1:]}";\n' # graph += "}\n" if "+Data" in app: for key, obj in app["+Data"].items(): if key[0] == "+": datasources[key[1:]] = obj["Class"] node = __process_ds__(key[1:], obj, edges) graph += node arrows = __process_edges__(edges, datasources) graph += "\n" + ext + "\n" + arrows graph += "}\n" fname = f"{label[1:]}" with open(os.path.join(__PATH__, f"{fname}.dot"), "w") as file: file.write(graph) os.system( f"dot -Tsvg -o{os.path.join(__PATH__, fname)}.svg {os.path.join(__PATH__, fname)}.dot" ) mdstate += f"![{fname}]({fname}.svg)\n" return mdstate def __doc_application__(label, app): mdapp = f'\n## Real-time application "{label[1:]}"\n\n' mdapp += f"Below are descrbed the states and threads of the real-time application {label[1:]}.\n" mdapp += "```\n" mdapp += f'╭{"─"*(len(label)-1)}╮\n' mdapp += f"│{label[1:]}│\n" mdapp += f'╰┬{"─"*(len(label)-2)}╯\n' states = app["+States"] for i, (label, obj) in enumerate(states.items()): ch = "├─▷" if i < len(states) - 1 else "╰─▷" tab = " │ " if i < len(states) - 1 else " " if label[0] == "+": mdapp += f" {ch} {label[1:]}\n" mdapp += __doc_app_state__(obj, tab) mdapp += "```\n" for label, obj in states.items(): if label[0] == "+": mdapp += f'\n### State "{label[1:]}"\n' mdapp += __doc_state__(label, obj, app) return mdapp def document(fname, args, config): """Document MARTe object.""" global __PATH__ __PATH__ = os.path.join(args.destination, fname) if not os.path.isdir(__PATH__): os.mkdir(__PATH__) mddoc = f"# {fname}\n" for label, obj in config.items(): if "Class" in obj: if obj["Class"] == "RealTimeApplication": mddoc += __doc_application__(label, obj) if obj["Class"] == "StateMachine": mddoc += f'\n## State-Machine "{label[1:]}"' with open(os.path.join(__PATH__, f"{fname}.md"), "w") as file: file.write(mddoc)