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

pybricks / pybricks-micropython / 10047310602

22 Jul 2024 07:41PM UTC coverage: 56.042% (-0.6%) from 56.592%
10047310602

Pull #246

github

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

9 of 80 new or added lines in 5 files covered. (11.25%)

12 existing lines in 5 files now uncovered.

3766 of 6720 relevant lines covered (56.04%)

20062093.59 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 void 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;
40
    }
NEW
41
    memcpy(app_data_instance->rx_buffer + offset, data, size);
×
42
}
43

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

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

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

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

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

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

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

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

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

NEW
86
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) {
×
87

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

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

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

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

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

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

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

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

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

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

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

146
#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