Skip to content

Commit 60f09ed

Browse files
authored
[WIP] cycle_time as generic signal and frame attribute (#405)
* implement cycle_time as generic signal and frame attribute / remove Gen[Msg/Sig]cycle from attributes #146 * use effective frame cycle time for export use gcd of singnal-cycletimes for calculating effective cycle time This removes one (of many) dbc specific parts from canmatrix and other formats
1 parent b32f64a commit 60f09ed

16 files changed

+126
-51
lines changed

docs/cli.rst

+2
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ ____________________
414414

415415

416416
* yaml
417+
* scapy
418+
* lua
417419
* json:
418420

419421
--jsonExportCanard

examples/BusmasterRestbus.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,10 @@ def ticker_ecus(db, dbcname):
171171
bu._cycles = {}
172172
for frame in db.frames:
173173
if bu.name in frame.transmitters:
174-
if "GenMsgCycleTime" in frame.attributes and "GenMsgStartValue" in frame.attributes:
174+
if frame.effective_cycle_time != 0 and "GenMsgStartValue" in frame.attributes:
175175
data = frame.attributes["GenMsgStartValue"][1:-2]
176176
dlc = (math.floor(len(data) / 2))
177-
cycleTime = int(frame.attributes["GenMsgCycleTime"])
177+
cycleTime = frame.effective_cycle_time
178178
if float(cycleTime) > 0:
179179
if cycleTime in bu._cycles:
180180
bu._cycles[cycleTime].append(frame.arbitration_id.id)

examples/createccl.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ def main():
9999
txDict[frame] = sendIndex
100100
sendIndex += 1
101101
# print frame.name
102-
# if "GenMsgCycleTime" in frame.attributes and int(frame.attributes["GenMsgCycleTime"]) != 0:
102+
# if frame.effective_cycletime != 0:
103103
# print frame.name,
104-
# print frame.attributes["GenMsgCycleTime"]
104+
# print frame.effective_cycletime
105105
# ccl_h += createStoreMacrosForFrame(frame, "_" + frame.name + "_")
106106

107107
tempStr = ""

src/canmatrix/canmatrix.py

+20-12
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import itertools
3434
import logging
3535
import math
36+
import fractions
3637
import struct
3738
import typing
3839
from builtins import *
@@ -165,6 +166,8 @@ class Signal(object):
165166
calc_min_for_none = attr.ib(default=True) # type: bool
166167
calc_max_for_none = attr.ib(default=True) # type: bool
167168

169+
cycle_time = attr.ib(default=0) # type: int
170+
168171
min = attr.ib(
169172
converter=lambda value, float_factory=float_factory: (
170173
float_factory(value)
@@ -754,6 +757,8 @@ class Frame(object):
754757
receivers = attr.ib(factory=list) # type: typing.MutableSequence[str]
755758
signalGroups = attr.ib(factory=list) # type: typing.MutableSequence[SignalGroup]
756759

760+
cycle_time = attr.ib(default=0) # type: int
761+
757762
is_j1939 = attr.ib(default=False) # type: bool
758763
# ('cycleTime', '_cycleTime', int, None),
759764
# ('sendType', '_sendType', str, None),
@@ -828,27 +833,30 @@ def source(self, value): # type: (int) -> None
828833
self.arbitration_id.j1939_source = value
829834

830835

831-
# @property
832-
# def cycleTime(self):
833-
# if self._cycleTime is None:
834-
# self._cycleTime = self.attribute("GenMsgCycleTime")
835-
# return self._cycleTime
836-
#
836+
@property
837+
def effective_cycle_time(self):
838+
"""Calculate effective cycle time for frame, depending on singal cycle times"""
839+
min_cycle_time_list = [y for y in [x.cycle_time for x in self.signals] + [self.cycle_time] if y != 0]
840+
if len(min_cycle_time_list) == 0:
841+
return 0
842+
elif len(min_cycle_time_list) == 1:
843+
return min_cycle_time_list[0]
844+
else:
845+
gcd = fractions.gcd(min_cycle_time_list[0],min_cycle_time_list[1])
846+
for i in range(2,len(min_cycle_time_list)):
847+
gcd = fractions.gcd(gcd, min_cycle_time_list[i])
848+
return gcd
849+
# return min(min_cycle_time_list)
850+
837851
# @property
838852
# def sendType(self, db = None):
839853
# if self._sendType is None:
840854
# self._sendType = self.attribute("GenMsgSendType")
841855
# return self._sendType
842856
#
843-
# @cycleTime.setter
844-
# def cycleTime(self, value):
845-
# self._cycleTime = value
846-
# self.attributes["GenMsgCycleTime"] = value
847-
#
848857
# @sendType.setter
849858
# def sendType(self, value):
850859
# self._sendType = value
851-
# self.attributes["GenMsgCycleTime"] = value
852860

853861
def attribute(self, attribute_name, db=None, default=None):
854862
# type: (str, typing.Optional[CanMatrix], typing.Any) -> typing.Any

src/canmatrix/cli/convert.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def get_formats():
5959
@click.option('--deleteSignal', 'deleteSignal', help="delete Signal form databases. (comma separated list)\nSyntax: --deleteSignal=mySignal1,mySecondSignal")
6060
@click.option('--renameSignal', 'renameSignal', help="rename Signal form databases. (comma separated list)\nSyntax: --renameSignal=myOldSignal:myNewSignal,mySecondSignal:mySecondNewSignal")
6161
@click.option('--deleteZeroSignals/--no-deleteZeroSignals', 'deleteZeroSignals', default=False, help="delete zero length signals (signals with 0 bit length) from matrix\ndefault False")
62-
@click.option('--deleteSignalAttributes', 'deleteSignalAttributes', help="delete attributes from all signals\nExample --deleteSignalAttributes GenMsgCycle,CycleTime")
62+
@click.option('--deleteSignalAttributes', 'deleteSignalAttributes', help="delete attributes from all signals\nExample --deleteSignalAttributes GenMsgSomeVar,CycleTime")
6363
@click.option('--deleteFrame', 'deleteFrame', help="delete Frame form databases. (comma separated list)\nSyntax: --deleteFrame=myFrame1,mySecondFrame")
6464
@click.option('--renameFrame', 'renameFrame', help="increment each frame.id in database by increment\nSyntax: --frameIdIncrement=increment")
6565
@click.option('--addFrameReceiver', 'addFrameReceiver', help="add receiver Ecu to frame(s) (comma separated list)\nSyntax: --addFrameReceiver=framename:myNewEcu,mySecondEcu:myNEWEcu")
@@ -69,7 +69,7 @@ def get_formats():
6969
@click.option('--recalcDLC', 'recalcDLC', help="recalculate dlc; max: use maximum of stored and calculated dlc; force: force new calculated dlc")
7070
@click.option('--skipLongDlc', 'skipLongDlc', help="skip all Frames with dlc bigger than given threshold")
7171
@click.option('--cutLongFrames', 'cutLongFrames', help="cut all signals out of Frames with dlc bigger than given threshold")
72-
@click.option('--deleteFrameAttributes', 'deleteFrameAttributes', help="delete attributes from all frames\nExample --deleteFrameAttributes GenMsgCycle,CycleTime")
72+
@click.option('--deleteFrameAttributes', 'deleteFrameAttributes', help="delete attributes from all frames\nExample --deleteFrameAttributes GenMsgSomeVar,CycleTime")
7373
@click.option('--ecus', help="Copy only given ECUs (comma separated list) to target matrix")
7474
@click.option('--frames', help="Copy only given Frames (comma separated list) to target matrix")
7575
@click.option('--signals', help="Copy only given Signals (comma separated list) to target matrix just as 'free' signals without containing frame")

src/canmatrix/formats/arxml.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1316,11 +1316,11 @@ def store_frame_timings(target_frame, cyclic_timing, event_timing, minimum_delay
13161316

13171317
value = get_child(repeating_time, "VALUE", root_or_cache, ns)
13181318
if value is not None:
1319-
target_frame.add_attribute("GenMsgCycleTime", str(int(float_factory(value.text) * 1000)))
1319+
target_frame.cycle_time = int(float_factory(value.text) * 1000)
13201320
elif cyclic_timing is not None:
13211321
value = get_child(time_period, "VALUE", root_or_cache, ns)
13221322
if value is not None:
1323-
target_frame.add_attribute("GenMsgCycleTime", str(int(float_factory(value.text) * 1000)))
1323+
target_frame.cycle_time = int(float_factory(value.text) * 1000)
13241324

13251325

13261326
def get_frame(frame_triggering, root_or_cache, multiplex_translation, ns, float_factory):
@@ -1659,7 +1659,6 @@ def load(file, **options):
16591659
db.add_ecu_defines("NWM-Stationsadresse", 'HEX 0 63')
16601660
db.add_ecu_defines("NWM-Knoten", 'ENUM "nein","ja"')
16611661
db.add_signal_defines("LongName", 'STRING')
1662-
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
16631662
db.add_frame_defines("GenMsgDelayTime", 'INT 0 65535')
16641663
db.add_frame_defines("GenMsgNrOfRepetitions", 'INT 0 65535')
16651664
db.add_frame_defines("GenMsgStartValue", 'STRING')

src/canmatrix/formats/dbc.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,12 @@ def dump(in_db, f, **options):
226226

227227
numbered_names = collections.OrderedDict() # type: ignore
228228

229+
if frame.cycle_time != 0:
230+
frame.add_attribute("GenMsgCycleTime", frame.cycle_time)
231+
229232
for signal in frame.signals:
233+
if signal.cycle_time != 0:
234+
signal.add_attribute("GenSigCycleTime", signal.cycle_time)
230235
name = normalized_names[signal]
231236
if compatibility:
232237
name = re.sub("[^A-Za-z0-9]", whitespace_replacement, name)
@@ -238,6 +243,13 @@ def dump(in_db, f, **options):
238243
name += str(duplicate_signal_counter[name] - 1)
239244
output_names[frame][signal] = name
240245

246+
if max([x.cycle_time for x in db.frames]) > 0:
247+
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
248+
if max([x.cycle_time for y in db.frames for x in y.signals]) > 0:
249+
db.add_signal_defines("GenSigCycleTime", 'INT 0 65535')
250+
251+
252+
241253
# Frames
242254
for frame in db.frames:
243255
multiplex_written = False
@@ -905,6 +917,7 @@ def add_frame_by_id(new_frame): # type: (canmatrix.Frame) -> None
905917
ecu.name = ecu.attributes.get("SystemNodeLongSymbol")[1:-1]
906918
ecu.del_attribute("SystemNodeLongSymbol")
907919
for frame in db.frames:
920+
frame.cycle_time = int(frame.attributes.get("GenMsgCycleTime", 0))
908921
if frame.attributes.get("SystemMessageLongSymbol", None) is not None:
909922
frame.name = frame.attributes.get("SystemMessageLongSymbol")[1:-1]
910923
frame.del_attribute("SystemMessageLongSymbol")
@@ -917,6 +930,7 @@ def add_frame_by_id(new_frame): # type: (canmatrix.Frame) -> None
917930
# frame.extended = 1
918931

919932
for signal in frame.signals:
933+
signal.cycle_time = int(signal.attributes.get("GenSigCycleTime", 0))
920934
if signal.attribute("SystemSignalLongSymbol") is not None:
921935
signal.name = signal.attribute("SystemSignalLongSymbol")[1:-1]
922936
signal.del_attribute("SystemSignalLongSymbol")
@@ -947,9 +961,15 @@ def add_frame_by_id(new_frame): # type: (canmatrix.Frame) -> None
947961
frame.is_fd = True
948962
if "J1939PG" in frame.attributes.get("VFrameFormat", ""):
949963
frame.is_j1939 = True
950-
951964
db.update_ecu_list()
952965
db.del_ecu("Vector__XXX")
966+
db.del_frame_attributes(["GenMsgCycleTime"])
967+
db.del_signal_attributes(["GenSigCycleTime"])
968+
if "GenMsgCycleTime" in db.frame_defines:
969+
del (db.frame_defines["GenMsgCycleTime"])
970+
if "GenSigCycleTime" in db.signal_defines:
971+
del (db.signal_defines["GenSigCycleTime"])
972+
953973
free_signals_dummy_frame = db.frame_by_name("VECTOR__INDEPENDENT_SIG_MSG")
954974
if free_signals_dummy_frame is not None and free_signals_dummy_frame.arbitration_id.id == 0x40000000:
955975
db.signals = free_signals_dummy_frame.signals

src/canmatrix/formats/kcd.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,9 @@ def dump(dbs, f, **_options):
179179

180180
if frame.arbitration_id.extended == 1:
181181
message.set("format", "extended")
182-
if "GenMsgCycleTime" in db.frame_defines:
183-
cycle_time = frame.attribute("GenMsgCycleTime", db=db)
184-
if cycle_time is not None and int(cycle_time) > 0:
185-
message.set("triggered", "true")
186-
message.set("interval", "%d" % int(cycle_time))
182+
if frame.effective_cycle_time != 0:
183+
message.set("triggered", "true")
184+
message.set("interval", "%d" % int(frame.effective_cycle_time))
187185

188186
comment_elem = lxml.etree.Element('Notes')
189187
if frame.comment is not None:
@@ -357,7 +355,6 @@ def load(f, **options):
357355
counter = 0
358356
for bus in buses:
359357
db = canmatrix.CanMatrix()
360-
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
361358
for node in nodes:
362359
db.ecus.append(canmatrix.Ecu(node.get('name')))
363360
node_list[node.get('id')] = node.get('name')
@@ -370,7 +367,7 @@ def load(f, **options):
370367
new_frame = canmatrix.Frame(message.get('name'))
371368

372369
if 'triggered' in message.attrib:
373-
new_frame.add_attribute("GenMsgCycleTime", message.get('interval'))
370+
new_frame.cycle_time = int(message.get('interval'))
374371

375372
if 'length' in message.attrib:
376373
dlc = int(message.get('length'))

src/canmatrix/formats/sym.py

+5-12
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,8 @@ def send_receive(for_frame):
258258
mux_out += id_type
259259
first = 1
260260
mux_out += "DLC=%d\n" % frame.size
261-
if "GenMsgCycleTime" in db.frame_defines:
262-
cycle_time = frame.attribute("GenMsgCycleTime", db=db)
263-
if cycle_time is not None:
264-
mux_out += "CycleTime=" + str(cycle_time) + "\n"
261+
if frame.cycle_time != 0:
262+
mux_out += "CycleTime=" + str(frame.effective_cycle_time) + "\n"
265263

266264
mux_name = frame.mux_names.get(i, mux_signal.name + "%d" % i)
267265

@@ -299,10 +297,8 @@ def send_receive(for_frame):
299297
output += name
300298
output += id_type
301299
output += "DLC=%d\n" % frame.size
302-
if "GenMsgCycleTime" in db.frame_defines:
303-
cycle_time = frame.attribute("GenMsgCycleTime", db=db)
304-
if cycle_time is not None:
305-
output += "CycleTime=" + str(cycle_time) + "\n"
300+
if frame.cycle_time != 0:
301+
output += "CycleTime=" + str(frame.effective_cycle_time) + "\n"
306302
for signal in frame.signals:
307303
output += create_signal(db, signal)
308304
output += "\n"
@@ -330,7 +326,6 @@ class Mode(object):
330326
frame = None
331327

332328
db = canmatrix.CanMatrix()
333-
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
334329
db.add_frame_defines("Receivable", 'BOOL False True')
335330
db.add_frame_defines("Sendable", 'BOOL False True')
336331
db.add_signal_defines("GenSigStartValue", 'FLOAT -3.4E+038 3.4E+038')
@@ -605,9 +600,7 @@ class Mode(object):
605600
frame.size = int(line.split('=')[1])
606601

607602
elif line.startswith('CycleTime'):
608-
frame.add_attribute(
609-
"GenMsgCycleTime",
610-
line.split('=')[1].strip())
603+
frame.cycle_time = int(line.split('=')[1].strip())
611604
# else:
612605
# print line
613606
# else:

src/canmatrix/formats/xls.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,6 @@ def load(file, **options):
351351
# Defines not imported...
352352
# db.add_ecu_defines("NWM-Stationsadresse", 'HEX 0 63')
353353
# db.add_ecu_defines("NWM-Knoten", 'ENUM "nein","ja"')
354-
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
355354
db.add_frame_defines("GenMsgDelayTime", 'INT 0 65535')
356355
db.add_frame_defines("GenMsgCycleTimeActive", 'INT 0 65535')
357356
db.add_frame_defines("GenMsgNrOfRepetitions", 'INT 0 65535')
@@ -452,7 +451,7 @@ def load(file, **options):
452451
cycle_time = int(cycle_time)
453452
except:
454453
cycle_time = 0
455-
new_frame.add_attribute("GenMsgCycleTime", str(int(cycle_time)))
454+
new_frame.cycle_time = cycle_time
456455

457456
for additional_index in additional_inputs:
458457
if "frame" in additional_inputs[additional_index]:

src/canmatrix/formats/xls_common.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ def get_frame_info(db, frame):
3838
# frame-Name
3939
ret_array.append(frame.name)
4040

41-
if "GenMsgCycleTime" in db.frame_defines:
42-
# determine cycle-time
43-
ret_array.append(frame.attribute("GenMsgCycleTime", db=db))
41+
ret_array.append(frame.effective_cycle_time)
4442

4543
# determine send-type
4644
if "GenMsgSendType" in db.frame_defines:

src/canmatrix/formats/xlsx.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,6 @@ def load(filename, **options):
402402
letter_index += ["%s%s" % (a, b) for a in all_letters for b in all_letters]
403403

404404
# Defines not imported...
405-
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
406405
db.add_frame_defines("GenMsgDelayTime", 'INT 0 65535')
407406
db.add_frame_defines("GenMsgCycleTimeActive", 'INT 0 65535')
408407
db.add_frame_defines("GenMsgNrOfRepetitions", 'INT 0 65535')
@@ -465,7 +464,7 @@ def load(filename, **options):
465464
if launch_type not in launch_types:
466465
launch_types.append(launch_type)
467466

468-
new_frame.add_attribute("GenMsgCycleTime", cycle_time)
467+
new_frame.cycle_time = cycle_time
469468

470469
# new signal detected
471470
if 'Signal Name' in row and row['Signal Name'] != signal_name:

src/canmatrix/tests/test_canmatrix.py

+24
Original file line numberDiff line numberDiff line change
@@ -985,3 +985,27 @@ def test_canmatrix_del_frame_by_instance(empty_matrix):
985985
empty_matrix.del_frame(f1)
986986
assert empty_matrix.frames == [f2]
987987

988+
def test_effective_cycle_time():
989+
frame = canmatrix.Frame()
990+
sig1 = canmatrix.Signal(name = "s1", cycle_time=1)
991+
sig2 = canmatrix.Signal(name = "s2", cycle_time=0)
992+
frame.add_signal(sig1)
993+
frame.add_signal(sig2)
994+
assert frame.effective_cycle_time == 1
995+
996+
sig2.cycle_time = 2
997+
assert frame.effective_cycle_time == 1
998+
999+
sig1.cycle_time = 4
1000+
assert frame.effective_cycle_time == 2
1001+
1002+
sig1.cycle_time = 3
1003+
assert frame.effective_cycle_time == 1
1004+
1005+
frame.cycle_time = 1
1006+
assert frame.effective_cycle_time == 1
1007+
1008+
frame.cycle_time = 0
1009+
sig1.cycle_time = 0
1010+
sig2.cycle_time = 0
1011+
assert frame.effective_cycle_time == 0

src/canmatrix/tests/test_cli_convert.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def create_dbc_with_special_char():
6464

6565
db = canmatrix.CanMatrix()
6666
db.add_frame(myFrame)
67-
db.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
67+
db.add_frame_defines("SomeUnneededDefine", 'INT 0 65535')
6868
canmatrix.formats.dumpp({"": db}, outFile, dbcExportEncoding='iso-8859-1',
6969
dbcExportCommentEncoding='iso-8859-1')
7070
return outFile

0 commit comments

Comments
 (0)