• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pybricks / pybricks-micropython / 19016791193

02 Nov 2025 06:40PM UTC coverage: 57.167% (-2.6%) from 59.744%
19016791193

Pull #406

github

laurensvalk
bricks/virtualhub: Replace with embedded simulation.

Instead of using the newly introduced simhub alongside the virtualhub, we'll just replace the old one entirely now that it has reached feature parity. We can keep calling it the virtualhub.
Pull Request #406: New virtual hub for more effective debugging

41 of 48 new or added lines in 7 files covered. (85.42%)

414 existing lines in 53 files now uncovered.

4479 of 7835 relevant lines covered (57.17%)

17178392.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.16
/pybricks/common/pb_type_logger.c
1
// SPDX-License-Identifier: MIT
2
// Copyright (c) 2018-2023 The Pybricks Authors
3

4
#include "py/mpconfig.h"
5

6
#if PYBRICKS_PY_COMMON_LOGGER
7

8
#include <inttypes.h>
9
#include <stdbool.h>
10
#include <stdio.h>
11
#include <string.h>
12

13
#include <pbio/config.h>
14
#include <pbio/logger.h>
15
#include <pbio/int_math.h>
16
#include <pbio/servo.h>
17

18
#include "py/obj.h"
19
#include "py/runtime.h"
20
#include "py/mpconfig.h"
21

22
#include <pybricks/util_pb/pb_error.h>
23
#include <pybricks/util_mp/pb_obj_helper.h>
24
#include <pybricks/util_mp/pb_kwarg_helper.h>
25

26
/**
27
 * pybricks.tools.Logger class object
28
 */
29
typedef struct _tools_Logger_obj_t {
30
    mp_obj_base_t base;
31
    /**
32
     * Reference to pbio log object, which does not have its own data buffer.
33
     */
34
    pbio_log_t *log;
35
    /**
36
     * Data buffer that will be allocated on MicroPython heap.
37
     */
38
    int32_t *buf;
39
    /**
40
     * Number of columns, needed when starting log which happens after object creation.
41
     */
42
    uint8_t num_cols;
43
    /**
44
     * Buffer size. Used to free (renew) old data when resetting logger.
45
     */
46
    uint32_t last_size;
47
} tools_Logger_obj_t;
48

49
static mp_obj_t tools_Logger_start(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
×
50
    PB_PARSE_ARGS_METHOD(n_args, pos_args, kw_args,
×
51
        tools_Logger_obj_t, self,
52
        PB_ARG_REQUIRED(duration),
53
        PB_ARG_DEFAULT_INT(down_sample, 1));
54

55
    // Log only one row per divisor samples.
56
    mp_uint_t down_sample = pbio_int_math_max(pb_obj_get_int(down_sample_in), 1);
×
57
    mp_uint_t num_rows = pb_obj_get_int(duration_in) / PBIO_CONFIG_CONTROL_LOOP_TIME_MS / down_sample;
×
58

59
    // Size is number of rows times column width. All data are int32.
60
    mp_int_t size = num_rows * self->num_cols;
×
61
    self->buf = m_renew(int32_t, self->buf, self->last_size, size);
×
62
    self->last_size = size;
×
63

64
    // Indicates that background control loops may enter data in log.
65
    pbio_logger_start(self->log, self->buf, num_rows, self->num_cols, down_sample);
×
66

67
    return mp_const_none;
×
68
}
69
static MP_DEFINE_CONST_FUN_OBJ_KW(tools_Logger_start_obj, 1, tools_Logger_start);
70

71
static mp_obj_t tools_Logger_stop(mp_obj_t self_in) {
×
72
    tools_Logger_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
73

74
    // Indicates that background control loops log write more data.
75
    pbio_logger_stop(self->log);
×
76

77
    return mp_const_none;
×
78
}
79
static MP_DEFINE_CONST_FUN_OBJ_1(tools_Logger_stop_obj, tools_Logger_stop);
80

81
static mp_obj_t tools_Logger_save(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
×
82

83
    PB_PARSE_ARGS_METHOD(n_args, pos_args, kw_args,
×
84
        tools_Logger_obj_t, self,
85
        PB_ARG_DEFAULT_NONE(path));
86

87
    // Don't allow any more data to be added to logs.
88
    pbio_logger_stop(self->log);
×
89

90
    // Get log file path.
91
    const char *path = path_in == mp_const_none ? "log.txt" : mp_obj_str_get_str(path_in);
×
92

93
    #if PYBRICKS_PY_COMMON_LOGGER_REAL_FILE
94
    // Create an empty log file locally.
95
    FILE *log_file = fopen(path, "w");
×
96
    if (log_file == NULL) {
×
97
        pb_assert(PBIO_ERROR_IO);
×
98
    }
99
    #else
100

101
    // Tell IDE to open remote file.
102
    mp_printf(&mp_plat_print, "PB_OF:%s\n", path);
103
    #endif // PYBRICKS_PY_COMMON_LOGGER_REAL_FILE
104

105
    pbio_error_t err = PBIO_SUCCESS;
×
106

107
    // Write data to file line by line
108
    for (uint32_t row = 0; row < pbio_logger_get_num_rows_used(self->log); row++) {
×
109

110
        int32_t *row_data = pbio_logger_get_row_data(self->log, row);
×
111

112
        for (uint32_t col = 0; col < self->log->num_cols; col++) {
×
113

114
            // Write "-12345, " or "-12345\n" for last value on row.
115
            const char *format = col + 1 < self->log->num_cols ? "%d, " : "%d\n";
×
116

117
            // Write one value.
118
            #if PYBRICKS_PY_COMMON_LOGGER_REAL_FILE
119
            if (fprintf(log_file, format, row_data[col]) < 0) {
×
UNCOV
120
                break;
×
121
            }
122
            #else
123
            mp_printf(&mp_plat_print, format, row_data[col]);
124
            #endif // PYBRICKS_PY_COMMON_LOGGER_REAL_FILE
125
        }
126

127
        // Writing data can take a while, so give system some time too.
128
        MICROPY_VM_HOOK_LOOP
×
129
        mp_handle_pending(true);
×
130
    }
131

132
    #if PYBRICKS_PY_COMMON_LOGGER_REAL_FILE
133
    // Close the file
134
    if (fclose(log_file) != 0) {
×
135
        err = PBIO_ERROR_IO;
×
136
    }
137
    #else
138
    mp_print_str(&mp_plat_print, "PB_EOF\n");
139
    #endif // PYBRICKS_PY_COMMON_LOGGER_REAL_FILE
140

141
    pb_assert(err);
×
142
    return mp_const_none;
×
143
}
144
static MP_DEFINE_CONST_FUN_OBJ_KW(tools_Logger_save_obj, 1, tools_Logger_save);
145

146
// dir(pybricks.tools.Logger)
147
static const mp_rom_map_elem_t tools_Logger_locals_dict_table[] = {
148
    { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&tools_Logger_start_obj) },
149
    { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&tools_Logger_stop_obj) },
150
    { MP_ROM_QSTR(MP_QSTR_save), MP_ROM_PTR(&tools_Logger_save_obj) },
151
};
152
static MP_DEFINE_CONST_DICT(tools_Logger_locals_dict, tools_Logger_locals_dict_table);
153

154
// type(pybricks.tools.Logger)
155
static MP_DEFINE_CONST_OBJ_TYPE(tools_Logger_type,
156
    MP_QSTR_Logger,
157
    MP_TYPE_FLAG_NONE,
158
    locals_dict, &tools_Logger_locals_dict);
159

160
mp_obj_t common_Logger_obj_make_new(pbio_log_t *log, uint8_t num_values) {
38✔
161
    tools_Logger_obj_t *logger = mp_obj_malloc(tools_Logger_obj_t, &tools_Logger_type);
38✔
162
    logger->log = log;
38✔
163
    logger->num_cols = num_values + PBIO_LOGGER_NUM_DEFAULT_COLS;
38✔
164
    return MP_OBJ_FROM_PTR(logger);
38✔
165
}
166

167
#endif // PYBRICKS_PY_COMMON_LOGGER
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc