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

pybricks / pybricks-micropython / 10096696648

23 Jul 2024 01:21PM UTC coverage: 56.025% (-0.6%) from 56.592%
10096696648

Pull #246

github

laurensvalk
pybricks.tools.AppData: Implement write.
Pull Request #246: pybricks.tools: Add hostbuffer to receive IDE data

9 of 81 new or added lines in 4 files covered. (11.11%)

11 existing lines in 4 files now uncovered.

3766 of 6722 relevant lines covered (56.02%)

20056125.69 hits per line

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

0.0
/pybricks/tools/pb_type_app_data.c
1
// SPDX-License-Identifier: MIT
2
// Copyright (c) 2024 The Pybricks Authors
3

4
#include "py/mpconfig.h"
5

6
#if PYBRICKS_PY_TOOLS_APP_DATA
7

8
#include <string.h>
9

10
#include <pbsys/command.h>
11
#include <pbdrv/bluetooth.h>
12

13
#include "py/mphal.h"
14
#include "py/objstr.h"
15

16
#include <pybricks/tools.h>
17
#include <pybricks/tools/pb_type_awaitable.h>
18

19
#include <pybricks/util_mp/pb_kwarg_helper.h>
20
#include <pybricks/util_mp/pb_obj_helper.h>
21
#include <pybricks/util_pb/pb_error.h>
22

23
typedef struct _pb_type_app_data_obj_t {
24
    mp_obj_base_t base;
25
    pbio_task_t tx_task;
26
    pbdrv_bluetooth_send_context_t tx_context;
27
    mp_obj_t rx_format;
28
    mp_obj_str_t rx_bytes_obj;
29
    uint8_t tx_buffer[PBDRV_BLUETOOTH_MAX_MTU_SIZE];
30
    uint8_t rx_buffer[] __attribute__((aligned(4)));
31
} pb_type_app_data_obj_t;
32

33
// pointer to dynamically allocated app_data singleton for driver callback.
34
static pb_type_app_data_obj_t *app_data_instance;
35

NEW
36
static pbio_error_t handle_incoming_app_data(uint16_t offset, uint32_t size, const uint8_t *data) {
×
37
    // Can't write if rx_buffer does not exist or isn't big enough.
NEW
38
    if (!app_data_instance || offset + size > app_data_instance->rx_bytes_obj.len) {
×
39
        return PBIO_ERROR_INVALID_ARG;
40
    }
NEW
41
    memcpy(app_data_instance->rx_buffer + offset, data, size);
×
NEW
42
    return PBIO_SUCCESS;
×
43
}
44

NEW
45
STATIC mp_obj_t pb_type_app_data_get_bytes(mp_obj_t self_in) {
×
NEW
46
    pb_type_app_data_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
47
    // Don't return internal bytes object but make a copy so the user bytes
48
    // object is constant as would be expected. Revisit: enable and return
49
    // a memoryview, especially if using large buffers.
NEW
50
    return mp_obj_new_bytes(self->rx_bytes_obj.data, self->rx_bytes_obj.len);
×
51
}
52
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_get_bytes_obj, pb_type_app_data_get_bytes);
53

NEW
54
STATIC mp_obj_t pb_type_app_data_get_values(mp_obj_t self_in) {
×
55

56
    // Implementation in MicroPython is static, so import from ustruct.unpack.
NEW
57
    mp_obj_t ustruct_unpack = pb_function_import_helper(MP_QSTR_ustruct, MP_QSTR_unpack);
×
58

59
    // Host (sender) is responsible for making sure that each individual
60
    // value remains valid, i.e. is written in a single chunk, since the
61
    // following may allocate, and thus be updated between unpacking values.
NEW
62
    pb_type_app_data_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
NEW
63
    return mp_call_function_2(ustruct_unpack, self->rx_format, MP_OBJ_FROM_PTR(&self->rx_bytes_obj));
×
64
}
65
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_get_values_obj, pb_type_app_data_get_values);
66

NEW
67
STATIC mp_obj_t pb_type_app_data_write_bytes(mp_obj_t self_in, mp_obj_t data_in) {
×
NEW
68
    pb_type_app_data_obj_t *self = MP_OBJ_TO_PTR(self_in);
×
69

70
    // Copy data to local buffer. Needs to remain valid while sending.
NEW
71
    size_t len;
×
NEW
72
    const char *data = mp_obj_str_get_data(data_in, &len);
×
73

NEW
74
    if (len > PBDRV_BLUETOOTH_MAX_MTU_SIZE - 1) {
×
NEW
75
        mp_raise_msg_varg(&mp_type_ValueError,
×
NEW
76
            MP_ERROR_TEXT("Cannot send more than %d bytes\n"), PBDRV_BLUETOOTH_MAX_MTU_SIZE - 1);
×
77
    }
78

NEW
79
    memcpy(self->tx_buffer + 1, data, len);
×
NEW
80
    self->tx_context.size = len + 1;
×
81

NEW
82
    pbdrv_bluetooth_send_queued(&self->tx_task, &self->tx_context);
×
NEW
83
    return pb_module_tools_pbio_task_wait_or_await(&self->tx_task);
×
84
}
85
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pb_type_app_data_write_bytes_obj, pb_type_app_data_write_bytes);
86

NEW
87
STATIC mp_obj_t pb_type_app_data_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
×
88

NEW
89
    PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
×
NEW
90
        PB_ARG_REQUIRED(rx_format));
×
91

92
    // Use ustruct.calcsize to parse user rx_format for size.
NEW
93
    mp_obj_t ustruct_calcsize = pb_function_import_helper(MP_QSTR_ustruct, MP_QSTR_calcsize);
×
NEW
94
    size_t size = mp_obj_get_int(mp_call_function_1(ustruct_calcsize, rx_format_in));
×
95

96
    // Can only create one instance for now.
NEW
97
    if (app_data_instance) {
×
NEW
98
        mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("host rx_buffer already allocated"));
×
99
    }
100

101
    // Use finalizer so we can deactivate the data callback when rx_buffer is garbage collected.
NEW
102
    app_data_instance = m_new_obj_var_with_finaliser(pb_type_app_data_obj_t, uint8_t, size);
×
NEW
103
    app_data_instance->base.type = type;
×
NEW
104
    app_data_instance->rx_format = rx_format_in;
×
105

106
    // Keep rx_buffer in bytes object rx_format for compatibility with unpack.
NEW
107
    app_data_instance->rx_bytes_obj.base.type = &mp_type_bytes;
×
NEW
108
    app_data_instance->rx_bytes_obj.len = size;
×
NEW
109
    app_data_instance->rx_bytes_obj.data = app_data_instance->rx_buffer;
×
110

111
    // Activate callback now that we have allocated the rx_buffer.
NEW
112
    pbsys_command_set_write_app_data_callback(handle_incoming_app_data);
×
113

114
    // Prepare tx context. Only the length and data is variable.
NEW
115
    app_data_instance->tx_context.done = NULL;
×
NEW
116
    app_data_instance->tx_context.connection = PBDRV_BLUETOOTH_CONNECTION_PYBRICKS;
×
NEW
117
    app_data_instance->tx_context.data = app_data_instance->tx_buffer;
×
NEW
118
    app_data_instance->tx_buffer[0] = PBIO_PYBRICKS_EVENT_WRITE_APP_DATA;
×
119

NEW
120
    return MP_OBJ_FROM_PTR(app_data_instance);
×
121
}
122

NEW
123
mp_obj_t pb_type_app_data_close(mp_obj_t stream) {
×
NEW
124
    if (app_data_instance) {
×
NEW
125
        pbsys_command_set_write_app_data_callback(NULL);
×
NEW
126
        app_data_instance = NULL;
×
127
    }
NEW
128
    return mp_const_none;
×
129
}
130
MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_close_obj, pb_type_app_data_close);
131

132
STATIC const mp_rom_map_elem_t pb_type_app_data_locals_dict_table[] = {
133
    { MP_ROM_QSTR(MP_QSTR___del__),      MP_ROM_PTR(&pb_type_app_data_close_obj) },
134
    { MP_ROM_QSTR(MP_QSTR_close),        MP_ROM_PTR(&pb_type_app_data_close_obj) },
135
    { MP_ROM_QSTR(MP_QSTR_get_bytes),    MP_ROM_PTR(&pb_type_app_data_get_bytes_obj) },
136
    { MP_ROM_QSTR(MP_QSTR_get_values),   MP_ROM_PTR(&pb_type_app_data_get_values_obj) },
137
    { MP_ROM_QSTR(MP_QSTR_write_bytes),    MP_ROM_PTR(&pb_type_app_data_write_bytes_obj) },
138
};
139
STATIC MP_DEFINE_CONST_DICT(pb_type_app_data_locals_dict, pb_type_app_data_locals_dict_table);
140

141
MP_DEFINE_CONST_OBJ_TYPE(pb_type_app_data,
142
    MP_QSTR_AppData,
143
    MP_TYPE_FLAG_NONE,
144
    make_new, pb_type_app_data_make_new,
145
    locals_dict, &pb_type_app_data_locals_dict);
146

147
#endif // PYBRICKS_PY_TOOLS_APP_DATA
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