diff --git a/can.py b/can.py index 20c5cc4..7db4830 100755 --- a/can.py +++ b/can.py @@ -1,5 +1,6 @@ #!/bin/python +from __future__ import annotations from operator import mod from unicodedata import normalize import json @@ -40,7 +41,7 @@ def validate_bit(bit: int) -> bool: return False return True - class topic: + class Topic: def __init__(self, msg: str, id: int,frequency: int, description: str): self.name = Can.convert_string(msg) @@ -70,6 +71,43 @@ def get(self) -> dict: "frequency": self.frequency, "frame_length": self.frame_length } + + @classmethod + def from_dict(cls, data: dict) -> Can.Topic: + topic = cls( + msg=data["name"], + id=data["id"], + frequency=data["frequency"], + description=data["description"] + ) + for (i, byte) in enumerate(data["bytes"]): + # Signature byte is already described + if i == 0 or byte is None: + continue + + topic.describe_byte( + name=byte["name"], + byte=i, + description=byte["description"], + btype=byte["type"], + units=byte["units"] + ) + + # Describe bits if byte is a bitfield + if byte["type"] != "bitfield": + continue + + for bit in byte["bits"]: + # Skip if bit is not described + if bit is None: + continue + + topic.describe_bit( + name=bit, + byte=i, + bit=byte["bits"].index(bit) + ) + return topic def __str__(self) -> str: return json.dumps(self.get(), indent=4) @@ -143,7 +181,7 @@ def describe_byte(self, } if self.bytes[byte]["type"] == "bitfield": - self.bytes[byte]["bits"] = [None]*8 + self.bytes[byte]["bits"] = [None] * 8 def describe_bit(self, name: str, byte: int, bit: int): self.validate_byte(byte) @@ -158,7 +196,7 @@ def describe_bit(self, name: str, byte: int, bit: int): self.bytes[byte]["bits"][bit] = name - class module: + class Module: def __init__(self, name: str, signature: int, description: str): self.validate_name(name) @@ -220,17 +258,60 @@ def __str__(self) -> str: def add_module(self, module): for m in self.modules: + # Check if module name is unique if m['name'] == dict(module.get()).get('name'): - raise ValueError("module field `name` must be unique!") + raise ValueError("module field `name` must be unique!", m['name']) + # Check if module signature is unique + if m['signature'] == dict(module.get()).get('signature'): + raise ValueError( + "module field `signature` must be unique!, module ", + m['name'], + " and ", + module.get()['name'], + " have the same signature: ", + m["signature"] + ) + # Check if topics id is unique + for db_topic in m['topics']: + for topic in module.get()['topics']: + if topic['id'] == db_topic['id']: + print(f"WARNING: Topic id {topic['id']} is not unique", + f"conflict between module {m['name']} and {module.get()['name']}") self.modules.append(module.get()) + + def add_multiple_modules(self, module: Module, quantity: int) -> None: + ''' + Add multiple modules based on a template module and a quantity + ''' + for i in range(quantity): + # Create topics + topics = [] + for topic in module.get()["topics"]: + new_topic = Can.Topic.from_dict(topic) + # Update topic id + new_topic.id = topic["id"] + i + topics.append(new_topic) + + # Create module + new_module = Can.Module( + name=module.get()["name"] + "_" + str(i + 1), + signature=module.get()["signature"] + i, + description=module.get()["description"] + " " + str(i + 1) + ) + # Add topics to module + for topic in topics: + new_module.add_topic(topic) + + # Add module to database + self.add_module(new_module) def import_json(self, filename: str): with open(filename, 'r') as file: data = dict(json.load(file)) self.version = data["version"] for module in data["modules"]: - self.add_module(Can.module( + self.add_module(Can.Module( name=module.get('name'), signature=module.get('signature'), description=module.get('description') @@ -384,22 +465,22 @@ def plot_load(self): if __name__ == '__main__': - t1 = Can.topic("motor", 9, 100, "Motor controller parameters") + t1 = Can.Topic("motor", 9, 100, "Motor controller parameters") t1.describe_byte("motor", 1, "Switches and states", "bitfield", "") t1.describe_bit("motor on", 1, 0) t1.describe_byte("D raw", 2, "Motor Duty Cycle", "uint8_t", "%") t1.describe_byte("I raw", 3, "Motor Soft Start", "uint8_t", "%") - t2 = Can.topic("motor2", 19, 10, "Motor controller parameters") + t2 = Can.Topic("motor2", 19, 10, "Motor controller parameters") t2.describe_byte("motor", 1, "Switches and states", "bitfield", "") t2.describe_bit("motor on", 1, 0) t2.describe_byte("D raw", 2, "Motor Duty Cycle", "uint8_t", "%") t2.describe_byte("I raw", 3, "Motor Soft Start", "uint8_t", "%") # print(t1) - m1 = Can.module("mic17", 10, "Modulo de Interface de Controle") + m1 = Can.Module("mic17", 10, "Modulo de Interface de Controle") m1.add_topic(t1) m1.add_topic(t2) - m2 = Can.module("mam21", 10, "Mamm") + m2 = Can.Module("mam21", 10, "Mamm") # print(m1) diff --git a/can_ids_generator.py b/can_ids_generator.py index 7896cdb..f87c5c6 100644 --- a/can_ids_generator.py +++ b/can_ids_generator.py @@ -15,13 +15,13 @@ ################################################################################ ### MODULE: GENERIC -module_generic = can.module( +module_generic = can.Module( name="generic", signature=0, description="Modulo generico para facilitar implementacoes genericas" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=50, frequency=0, @@ -42,7 +42,7 @@ units="" ) #### TOPIC: GENERIC -topic_generic = can.topic( +topic_generic = can.Topic( msg="generic", id=51, frequency=0, @@ -55,13 +55,13 @@ ################################################################################ ### MODULE: MIC19 -module_mic19 = can.module( +module_mic19 = can.Module( name="mic19", signature=240, description="Modulo de Interface de Controle" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=30, description="Module state report", @@ -82,7 +82,7 @@ units="" ) #### TOPIC: MOTOR -topic_motor = can.topic( +topic_motor = can.Topic( msg="motor", id=31, description="Motor controller parameters", @@ -125,7 +125,7 @@ units="%" ) #### TOPIC: PUMPS -topic_pumps = can.topic( +topic_pumps = can.Topic( msg="pumps", id=41, frequency = 4, @@ -154,7 +154,7 @@ bit=2 ) #### TOPIC: MPPTS -topic_mppts = can.topic( +topic_mppts = can.Topic( msg="mppts", id=200, frequency = 4, @@ -180,7 +180,7 @@ units="%" ) #### TOPIC: MCS -topic_mcs = can.topic( +topic_mcs = can.Topic( msg="mcs", id=32, frequency = 50, @@ -199,7 +199,7 @@ bit=0 ) #### TOPIC: MDE -topic_mde = can.topic( +topic_mde = can.Topic( msg="mde", id=33, frequency = 50, @@ -230,13 +230,13 @@ ################################################################################ ### MODULE: MDE22 -module_mde22 = can.module( +module_mde22 = can.Module( name="mde22", signature=170, description="Modulo da Direção Elétrica" ) ### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=100, frequency=1, @@ -257,7 +257,7 @@ units="" ) # TOPIC: STEERING MODULE MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="steeringbat_measurements", id=201, frequency=10, @@ -312,13 +312,13 @@ ################################################################################ ### MODULE: MVC19_1 -module_mvc19_1 = can.module( +module_mvc19_1 = can.Module( name="mvc19_1", signature=210, description="Modulo de voltimetro 1" ) ### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=101, frequency=1, @@ -344,13 +344,13 @@ ################################################################################ ### MODULE: MVC19_2 -module_mvc19_2 = can.module( +module_mvc19_2 = can.Module( name="mvc19_2", signature=211, description="Modulo de voltimetro 2" ) ### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", frequency=1, id=102, @@ -377,13 +377,13 @@ ################################################################################ ### MODULE: MCC19_1 -module_mcc19_1 = can.module( +module_mcc19_1 = can.Module( name="mcc19_1", signature=225, description="Modulo controlador de carga" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=103, frequency=1, @@ -437,7 +437,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=202, frequency=10, @@ -500,13 +500,13 @@ ################################################################################ ### MODULE: MCC19_2 -module_mcc19_2 = can.module( +module_mcc19_2 = can.Module( name="mcc19_2", signature=226, description="Modulo controlador de carga" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=104, frequency=1, @@ -560,7 +560,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=203, frequency=10, @@ -623,13 +623,13 @@ ################################################################################ ### MODULE: MCC19_3 -module_mcc19_3 = can.module( +module_mcc19_3 = can.Module( name="mcc19_3", signature=227, description="Modulo controlador de carga" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=105, frequency=1, @@ -683,7 +683,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=204, frequency=10, @@ -746,13 +746,13 @@ ################################################################################ ### MODULE: MCC19_4 -module_mcc19_4 = can.module( +module_mcc19_4 = can.Module( name="mcc19_4", signature=228, description="Modulo controlador de carga" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=106, frequency=1, @@ -806,7 +806,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=205, frequency=10, @@ -868,13 +868,13 @@ ################################################################################ ### MODULE: MCC19_5 -module_mcc19_5 = can.module( +module_mcc19_5 = can.Module( name="mcc19_5", signature=229, description="Modulo controlador de carga" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=107, frequency=1, @@ -928,7 +928,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=206, frequency=10, @@ -990,13 +990,13 @@ ################################################################################ ### MODULE: MCC19_6 -module_mcc19_6 = can.module( +module_mcc19_6 = can.Module( name="mcc19_6", signature=239, description="Modulo controlador de carga" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=108, frequency=1, @@ -1050,7 +1050,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=207, frequency=10, @@ -1112,13 +1112,13 @@ ################################################################################ ### MODULE: MCB19_1 -module_mcb19_1 = can.module( +module_mcb19_1 = can.Module( name="mcb19_1", signature=220, description="Modulo de carregamento das baterias auxiliares" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=109, frequency=1, @@ -1172,7 +1172,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=208, frequency=10, @@ -1234,13 +1234,13 @@ ################################################################################ ### MODULE: MCB19_2 -module_mcb19_2 = can.module( +module_mcb19_2 = can.Module( name="mcb19_2", signature=221, description="Modulo de carregamento das baterias auxiliares" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=110, frequency=1, @@ -1294,7 +1294,7 @@ ) ### TOPIC: MEASUREMENTS -topic_measurements = can.topic( +topic_measurements = can.Topic( msg="measurements", id=209, frequency=10, @@ -1356,13 +1356,13 @@ ################################################################################ ### MODULE: MAC22 -module_mac22 = can.module( +module_mac22 = can.Module( name="mac22", signature=180, description="Modulo de Acionamento da Contatora" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=35, frequency=5, @@ -1383,7 +1383,7 @@ units="" ) #### TOPIC: CONTACTOR -topic_contactor = can.topic( +topic_contactor = can.Topic( msg="contactor", id=34, frequency=50, @@ -1404,13 +1404,13 @@ ################################################################################ ### MODULE: MAM19 -module_mam19 = can.module( +module_mam19 = can.Module( name="mam19", signature=190, description="Modulo de Acionamento do Motor" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1100011, frequency=5, @@ -1431,7 +1431,7 @@ units="" ) #### TOPIC: MOTOR -topic_motor = can.topic( +topic_motor = can.Topic( msg="motor", id=0b1100010, frequency=50, @@ -1452,7 +1452,7 @@ units="%" ) #### TOPIC: CONTACTOR -topic_contactor = can.topic( +topic_contactor = can.Topic( msg="contactor", id=0b100100, frequency=5, @@ -1474,14 +1474,14 @@ ################################################################################ ### MODULE: MAB19 -module_mab19 = can.module( +module_mab19 = can.Module( name="mab19", signature=230, description="Modulo de Acionamento das Bombas de Porao" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1101111, frequency=0, @@ -1503,7 +1503,7 @@ ) #### TOPIC: PUMPS -topic_pumps = can.topic( +topic_pumps = can.Topic( msg="pumps", id=0b11010010, frequency=0, @@ -1539,13 +1539,13 @@ ################################################################################ ### MODULE: MSC19_1 -module_msc19_1 = can.module( +module_msc19_1 = can.Module( name="msc19_1", signature=250, description="Main Battery Voltage Sensor" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1110000, frequency=1, @@ -1566,7 +1566,7 @@ units="" ) #### TOPIC: ADC -topic_adc = can.topic( +topic_adc = can.Topic( msg="ADC", id=0b11010011, frequency=10, @@ -1622,13 +1622,13 @@ ################################################################################ ### MODULE: MSC19_2 -module_msc19_2 = can.module( +module_msc19_2 = can.Module( name="msc19_2", signature=251, description="Auxilliary Battery Voltage Sensor" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1110001, frequency=1, @@ -1649,7 +1649,7 @@ units="" ) #### TOPIC: ADC -topic_adc = can.topic( +topic_adc = can.Topic( msg="ADC", id=0b11010100, frequency=10, @@ -1705,13 +1705,13 @@ ################################################################################ ### MODULE: MSC19_3 -module_msc19_3 = can.module( +module_msc19_3 = can.Module( name="msc19_3", signature=252, description="Extra Battery Voltage Sensor" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1110010, frequency=1, @@ -1732,7 +1732,7 @@ units="" ) #### TOPIC: ADC -topic_adc = can.topic( +topic_adc = can.Topic( msg="ADC", id=0b11010101, frequency=10, @@ -1788,13 +1788,13 @@ ################################################################################ ### MODULE: MSC19_4 -module_msc19_4 = can.module( +module_msc19_4 = can.Module( name="msc19_4", signature=253, description="Main Battery Input Current Sensor" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1110011, frequency=1, @@ -1815,7 +1815,7 @@ units="" ) #### TOPIC: ADC -topic_adc = can.topic( +topic_adc = can.Topic( msg="ADC", id=0b11010110, frequency=10, @@ -1871,13 +1871,13 @@ ################################################################################ ### MODULE: MSC19_5 -module_msc19_5 = can.module( +module_msc19_5 = can.Module( name="msc19_5", signature=254, description="Main Battery Output Current Sensor" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1110100, frequency=1, @@ -1898,7 +1898,7 @@ units="" ) #### TOPIC: ADC -topic_adc = can.topic( +topic_adc = can.Topic( msg="ADC", id=0b11010111, frequency=10, @@ -1954,13 +1954,13 @@ ################################################################################ ### MODULE: MCS19 -module_mcs19 = can.module( +module_mcs19 = can.Module( name="mcs19", signature=200, description="Modulo de Carregamento do Sistema" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=117, frequency=1, @@ -1982,7 +1982,7 @@ ) # TOPIC: Start stages -topic_start_stages = can.topic( +topic_start_stages = can.Topic( msg="start_stages", id=0b100101, frequency=50, @@ -2015,7 +2015,7 @@ ) #### TOPIC: BATTERY -topic_bat = can.topic( +topic_bat = can.Topic( msg="BAT", id=0b11011000, frequency=10, @@ -2065,7 +2065,7 @@ ) #### TOPIC: CAPACITOR -topic_cap = can.topic( +topic_cap = can.Topic( msg="CAP", id=0b11011001, frequency=10, @@ -2123,14 +2123,14 @@ ################################################################################ ### MODULE: MT19 -module_mt19 = can.module( +module_mt19 = can.Module( name="mt19", signature=255, description="Modulo Tacometro" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b11011010, description="Module state report", @@ -2152,7 +2152,7 @@ ) #### TOPIC: RPM -topic_rpm = can.topic( +topic_rpm = can.Topic( msg="RPM", id=0b11011011, description="RPM motor values", @@ -2180,13 +2180,13 @@ ################################################################################ ### MODULE: MSWI19 -module_mswi19 = can.module( +module_mswi19 = can.Module( name="mswi19", signature=241, description="Modulo de Interface de Controle" ) #### TOPIC: STATE -topic_state = can.topic( +topic_state = can.Topic( msg="state", id=0b1001, description="Module state report", @@ -2207,7 +2207,7 @@ units="" ) #### TOPIC: MOTOR -topic_motor = can.topic( +topic_motor = can.Topic( msg="motor", id=0b1010, description="Motor controller parameters", @@ -2245,7 +2245,7 @@ units="%" ) #### TOPIC: PUMPS -topic_pumps = can.topic( +topic_pumps = can.Topic( msg="pumps", id=0b11011100, description="Pumps controller parameters", @@ -2274,7 +2274,7 @@ bit=2 ) #### TOPIC: MPPTS -topic_mppts = can.topic( +topic_mppts = can.Topic( msg="mppts", id=0b11011101, description="Mppts controller parameters", @@ -2300,7 +2300,7 @@ units="%" ) #### TOPIC: MCS -topic_mcs = can.topic( +topic_mcs = can.Topic( msg="mcs", id=0b101000, description="MCS controller parameters", diff --git a/test/test_can.py b/test/test_can.py index 3790e0a..885bafb 100644 --- a/test/test_can.py +++ b/test/test_can.py @@ -9,7 +9,7 @@ def setUp(self): self.can = Can def test_topic(self): - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") expected = { "name": "MOTOR", "description": "topic description text here", @@ -33,7 +33,7 @@ def test_topic(self): ) def test_validate_byte(self): - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") # Trying to add using wrong type on the byte should raise TypeError: with self.assertRaises(TypeError): @@ -53,7 +53,7 @@ def test_validate_byte(self): t.describe_byte("some byte", 1,"byte description text here", 'u8') def test_validate_bit(self): - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") t.describe_byte("some byte", 1, "byte description text here", 'bitfield') @@ -73,7 +73,7 @@ def test_validate_bit(self): t.describe_bit("some bit", 1, 0) def test_validate_byte_name(self): - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") # Trying to add using wrong type on the name should raise TypeError: with self.assertRaises(TypeError): @@ -86,7 +86,7 @@ def test_validate_byte_name(self): t.describe_byte("some byte", 1, "byte description text here", 'u8') def test_validate_bit_name(self): - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") t.describe_byte("some byte", 1, "byte description text here", 'bitfield') @@ -101,7 +101,7 @@ def test_validate_bit_name(self): t.describe_bit("some bit", 1, 1) def test_describe_byte(self): - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") t.describe_byte( "motor", 1, "byte description text here", "bitfield", "") @@ -136,7 +136,7 @@ def test_describe_byte(self): def test_describe_bit(self): self.maxDiff = None - t = self.can.topic("motor", 9, 100, "topic description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") t.describe_byte( "motor", 1, "byte description text here", "bitfield", "") t.describe_bit("motor on", 1, 0) @@ -183,7 +183,7 @@ def setUp(self): self.can = Can def test_module(self): - m = self.can.module("mic17", 10, "module description text here") + m = self.can.Module("mic17", 10, "module description text here") expected = { "name": "MIC17", @@ -198,8 +198,8 @@ def test_module(self): ) def test_add_topic(self): - m = self.can.module("mic17", 10, "module description text here") - t = self.can.topic("motor", 9, 100, "topic description text here") + m = self.can.Module("mic17", 10, "module description text here") + t = self.can.Topic("motor", 9, 100, "topic description text here") m.add_topic(t) @@ -242,7 +242,7 @@ def setUp(self): self.can = Can def test_add_module(self): - m = self.can.module("mic17", 10, "module description text here") + m = self.can.Module("mic17", 10, "module description text here") c = Can(version="0.0.0", bitrate=500e3) c.add_module(m) @@ -269,7 +269,7 @@ def test_add_module(self): c.add_module(m) def test_export_and_import_json(self): - m = self.can.module("mic17", 10, "module description text here") + m = self.can.Module("mic17", 10, "module description text here") c1 = Can(version="0.0.0", bitrate=500e3) c1.add_module(m) c1.export_json("test/test.json")