io.hpp 16.7 KB
Newer Older
Gauthier Quesnel's avatar
Gauthier Quesnel committed
1
2
3
4
5
6
7
8
9
10
// Copyright (c) 2020 INRA Distributed under the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef ORG_VLEPROJECT_IRRITATOR_IO_2020
#define ORG_VLEPROJECT_IRRITATOR_IO_2020

#include <irritator/core.hpp>

#include <algorithm>
11
12
#include <istream>
#include <ostream>
Gauthier Quesnel's avatar
Gauthier Quesnel committed
13
14
15
16
17
18

namespace irt {

class reader
{
private:
19
    std::istream& is;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
20

Gauthier Quesnel's avatar
Gauthier Quesnel committed
21
    array<model_id> map;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
22
23
24
    int model_error = 0;
    int connection_error = 0;

25
26
27
    char temp_1[32];
    char temp_2[32];

Gauthier Quesnel's avatar
Gauthier Quesnel committed
28
public:
29
30
    reader(std::istream& is_) noexcept
      : is(is_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
31
32
    {}

33
    ~reader() noexcept = default;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
34
35
36
37
38

    status operator()(simulation& sim) noexcept
    {
        int model_number = 0;

39
        irt_return_if_fail((is >> model_number), status::io_file_format_error);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
40
41
42
43
44
        irt_return_if_fail(model_number > 0,
                           status::io_file_format_model_number_error);

        irt_return_if_bad(map.init(model_number));

Gauthier Quesnel's avatar
Gauthier Quesnel committed
45
        std::fill_n(std::begin(map), std::size(map), static_cast<model_id>(0));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
46
47
48
49

        int id;

        for (int i = 0; i != model_number; ++i, ++model_error) {
50
51
            irt_return_if_fail((is >> id >> temp_1 >> temp_2),
                               status::io_file_format_model_error);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
52

Gauthier Quesnel's avatar
Gauthier Quesnel committed
53
54
55
            irt_return_if_fail(0 <= id && id < model_number,
                               status::io_file_format_model_error);

56
            irt_return_if_bad(read(sim, id, temp_1, temp_2));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
57
58
        }

59
        while (is) {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
60
61
            int mdl_src_index, port_src_index, mdl_dst_index, port_dst_index;

62
63
64
65
            if (!(is >> mdl_src_index >> port_src_index >> mdl_dst_index >>
                  port_dst_index)) {
                if (is.eof())
                    break;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
66

67
68
                irt_bad_return(status::io_file_format_error);
            }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
69

70
71
            auto* mdl_src = sim.models.try_to_get(mdl_src_index);
            irt_return_if_fail(mdl_src, status::io_file_format_model_unknown);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
72

73
74
            auto* mdl_dst = sim.models.try_to_get(mdl_dst_index);
            irt_return_if_fail(mdl_dst, status::io_file_format_model_unknown);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
75

76
77
            output_port_id output_port;
            input_port_id input_port;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
78

79
80
81
82
            irt_return_if_bad(
              sim.get_output_port_id(*mdl_src, port_src_index, &output_port));
            irt_return_if_bad(
              sim.get_input_port_id(*mdl_dst, port_dst_index, &input_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
83

84
85
            irt_return_if_bad(sim.connect(output_port, input_port));
            ++connection_error;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
86
87
88
89
90
91
        }

        return status::success;
    }

private:
92
93
    bool convert(const std::string_view dynamics_name,
                 dynamics_type* type) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
94
    {
95
        if (dynamics_name == "none") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
96
97
98
99
            *type = dynamics_type::none;
            return true;
        }

100
        if (dynamics_name == "integrator") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
101
102
103
104
            *type = dynamics_type::integrator;
            return true;
        }

105
        if (dynamics_name == "quantifier") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
106
107
108
109
            *type = dynamics_type::quantifier;
            return true;
        }

110
        if (dynamics_name == "adder_2") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
111
112
113
114
            *type = dynamics_type::adder_2;
            return true;
        }

115
        if (dynamics_name == "adder_3") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
116
117
118
119
            *type = dynamics_type::adder_3;
            return true;
        }

120
        if (dynamics_name == "adder_4") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
121
122
123
124
            *type = dynamics_type::adder_4;
            return true;
        }

125
        if (dynamics_name == "mult_2") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
126
127
128
129
            *type = dynamics_type::mult_2;
            return true;
        }

130
        if (dynamics_name == "mult_3") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
131
132
133
134
            *type = dynamics_type::mult_3;
            return true;
        }

135
        if (dynamics_name == "mult_4") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
136
137
138
139
            *type = dynamics_type::mult_4;
            return true;
        }

140
        if (dynamics_name == "counter") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
141
142
143
144
            *type = dynamics_type::counter;
            return true;
        }

145
        if (dynamics_name == "generator") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
146
147
148
149
            *type = dynamics_type::generator;
            return true;
        }

150
        if (dynamics_name == "constant") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
151
152
153
154
            *type = dynamics_type::constant;
            return true;
        }

155
        if (dynamics_name == "cross") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
156
157
158
159
            *type = dynamics_type::cross;
            return true;
        }

160
161
162
163
164
        if (dynamics_name == "accumulator_2") {
            *type = dynamics_type::accumulator_2;
            return true;
        }

165
        if (dynamics_name == "time_func") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
166
167
168
169
170
171
172
            *type = dynamics_type::time_func;
            return true;
        }

        return false;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
173
174
175
176
    status read(simulation& sim,
                int id,
                const char* name,
                const char* dynamics_name) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
177
178
179
180
181
182
183
    {
        dynamics_type type;

        irt_return_if_fail(convert(dynamics_name, &type),
                           status::io_file_format_dynamics_unknown);

        model_id mdl = static_cast<model_id>(0);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
184
        auto ret = sim.dispatch(type, [this, &sim, name](auto& dyn_models) {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
185
186
187
188
189
190
191
192
193
194
195
196
197
            irt_return_if_fail(dyn_models.can_alloc(1),
                               status::io_file_format_dynamics_limit_reach);
            auto& dyn = dyn_models.alloc();
            auto dyn_id = dyn_models.get_id(dyn);

            sim.alloc(dyn, dyn_id, name);

            irt_return_if_fail(read(dyn),
                               status::io_file_format_dynamics_init_error);

            return status::success;
        });

Gauthier Quesnel's avatar
Gauthier Quesnel committed
198
199
        irt_return_if_bad(ret);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
200
        map[id] = mdl;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
201
202

        return status::success;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
203
204
205
206
207
208
209
210
211
    }

    bool read(none& /*dyn*/) noexcept
    {
        return true;
    }

    bool read(integrator& dyn) noexcept
    {
212
        return !!(is >> dyn.default_current_value >> dyn.default_reset_value);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
213
214
215
216
    }

    bool read(quantifier& dyn) noexcept
    {
217
218
        if (!(is >> dyn.default_step_size >> dyn.default_past_length >>
              temp_1 >> temp_2))
Gauthier Quesnel's avatar
Gauthier Quesnel committed
219
220
            return false;

221
        if (std::strcmp(temp_1, "possible") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
222
            dyn.default_adapt_state = quantifier::adapt_state::possible;
223
        else if (std::strcmp(temp_1, "impossible") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
224
            dyn.default_adapt_state = quantifier::adapt_state::impossible;
225
        else if (std::strcmp(temp_1, "done") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
226
227
228
229
            dyn.default_adapt_state = quantifier::adapt_state::done;
        else
            return false;

230
        if (std::strcmp(temp_2, "true") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
231
            dyn.default_zero_init_offset = true;
232
        else if (std::strcmp(temp_2, "false") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
233
234
235
236
237
238
239
240
241
            dyn.default_zero_init_offset = false;
        else
            return false;

        return true;
    }

    bool read(adder_2& dyn) noexcept
    {
242
243
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
244
245
246
247
    }

    bool read(adder_3& dyn) noexcept
    {
248
249
250
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_input_coeffs[0] >>
                  dyn.default_input_coeffs[1] >> dyn.default_input_coeffs[2]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
251
252
253
254
    }

    bool read(adder_4& dyn) noexcept
    {
255
256
257
258
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_values[3] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1] >>
                  dyn.default_input_coeffs[2] >> dyn.default_input_coeffs[3]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
259
260
261
262
    }

    bool read(mult_2& dyn) noexcept
    {
263
264
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
265
266
267
268
    }

    bool read(mult_3& dyn) noexcept
    {
269
270
271
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_input_coeffs[0] >>
                  dyn.default_input_coeffs[1] >> dyn.default_input_coeffs[2]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
272
273
274
275
    }

    bool read(mult_4& dyn) noexcept
    {
276
277
278
279
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_values[3] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1] >>
                  dyn.default_input_coeffs[2] >> dyn.default_input_coeffs[3]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
280
281
282
283
284
285
286
    }

    bool read(counter& /*dyn*/) noexcept
    {
        return true;
    }

287
    bool read(generator& dyn) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
288
    {
289
        return !!(is >> dyn.default_value >> dyn.default_period >>
290
                  dyn.default_offset);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
291
292
293
294
    }

    bool read(constant& dyn) noexcept
    {
295
        return !!(is >> dyn.default_value);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
296
297
298
299
    }

    bool read(cross& dyn) noexcept
    {
300
        return !!(is >> dyn.default_threshold);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
301
302
    }

303
304
305
306
307
    bool read(accumulator_2& /*dyn*/) noexcept
    {
        return true;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
308
309
    bool read(time_func& dyn) noexcept
    {
310
        if (!(is >> temp_1))
Gauthier Quesnel's avatar
Gauthier Quesnel committed
311
312
            return false;

313
        if (std::strcmp(temp_1, "square") == 0)
314
            dyn.default_f = &square_time_function;
315
        else
316
            dyn.default_f = &time_function;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
317
318
319
320
321
322
323

        return true;
    }
};

struct writer
{
324
    std::ostream& os;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
325

Gauthier Quesnel's avatar
Gauthier Quesnel committed
326
327
    array<model_id> map;

328
329
    writer(std::ostream& os_) noexcept
      : os(os_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
330
331
332
333
    {}

    status operator()(const simulation& sim) noexcept
    {
334
        os << sim.models.size() << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
335

Gauthier Quesnel's avatar
Gauthier Quesnel committed
336
337
338
339
        irt_return_if_bad(map.init(sim.models.size()));

        std::fill_n(std::begin(map), std::size(map), static_cast<model_id>(0));

Gauthier Quesnel's avatar
Gauthier Quesnel committed
340
        model* mdl = nullptr;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
341
        int id = 0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
342
343
344
        while (sim.models.next(mdl)) {
            const auto mdl_id = sim.models.get_id(mdl);

345
            os << id << ' ' << mdl->name.c_str() << ' ';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
346
            map[id] = mdl_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
347
348
349
350
351

            sim.dispatch(mdl->type, [this, mdl](auto& dyn_models) {
                write(dyn_models.get(mdl->id));
                return status::success;
            });
Gauthier Quesnel's avatar
Gauthier Quesnel committed
352
353

            ++id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
354
        }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
355

Gauthier Quesnel's avatar
Gauthier Quesnel committed
356
357
358
359
360
361
        irt::output_port* out = nullptr;
        while (sim.output_ports.next(out)) {
            for (auto dst : out->connections) {
                if (auto* in = sim.input_ports.try_to_get(dst); in) {
                    auto* mdl_src = sim.models.try_to_get(out->model);
                    auto* mdl_dst = sim.models.try_to_get(in->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
362

Gauthier Quesnel's avatar
Gauthier Quesnel committed
363
364
                    if (!(mdl_src && mdl_dst))
                        continue;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
365

Gauthier Quesnel's avatar
Gauthier Quesnel committed
366
367
                    int src_index = -1;
                    int dst_index = -1;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
368

Gauthier Quesnel's avatar
Gauthier Quesnel committed
369
                    irt_return_if_bad(
Gauthier Quesnel's avatar
Gauthier Quesnel committed
370
                      sim.get_input_port_index(*mdl_dst, dst, &dst_index));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
371

Gauthier Quesnel's avatar
Gauthier Quesnel committed
372
                    irt_return_if_bad(sim.get_output_port_index(
Gauthier Quesnel's avatar
Gauthier Quesnel committed
373
374
375
376
377
378
379
                      *mdl_src, sim.output_ports.get_id(out), &src_index));

                    auto it_out = std::find(map.begin(), map.end(), out->model);
                    auto it_in = std::find(map.begin(), map.end(), in->model);

                    assert(it_out != map.end());
                    assert(it_in != map.end());
Gauthier Quesnel's avatar
Gauthier Quesnel committed
380

381
                    os << std::distance(map.begin(), it_out) << ' ' << src_index
382
383
                       << ' ' << std::distance(map.begin(), it_in) << ' '
                       << dst_index << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
384
385
386
387
388
389
390
391
                }
            }
        }

        return status::success;
    }

private:
Gauthier Quesnel's avatar
Gauthier Quesnel committed
392
    void write(const none& /*dyn*/) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
393
    {
394
        os << "none\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
395
396
397
398
    }

    void write(const integrator& dyn) noexcept
    {
399
400
        os << "integrator " << dyn.default_current_value << ' '
           << dyn.default_reset_value << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
401
402
403
404
    }

    void write(const quantifier& dyn) noexcept
    {
405
406
407
408
409
410
411
412
413
        os << "quantifier " << dyn.default_step_size << ' '
           << dyn.default_past_length << ' '
           << ((dyn.default_adapt_state == quantifier::adapt_state::possible)
                 ? "possible "
                 : dyn.default_adapt_state ==
                       quantifier::adapt_state::impossible
                     ? "impossibe "
                     : "done ")
           << (dyn.default_zero_init_offset == true ? "true\n" : "false\n");
Gauthier Quesnel's avatar
Gauthier Quesnel committed
414
415
416
417
    }

    void write(const adder_2& dyn) noexcept
    {
418
419
420
        os << "adder_2 " << dyn.default_values[0] << ' '
           << dyn.default_values[1] << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
421
422
423
424
    }

    void write(const adder_3& dyn) noexcept
    {
425
426
427
428
        os << "adder_3 " << dyn.default_values[0] << ' '
           << dyn.default_values[1] << ' ' << dyn.default_values[2] << ' '
           << dyn.default_input_coeffs[0] << ' ' << dyn.default_input_coeffs[1]
           << ' ' << dyn.default_input_coeffs[2] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
429
430
431
432
    }

    void write(const adder_4& dyn) noexcept
    {
433
434
435
436
437
        os << "adder_4 " << dyn.default_values[0] << ' '
           << dyn.default_values[1] << ' ' << dyn.default_values[2] << ' '
           << dyn.default_values[3] << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << ' ' << dyn.default_input_coeffs[2]
           << ' ' << dyn.default_input_coeffs[3] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
438
439
440
441
    }

    void write(const mult_2& dyn) noexcept
    {
442
443
444
        os << "mult_2 " << dyn.default_values[0] << ' ' << dyn.default_values[1]
           << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
445
446
447
448
    }

    void write(const mult_3& dyn) noexcept
    {
449
450
451
452
        os << "mult_3 " << dyn.default_values[0] << ' ' << dyn.default_values[1]
           << ' ' << dyn.default_values[2] << ' ' << dyn.default_input_coeffs[0]
           << ' ' << dyn.default_input_coeffs[1] << ' '
           << dyn.default_input_coeffs[2] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
453
454
455
456
    }

    void write(const mult_4& dyn) noexcept
    {
457
458
459
460
461
        os << "mult_4 " << dyn.default_values[0] << ' ' << dyn.default_values[1]
           << ' ' << dyn.default_values[2] << ' ' << dyn.default_values[3]
           << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << ' ' << dyn.default_input_coeffs[2]
           << ' ' << dyn.default_input_coeffs[3] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
462
463
464
465
    }

    void write(const counter& /*dyn*/) noexcept
    {
466
        os << "counter\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
467
468
    }

469
    void write(const generator& dyn) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
470
    {
471
472
473
        os << "generator " << dyn.default_value << ' ' << dyn.default_period
           << ' ' << dyn.default_offset << '\n';
        ;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
474
475
476
477
    }

    void write(const constant& dyn) noexcept
    {
478
        os << "constant " << dyn.default_value << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
479
480
481
482
    }

    void write(const cross& dyn) noexcept
    {
483
        os << "cross " << dyn.default_threshold << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
484
485
    }

486
487
488
489
490
    void write(const accumulator_2& /*dyn*/) noexcept
    {
        os << "accumulator_2\n";
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
491
492
    void write(const time_func& dyn) noexcept
    {
493
494
        os << "time_func "
           << (dyn.default_f == &time_function ? "time\n" : "square\n");
Gauthier Quesnel's avatar
Gauthier Quesnel committed
495
496
497
498
499
500
    }
};

class dot_writer
{
private:
501
    std::ostream& os;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
502
503

public:
504
505
    dot_writer(std::ostream& os_)
      : os(os_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
    {}

    /* With input and output port.

    digraph graphname{
        graph[rankdir = "LR"];
        node[shape = "record"];
        edge[];

        "sum_a"[label = "sum-a | <f0> | <f1>"];

        "sum_a":f0->int_a[id = 1];
        sum_b->int_b[label = "2-10"];
        prod->sum_b[label = "3-4"];
        prod -> "sum_a":f0[label = "3-2"];
        int_a->qua_a[label = "4-11"];
        int_a->prod[label = "4-5"];
        int_a -> "sum_a":f1[label = "4-1"];
        int_b->qua_b[label = "5-12"];
        int_b->prod[label = "5-6"];
        int_b->sum_b[label = "5-3"];
        qua_a->int_a[label = "6-7"];
        qua_b->int_b[label = "7-9"];
    }
    */

    void operator()(const simulation& sim) noexcept
    {
534
        os << "digraph graphname {\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
535
536
537
538
539
540
541
542
543
544
545
546
547

        irt::output_port* output_port = nullptr;
        while (sim.output_ports.next(output_port)) {
            for (const irt::input_port_id dst : output_port->connections) {
                if (auto* input_port = sim.input_ports.try_to_get(dst);
                    input_port) {
                    auto* mdl_src = sim.models.try_to_get(output_port->model);
                    auto* mdl_dst = sim.models.try_to_get(input_port->model);

                    if (!(mdl_src && mdl_dst))
                        continue;

                    if (mdl_src->name.empty())
548
                        os << irt::get_key(output_port->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
549
                    else
550
551
552
                        os << mdl_src->name.c_str();

                    os << " -> ";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
553
554

                    if (mdl_dst->name.empty())
555
                        os << irt::get_key(input_port->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
556
                    else
557
                        os << mdl_dst->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
558

559
                    os << " [label=\"";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
560
561

                    if (output_port->name.empty())
562
563
                        os << irt::get_key(
                          sim.output_ports.get_id(*output_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
564
                    else
565
                        os << output_port->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
566

567
                    os << " - ";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
568
569

                    if (input_port->name.empty())
570
                        os << irt::get_key(sim.input_ports.get_id(*input_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
571
                    else
572
                        os << input_port->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
573

574
                    os << "\"];\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
575
576
577
578
579
580
581
582
583
                }
            }
        }
    }
};

} // namespace irt

#endif