io.hpp 16.6 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
        if (dynamics_name == "time_func") {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
161
162
163
164
165
166
167
            *type = dynamics_type::time_func;
            return true;
        }

        return false;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
168
169
170
171
    status read(simulation& sim,
                int id,
                const char* name,
                const char* dynamics_name) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
172
173
174
175
176
177
178
    {
        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
179
        auto ret = sim.dispatch(type, [this, &sim, name](auto& dyn_models) {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
180
181
182
183
184
185
186
187
188
189
190
191
192
            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
193
194
        irt_return_if_bad(ret);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
195
        map[id] = mdl;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
196
197

        return status::success;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
198
199
200
201
202
203
204
205
206
    }

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

    bool read(integrator& dyn) noexcept
    {
207
        return !!(is >> dyn.default_current_value >> dyn.default_reset_value);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
208
209
210
211
    }

    bool read(quantifier& dyn) noexcept
    {
212
213
        if (!(is >> dyn.default_step_size >> dyn.default_past_length >>
              temp_1 >> temp_2))
Gauthier Quesnel's avatar
Gauthier Quesnel committed
214
215
            return false;

216
        if (std::strcmp(temp_1, "possible") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
217
            dyn.default_adapt_state = quantifier::adapt_state::possible;
218
        else if (std::strcmp(temp_1, "impossible") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
219
            dyn.default_adapt_state = quantifier::adapt_state::impossible;
220
        else if (std::strcmp(temp_1, "done") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
221
222
223
224
            dyn.default_adapt_state = quantifier::adapt_state::done;
        else
            return false;

225
        if (std::strcmp(temp_2, "true") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
226
            dyn.default_zero_init_offset = true;
227
        else if (std::strcmp(temp_2, "false") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
228
229
230
231
232
233
234
235
236
            dyn.default_zero_init_offset = false;
        else
            return false;

        return true;
    }

    bool read(adder_2& dyn) noexcept
    {
237
238
        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
239
240
241
242
    }

    bool read(adder_3& dyn) noexcept
    {
243
244
245
        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
246
247
248
249
    }

    bool read(adder_4& dyn) noexcept
    {
250
251
252
253
        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
254
255
256
257
    }

    bool read(mult_2& dyn) noexcept
    {
258
259
        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
260
261
262
263
    }

    bool read(mult_3& dyn) noexcept
    {
264
265
266
        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
267
268
269
270
    }

    bool read(mult_4& dyn) noexcept
    {
271
272
273
274
        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
275
276
277
278
279
280
281
    }

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

282
    bool read(generator& dyn) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
283
    {
284
285
286
        return !!(is >> dyn.default_value >> dyn.default_period >>
                  dyn.default_offset);;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
287
288
289
290
    }

    bool read(constant& dyn) noexcept
    {
291
        return !!(is >> dyn.default_value);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
292
293
294
295
    }

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

299
300
301
302
303
    bool read(accumulator_2& /*dyn*/) noexcept
    {
        return true;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
304
305
    bool read(time_func& dyn) noexcept
    {
306
        if (!(is >> temp_1))
Gauthier Quesnel's avatar
Gauthier Quesnel committed
307
308
            return false;

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

        return true;
    }
};

struct writer
{
320
    std::ostream& os;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
321

Gauthier Quesnel's avatar
Gauthier Quesnel committed
322
323
    array<model_id> map;

324
325
    writer(std::ostream& os_) noexcept
      : os(os_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
326
327
328
329
    {}

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

Gauthier Quesnel's avatar
Gauthier Quesnel committed
332
333
334
335
        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
336
        model* mdl = nullptr;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
337
        int id = 0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
338
339
340
        while (sim.models.next(mdl)) {
            const auto mdl_id = sim.models.get_id(mdl);

341
            os << id << ' ' << mdl->name.c_str() << ' ';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
342
            map[id] = mdl_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
343
344
345
346
347

            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
348
349

            ++id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
350
        }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
351

Gauthier Quesnel's avatar
Gauthier Quesnel committed
352
353
354
355
356
357
        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
358

Gauthier Quesnel's avatar
Gauthier Quesnel committed
359
360
                    if (!(mdl_src && mdl_dst))
                        continue;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
361

Gauthier Quesnel's avatar
Gauthier Quesnel committed
362
363
                    int src_index = -1;
                    int dst_index = -1;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
364

Gauthier Quesnel's avatar
Gauthier Quesnel committed
365
                    irt_return_if_bad(
Gauthier Quesnel's avatar
Gauthier Quesnel committed
366
                      sim.get_input_port_index(*mdl_dst, dst, &dst_index));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
367

Gauthier Quesnel's avatar
Gauthier Quesnel committed
368
                    irt_return_if_bad(sim.get_output_port_index(
Gauthier Quesnel's avatar
Gauthier Quesnel committed
369
370
371
372
373
374
375
                      *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
376

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

        return status::success;
    }

private:
Gauthier Quesnel's avatar
Gauthier Quesnel committed
388
    void write(const none& /*dyn*/) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
389
    {
390
        os << "none\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
391
392
393
394
    }

    void write(const integrator& dyn) noexcept
    {
395
396
        os << "integrator " << dyn.default_current_value << ' '
           << dyn.default_reset_value << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
397
398
399
400
    }

    void write(const quantifier& dyn) noexcept
    {
401
402
403
404
405
406
407
408
409
        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
410
411
412
413
    }

    void write(const adder_2& dyn) noexcept
    {
414
415
416
        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
417
418
419
420
    }

    void write(const adder_3& dyn) noexcept
    {
421
422
423
424
        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
425
426
427
428
    }

    void write(const adder_4& dyn) noexcept
    {
429
430
431
432
433
        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
434
435
436
437
    }

    void write(const mult_2& dyn) noexcept
    {
438
439
440
        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
441
442
443
444
    }

    void write(const mult_3& dyn) noexcept
    {
445
446
447
448
        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
449
450
451
452
    }

    void write(const mult_4& dyn) noexcept
    {
453
454
455
456
457
        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
458
459
460
461
    }

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

465
    void write(const generator& dyn) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
466
    {
467
468
        os << "generator"<< dyn.default_value << ' ' << dyn.default_period
           << ' ' << dyn.default_offset << '\n';;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
469
470
471
472
    }

    void write(const constant& dyn) noexcept
    {
473
        os << "constant " << dyn.default_value << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
474
475
476
477
    }

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

481
482
483
484
485
    void write(const accumulator_2& /*dyn*/) noexcept
    {
        os << "accumulator_2\n";
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
486
487
    void write(const time_func& dyn) noexcept
    {
488
489
        os << "time_func "
           << (dyn.default_f == &time_function ? "time\n" : "square\n");
Gauthier Quesnel's avatar
Gauthier Quesnel committed
490
491
492
493
494
495
    }
};

class dot_writer
{
private:
496
    std::ostream& os;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
497
498

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

    /* 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
    {
529
        os << "digraph graphname {\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
530
531
532
533
534
535
536
537
538
539
540
541
542

        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())
543
                        os << irt::get_key(output_port->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
544
                    else
545
546
547
                        os << mdl_src->name.c_str();

                    os << " -> ";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
548
549

                    if (mdl_dst->name.empty())
550
                        os << irt::get_key(input_port->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
551
                    else
552
                        os << mdl_dst->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
553

554
                    os << " [label=\"";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
555
556

                    if (output_port->name.empty())
557
558
                        os << irt::get_key(
                          sim.output_ports.get_id(*output_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
559
                    else
560
                        os << output_port->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
561

562
                    os << " - ";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
563
564

                    if (input_port->name.empty())
565
                        os << irt::get_key(sim.input_ports.get_id(*input_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
566
                    else
567
                        os << input_port->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
568

569
                    os << "\"];\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
570
571
572
573
574
575
576
577
578
                }
            }
        }
    }
};

} // namespace irt

#endif
579