Source code for fcp.validator

"""c-generator.

Usage:
  validator.py validate <json>
  validator.py (-h | --help)
  validator.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
"""

import json
import logging

from .spec import *


check_table = {
    "sig": {},
    "msg": {},
    "dev": {},
    "spec": {},
    "cmd": {},
    "cfg": {},
    "log": {},
}


[docs]def check_decorator(category): def closure(f): if category not in check_table.keys(): print(f"{category} category not available") return check_table[category][f.__name__] = f return closure
[docs]def fail_msg(node, msg): def location(node): if node.parent is None: return node.name return location(node.parent) + "/" + node.name return f"{location(node)}: {msg}"
@check_decorator("sig") def signal_length(signal): if signal.length > 64 or signal.length <= 0: return fail_msg(signal, f"signal length is {signal.length}") @check_decorator("sig") def signal_start(signal): if signal.start > 63 or signal.start < 0: return fail_msg(signal, f"signal start is {signal.start}") @check_decorator("sig") def signal_total_length(signal): if signal.length + signal.offset > 64: return fail_msg(signal, f"signal end is bigger than 64") @check_decorator("sig") def signal_type(signal): types = ["unsigned", "signed", "double", "float"] if signal.type not in types: return fail_msg( signal, f"signal type is {signal.type}. Valid types are: {types}" ) if signal.type == "float" and signal.length != 32: return fail_msg(signal, f"signal is float but length is {signal.length} != 32") if signal.type == "double" and signal.length != 64: return fail_msg(signal, f"signal is double but length is {signal.length} != 64") @check_decorator("sig") def signal_endianess(signal): endianess = ["little_endian", "big_endian"] if signal.byte_order not in endianess: return fail_msg( signal, f"signal endianess not supported. Valid values are: {endianess}" ) @check_decorator("sig") def signal_name(signal): if not signal.name.isidentifier(): return fail_msg(signal, f"{signal.name} is not a valid identifier") @check_decorator("sig") def signal_scaling(signal): if signal.scale == 1 and signal.offset != 0: return fail_msg( signal, f"{signal.name}: signal offset is different than 0 for a scaling of 1", ) if signal.scale != 1 and signal.length > 32: return fail_msg( signal, f"scaling is not supported for variables bigger than 32 bits" ) @check_decorator("sig") def signal_mux(signal): if signal.mux == 0: return fail_msg(signal, f"Mux count is 0, should be >= 1") @check_decorator("sig") def signal_mux_count(sig): if int(sig.mux_count) == 0: return fail_msg(sig, f"Mux count *cannot* be 0") @check_decorator("msg") def msg_mux(msg): muxeds = [signal.mux for signal in msg.signals.values() if signal.mux != ""] if len(muxeds) == 0: return mux = msg.signals.get(muxeds[0]) if mux == None: return fail_msg(msg, f"Cannot find mux signal in multiplexed message") if 2 ** mux.length < mux.mux_count: return fail_msg(msg, f"Mux cannot fit all possible multiplexed values") @check_decorator("msg") def msg_name(msg): if not msg.name.isidentifier(): return fail_msg(msg, f"Message name is not a valid identifier") @check_decorator("msg") def msg_id(msg): if msg.id > 64 or msg.id < 0: return fail_msg(msg, f"Message id is not valid: {msg.id}") @check_decorator("msg") def msg_dlc(msg): if msg.dlc > 8 or msg.dlc < 0: return fail_msg(msg, f"Message dlc is not valid: {msg.dlc}") @check_decorator("msg") def msg_frequency(msg): if msg.frequency < 0: return fail_msg(msg, f"Message frequency is not valid: {msg.frequency}") @check_decorator("msg") def msg_overlapping_signals(msg): values = [] for signal in msg.signals.values(): values += list(range(signal.start, signal.start + signal.length)) s = set(values) if len(s) != len(values): return fail_msg(msg, f"Message has overlapping signals") @check_decorator("dev") def dev_name(dev): if not dev.name.isidentifier(): return fail_msg(dev, f"Device name is not a valid identifier") @check_decorator("dev") def dev_id(dev): if dev.id > 31 or dev.id < 0: return fail_msg(dev, f"Device id is not valid: {dev.id}") @check_decorator("dev") def dev_overlapping_msg_ids(dev): ids = [msg.id for msg in dev.msgs.values()] ids_set = set(ids) if len(ids_set) != len(ids): return fail_msg(dev, f"Device has overlapping msg ids") @check_decorator("log") def log_id(log): if log.id > 255 and log.id < 0: return fail_msg(log, f"Log id is not valid: {log.id}") @check_decorator("log") def log_n_args(log): if log.n_args > 3 or log.n_args < 0: return fail_msg(log, f"Log n args is not valid: {log.n_args}") @check_decorator("log") def log_name(log): if not log.name.isidentifier(): return fail_msg(log, f"Log name is not valid: {log.name}") @check_decorator("cfg") def cfg_name(cfg): if not cfg.name.isidentifier(): return fail_msg(cfg, f"Cfg name is not valid: {cfg.name}") @check_decorator("cfg") def cfg_id(cfg): if cfg.id > 255 and cfg.id < 0: return fail_msg(cfg, f"Cfg id is not valid: {cfg.id}") @check_decorator("cmd") def cmd_name(cmd): if not cmd.name.isidentifier(): return fail_msg(cmd, f"Cmd name is not valid: {cmd.name}") @check_decorator("cmd") def cmd_id(cmd): if cmd.id > 255 and cmd.id < 0: return fail_msg(cmd, f"Cmd id is not valid: {cmd.id}") @check_decorator("cmd") def cmd_n_args(cmd): if cmd.n_args > 3 and cmd.n_args < 0: return fail_msg(cmd, f"Cmd n args is not valid: {cmd.n_args}") @check_decorator("spec") def spec_overlapping_dev_ids(spec): ids = [dev.id for dev in spec.devices.values()] ids_set = set(ids) if len(ids_set) != len(ids): return fail_msg(spec, f"There are overlapping device ids")
[docs]def spec_repeated_names(spec): def get_signal_names(spec): for dev in spec.devices.values(): for msg in dev.msgs.values(): for signal in msg.signals.values(): yield signal.name names = get_signal_names(spec) if len(set(names)) != len(names): return fail_msg(spec, f"There are repeated signal names")
[docs]def check(category, arg): failed = [] if category not in check_table.keys(): print(f"{category} category not available") return failed for check in check_table[category].values(): r = check(arg) if r: failed.append(r) return failed
[docs]def validate(logger, spec): failed = [] failed += check("spec", spec) for log in spec.logs.values(): failed += check("log", log) for message in spec.common.msgs.values(): failed += check("msg", message) for device in spec.devices.values(): failed += check("dev", device) for message in device.msgs.values(): failed += check("msg", message) for signal in message.signals.values(): failed += check("sig", signal) for cfg in device.cfgs.values(): failed += check("cfg", cfg) for cmd in device.cmds.values(): failed += check("cmd", cmd) return failed