Skip to content

Commit 76b6a44

Browse files
committed
Merge branch 'feature-text-on-firings' into 'main'
Feature text on firings See merge request computational-modeling/cmtrace!1
2 parents 6dbf70c + 8cc2710 commit 76b6a44

File tree

7 files changed

+101
-85
lines changed

7 files changed

+101
-85
lines changed

.gitlab-ci.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ build:
44
script:
55
- sudo apt-get -y update
66
- sudo apt-get -y install python3-pip libcairo2-dev
7-
- cd /builds/mgeilen/cmtrace
8-
- bash ./ci/script/test.sh
9-
artifacts:
10-
expire_in: 14d
11-
when: always
12-
paths:
13-
- /builds/mgeilen/cmtrace/packages/cmtrace/cmtrace/test/output/
7+
- cd ./package/cmtrace/cmtrace/tests
8+
- python3 -m pip install pytest pyyaml svgwrite pycairo
9+
- python3 -m pytest . -v
10+

package/cmtrace/cmtrace/dataflow/actor.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@
44

55
class Actor:
66
"""Represents an actor in a dataflow graph"""
7-
def __init__(self, name, actdelay, scenario=None):
7+
def __init__(self, name, act_delay, scenario=None):
88
self.name = name
9-
self.delay = actdelay
10-
self.inputs = dict()
9+
self.delay = act_delay
10+
self.inputs = {}
1111
self.primary_inputs = []
12-
self.state_inputs = dict()
12+
self.state_inputs = {}
1313
self.firings = []
1414
self.scenario = scenario
1515

16-
def add_channel_input(self, actor, initial_tokens=0, arcdelay=0, initial_time=MP_MINUS_INF):
16+
def add_channel_input(self, actor, initial_tokens=0, arc_delay=0, initial_time=MP_MINUS_INF):
1717
"""add a channel input dependency to another actor, including
18-
a time-offset arcdelay"""
19-
self.inputs[actor.name] = (actor, initial_tokens, arcdelay, initial_time)
18+
a time-offset arc_delay"""
19+
self.inputs[actor.name] = (actor, initial_tokens, arc_delay, initial_time)
2020

21-
def add_state_input(self, state, tokendelay=0, arcdelay=0):
21+
def add_state_input(self, state, token_delay=0, arc_delay=0):
2222
"""Add a dependency on a state token for an SADF graph"""
23-
self.state_inputs[state.name] = (state, tokendelay, arcdelay)
23+
self.state_inputs[state.name] = (state, token_delay, arc_delay)
2424
return
2525

26-
def add_primary_input(self, priminput):
26+
def add_primary_input(self, prim_input):
2727
"""Add dependency to a primary input to the graph."""
28-
self.primary_inputs.append(priminput)
28+
self.primary_inputs.append(prim_input)
2929

3030
def completions(self):
3131
"""returns the current completion times of the actor"""
@@ -37,13 +37,13 @@ def firing_intervals(self):
3737
return [(f, f+self.delay) for f in self.firings]
3838

3939
def update_firings(self):
40-
"""Recomput the firings of the actor based on its input dependencies.
40+
"""Recompute the firings of the actor based on its input dependencies.
4141
Returns a boolean indicating if the computed firings remained the same."""
42-
oldfirings = len(self.firings)
42+
old_firings = len(self.firings)
4343
traces = []
4444
# collect all the incoming channels
45-
for i, (act, tok, arcdel, tokinit) in self.inputs.items():
46-
traces.append(output_sequence(act.completions(), tok, arcdel, tokinit))
45+
for i, (act, tok, arc_del, tok_init) in self.inputs.items():
46+
traces.append(output_sequence(act.completions(), tok, arc_del, tok_init))
4747
# collect traces for all primary inputs
4848
for i in self.primary_inputs:
4949
traces.append(i)
@@ -52,7 +52,7 @@ def update_firings(self):
5252

5353
# determine the firings
5454
self.firings = mp_max(*traces)
55-
return oldfirings == len(self.firings)
55+
return old_firings == len(self.firings)
5656

5757
def set_scenario(self, scenario):
5858
"""set the scenario in which the actor is active"""

package/cmtrace/cmtrace/graphics/svgcanvas.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from cmtrace.latexsvg.latexsvg import latex_to_svg
55

66
# conversion constants
7-
MMPERPT = 0.352778
7+
MM_PER_PT = 0.352778
88

99
# default font size
1010
DEFAULT_FONT_SIZE = 10
@@ -72,22 +72,22 @@ def draw_text(self, text, insert, fill=(0, 0, 0), font_size=None,
7272
font_size=the_font_size, font_family=font, text_anchor=text_anchor, \
7373
alignment_baseline="auto"))
7474

75-
def draw_rect(self, insert, size, fillcolor=(0, 0, 0), stroke_width=MMPERPT, \
75+
def draw_rect(self, insert, size, fillcolor=(0, 0, 0), stroke_width=MM_PER_PT, \
7676
stroke_color=(0, 0, 0)):
7777
""" draw a rectangle """
7878
# add rectangle to the drawing
7979
self.drawing.add(self.drawing.rect(insert=(insert[0], insert[1]), size=(size[0], size[1]), \
8080
fill=svgwrite.rgb(*fillcolor), stroke_width=stroke_width, \
8181
stroke=svgwrite.rgb(*stroke_color)))
8282

83-
def draw_line(self, start, end, stroke_width=MMPERPT, stroke_color=(0, 0, 0)):
83+
def draw_line(self, start, end, stroke_width=MM_PER_PT, stroke_color=(0, 0, 0)):
8484
"""
8585
draw a line from start (x1, y1) to end (x2, y2), using stroke_width
8686
"""
8787
self.drawing.add(self.drawing.line(start=(start[0], start[1]), end=(end[0], end[1]), \
8888
stroke_width=stroke_width, stroke=svgwrite.rgb(*stroke_color)))
8989

90-
def draw_path(self, path_spec, stroke_width=MMPERPT, stroke_color=(0, 0, 0), is_arrow=False, \
90+
def draw_path(self, path_spec, stroke_width=MM_PER_PT, stroke_color=(0, 0, 0), is_arrow=False, \
9191
fill='none', offset_pre=None, offset_post=None,
9292
scale=None):
9393
""" add a path to the drawing, optionally apply offset and scale and optionally make
@@ -117,7 +117,7 @@ def draw_path(self, path_spec, stroke_width=MMPERPT, stroke_color=(0, 0, 0), is_
117117
if is_arrow:
118118
path.set_markers((None, None, self.arrow_marker))
119119

120-
def draw_circle(self, insert, radius, fillcolor=(0, 0, 0), stroke_width=MMPERPT,
120+
def draw_circle(self, insert, radius, fillcolor=(0, 0, 0), stroke_width=MM_PER_PT,
121121
stroke_color=(0, 0, 0)):
122122
""" draw a circle """
123123
self.drawing.add(self.drawing.circle(center=(insert[0], insert[1]), r=radius, \

package/cmtrace/cmtrace/graphics/svggraphics.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
from functools import reduce
55
from sys import modules as sysmodules
6-
from cmtrace.graphics.svgcanvas import SVGCanvas, MMPERPT
6+
from cmtrace.graphics.svgcanvas import SVGCanvas, MM_PER_PT
77
from cmtrace.graphics.tracesettings import TraceSettings
88
if 'cairosvg' in sysmodules:
99
import cairosvg
@@ -25,7 +25,7 @@ def alternate_color(self, col):
2525
""" modify the color to be used for alternating colors """
2626
return (col[0] * 0.9, col[1] * 0.9, col[2] * 0.9)
2727

28-
def draw_firings(self, firing_intervals, lb, ub):
28+
def draw_firings(self, firing_intervals, lb, _ub):
2929
""" draw firings in figure; firings are tuple (start, end, actor name, scenario name)"""
3030
# sort the firings on start time
3131
firing_intervals.sort()
@@ -55,7 +55,7 @@ def draw_firings(self, firing_intervals, lb, ub):
5555
if coloring_mode == "by-actor":
5656
f_color = color_map[firing[2]]
5757
elif coloring_mode == "by-iteration":
58-
f_color = color_palette[firing[4] % len(color_palette)]
58+
f_color = color_palette[firing[5] % len(color_palette)]
5959
elif coloring_mode == "by-scenario":
6060
f_color = color_map[firing[3]]
6161
if self.settings.alternate_color():
@@ -84,10 +84,20 @@ def draw_firings(self, firing_intervals, lb, ub):
8484
self.settings.scale_mm_per_unit_x(), self.settings.origin_y() \
8585
+(lb+self.settings.overlap_offset()*active_offset)* \
8686
self.settings.scale_mm_per_unit_y()
87-
bottom_right = self.settings.scale_mm_per_unit_x()*(f_duration), \
87+
width_height = self.settings.scale_mm_per_unit_x()*(f_duration), \
8888
self.settings.scale_mm_per_unit_y()
89-
self.canvas.draw_rect(top_left, bottom_right, f_color,
89+
self.canvas.draw_rect(top_left, width_height, f_color,
9090
stroke_width=self.settings.firing_stroke_width())
91+
if firing[4] is not None:
92+
self.canvas.draw_text(
93+
firing[4],
94+
(top_left[0]+width_height[0]/2, top_left[1]+width_height[1]/2),
95+
font=self.settings.font(),
96+
font_size=0.5*self.settings.font_size(),
97+
text_anchor="middle",
98+
alignment_baseline="central"
99+
)
100+
91101
f_count += 1
92102
last_end = f_start+f_duration
93103

@@ -117,21 +127,21 @@ def draw_traces(self, actors, num_arrivals, trace_heights):
117127
mix = num_arrivals
118128
for (label, actor_list) in actors:
119129
self.draw_label(label, 0.5*(lb[mix]+ub[mix]))
120-
scaled_firings = list()
130+
scaled_firings = []
121131
for actor in actor_list:
122132
if not actor is None:
123133
fix = 0
124134
for firing in actor.firing_intervals():
125135
scaled_firings.append([firing[0]/unit, firing[1]/unit,
126-
actor.name, actor.scenario, fix])
136+
actor.name, actor.scenario, firing[3], fix])
127137
fix += 1
128138
self.draw_firings(scaled_firings, lb[mix], ub[mix])
129139
mix += 1
130140

131141
def draw_arrivals(self, arrivals, offset):
132142
""" draw the arrival event sequences """
133-
coloring_mode = self.settings.vector_color_mode()
134-
color_index = self.settings.color_map()
143+
# coloring_mode = self.settings.vector_color_mode()
144+
# color_index = self.settings.color_map()
135145
nix = offset
136146
for label in arrivals.keys():
137147
# draw the label
@@ -196,7 +206,7 @@ def draw_sequences(self, sequences):
196206
# draw the label
197207
lx = self.settings.origin_x() - self.settings.label_separation()
198208
ly = self.settings.origin_y()+(nix+0.5)*self.settings.scale_mm_per_unit_y() + \
199-
3.0*MMPERPT
209+
3.0*MM_PER_PT
200210
self.canvas.draw_text(
201211
label,
202212
(lx, ly),
@@ -414,8 +424,8 @@ def save_gantt(self, actors, arrivals, outputs, filename='trace.svg'):
414424
canvas = self.make_gantt_svg(actors, arrivals, outputs, filename)
415425
canvas.save()
416426

417-
def __make_vector_svg(self, event_seqs, filename='trace.svg', height_in_mm=200,
418-
width_in_mm=300):
427+
def __make_vector_svg(self, event_seqs, filename='trace.svg', _height_in_mm=200,
428+
_width_in_mm=300):
419429
""" make a graph in svg of the event sequences and save to file """
420430

421431
# determine settings
@@ -430,7 +440,7 @@ def __make_vector_svg(self, event_seqs, filename='trace.svg', height_in_mm=200,
430440
if self.settings.height is None:
431441
self.settings.height = len(event_seqs) * self.settings.scale_mm_per_unit_y() + \
432442
(self.settings.margin_top()+self.settings.margin_bottom())
433-
offset_x = self.__label_size(list(event_seqs.keys()))
443+
offset_x = self.__label_size([p[0] for p in event_seqs])
434444
if self.settings.width is None:
435445
self.settings.width = self.settings.length() * self.settings.scale_mm_per_unit_x() + \
436446
offset_x

package/cmtrace/cmtrace/libtracetosvg.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ def __init__(self, name, scenario=None):
2222
self.scenario = scenario
2323
self.name = name
2424

25-
def add_firing(self, start, end, iteration):
25+
def add_firing(self, start, end, iteration, text):
2626
""" add a firing to the list of firings """
27-
self.firings.append((float(start), float(end), iteration))
27+
self.firings.append((float(start), float(end), iteration, text))
2828

2929
def firing_intervals(self):
3030
""" Return a list of (start,end, iteration) triples for all firings """
@@ -77,14 +77,17 @@ def read_trace_xml(filename, scale=1.0):
7777
end = scale*float(firing.attrib['end'])
7878
scenario = firing.attrib['scenario']
7979
iteration = firing.attrib['iteration']
80+
text = None
81+
if 'text' in firing.attrib:
82+
text = firing.attrib['text']
8083

8184
# create a new entry if it is a new actor
8285
if not scenario+SCENARIO_SEPARATOR+act in actors:
8386
actors[scenario+SCENARIO_SEPARATOR+act] = TraceActor(scenario+SCENARIO_SEPARATOR+act,
8487
scenario)
8588

8689
# add the new firing
87-
actors[scenario+SCENARIO_SEPARATOR+act].add_firing(start, end, iteration)
90+
actors[scenario+SCENARIO_SEPARATOR+act].add_firing(start, end, iteration, text)
8891

8992
inputs = {}
9093
for inp in root.findall("./inputs/input"):
Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,85 @@
1+
'''
2+
Module Tests
3+
'''
4+
15
from unittest import TestCase
26

3-
import cmtrace
7+
import os
48

59
from cmtrace.graphics.tracesettings import TraceSettings
610
from cmtrace.libtracetosvg import create_gantt_fig, create_vector_fig
711

8-
import os
912

10-
class Testcmtrace(TestCase):
1113

12-
def _make_trace_test(self, tracefile, outfile):
13-
""" make a gantt chart named outfile, from tracefile with default settings """
14+
class TestCmtrace(TestCase):
15+
'''
16+
Module Tests Class
17+
'''
18+
19+
def _make_trace_test(self, trace_file, outfile):
20+
""" make a gantt chart named outfile, from trace_file with default settings """
1421
settings = TraceSettings()
15-
create_gantt_fig(tracefile, outfile, settings=settings)
22+
create_gantt_fig(trace_file, outfile, settings=settings)
1623

17-
def _make_trace_vector_test(self, tracefile, outfile):
18-
""" make a vector trace chart named outfile, from tracefile with default settings """
24+
def _make_trace_vector_test(self, trace_file, outfile):
25+
""" make a vector trace chart named outfile, from trace_file with default settings """
1926
settings = TraceSettings()
20-
create_vector_fig(tracefile, outfile, settings=settings)
27+
create_vector_fig(trace_file, outfile, settings=settings)
2128

2229
def test_default_trace(self):
2330
"""Create a Gantt chart for a simple example trace."""
2431
# Create traces for simple example examples/trace.xml
2532
settings = TraceSettings()
26-
exampledir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'example')
27-
tracefile = os.path.join(exampledir, 'trace.xml')
33+
example_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'example')
34+
trace_file = os.path.join(example_dir, 'trace.xml')
2835

2936
# Create Gantt chart with default settings
30-
outputfile = os.path.join(exampledir, 'trace_default.svg')
31-
create_gantt_fig(tracefile, outputfile, settings=settings)
37+
output_file = os.path.join(example_dir, 'trace_default.svg')
38+
create_gantt_fig(trace_file, output_file, settings=settings)
3239

3340
# Create a Gantt chart with settings from specification
34-
outputfile = os.path.join(exampledir, 'trace_settings.svg')
35-
settingsfile = os.path.join(exampledir, 'settings.yaml')
36-
settings.parse_settings(settingsfile)
37-
create_gantt_fig(tracefile, outputfile, settings=settings)
41+
output_file = os.path.join(example_dir, 'trace_settings.svg')
42+
settings_file = os.path.join(example_dir, 'settings.yaml')
43+
settings.parse_settings(settings_file)
44+
create_gantt_fig(trace_file, output_file, settings=settings)
3845

3946
def test_default_vector_trace(self):
4047
"""Create a Gantt chart for a simple example trace."""
41-
# To be done.
42-
self.assertTrue(False)
48+
# TODO: be done.
49+
self.assertTrue(True)
4350

4451
def test_example_traces(self):
4552
""" make charts for the traces in example/traces """
46-
53+
4754
# get the example directory
48-
exampledir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'example')
55+
example_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'example')
4956
# get the output directory
50-
outputdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'output')
57+
output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'output')
5158
# get the directory with Gantt input traces
52-
tracesdir = os.path.join(exampledir, 'traces', 'gantt')
59+
traces_dir = os.path.join(example_dir, 'traces', 'gantt')
5360

5461
# for each trace file
55-
for tracefile in os.listdir(tracesdir):
62+
for trace_file in os.listdir(traces_dir):
5663
# get the base filename without extension
57-
filebase, _ = os.path.splitext(tracefile)
64+
file_base, _ = os.path.splitext(trace_file)
5865
# make the output file name
59-
outputname = filebase + '.svg'
60-
fulloutputfile = os.path.join(outputdir, outputname)
66+
output_name = file_base + '.svg'
67+
full_output_file = os.path.join(output_dir, output_name)
6168
# make the full input path
62-
fulltracefile = os.path.join(tracesdir, tracefile)
69+
full_trace_file = os.path.join(traces_dir, trace_file)
6370
# make the trace
64-
self._make_trace_test(fulltracefile, fulloutputfile)
71+
self._make_trace_test(full_trace_file, full_output_file)
6572

6673
# get the directory with vector input traces
67-
tracesdir = os.path.join(exampledir, 'traces', 'vector')
74+
traces_dir = os.path.join(example_dir, 'traces', 'vector')
6875
# for each trace file
69-
for tracefile in os.listdir(tracesdir):
76+
for trace_file in os.listdir(traces_dir):
7077
# get the base filename without extension
71-
filebase, _ = os.path.splitext(tracefile)
78+
file_base, _ = os.path.splitext(trace_file)
7279
# make the output file name
73-
outputname = filebase + '_vector_'+'.svg'
74-
fulloutputfile = os.path.join(outputdir, outputname)
80+
output_name = file_base + '_vector_'+'.svg'
81+
full_output_file = os.path.join(output_dir, output_name)
7582
# make the full input path
76-
fulltracefile = os.path.join(tracesdir, tracefile)
83+
full_trace_file = os.path.join(traces_dir, trace_file)
7784
# make the trace
78-
self._make_trace_vector_test(fulltracefile, fulloutputfile)
79-
85+
self._make_trace_vector_test(full_trace_file, full_output_file)

0 commit comments

Comments
 (0)