Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/ubuntu/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ IntegerDesc_t __attribute__((weak)) integer_desc_pool[] = {
IntegerParamValue_t integer_values_pool[sizeof(integer_desc_pool) / sizeof(IntegerDesc_t)];

StringDesc_t __attribute__((weak)) string_desc_pool[] = {
{"uavcan.node.description", "", true},
{"system.name", "dronecan_application", true},
};
StringParamValue_t string_values_pool[sizeof(string_desc_pool) / sizeof(StringDesc_t)];
Expand Down Expand Up @@ -140,7 +141,7 @@ void lights_callback(const uavcan_equipment_indication_LightsCommand &msg)
int main()
{

paramsInit(1, 1, -1, 1);
paramsInit(1, 2, -1, 1);
paramsResetToDefault();
ParamsApi params_api = {
.getName = paramsGetName,
Expand Down
2 changes: 1 addition & 1 deletion include/libdcnode/legacy/uavcan/protocol/param/getset.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ uint16_t uavcanParamGetSetMakeStringResponse(

// Value value (uint8[<=128] string_value)
canardEncodeScalar(buffer, 5, 3, &tag_string);
uint8_t string_size = strlenSafely(str_val, 128);
uint8_t string_size = strlenSafely(str_val, STRING_MAX_SIZE);
uint16_t string_bit_offset = string_size * 8;
canardEncodeScalar(buffer, 8, 8, &string_size);
memcpy(&buffer[2], str_val, string_size);
Expand Down
68 changes: 66 additions & 2 deletions tests/smoke_socketcan.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,55 @@
#!/usr/bin/env python3
import time
from pathlib import Path
import dronecan
import pytest

pytestmark = pytest.mark.filterwarnings(
"ignore:The 'bustype' argument is deprecated.*:DeprecationWarning"
)

APP_NODE_ID = 50
APP_IFACE = "slcan0"
NODE_ID_TESTER = 100
MESSAGE_TIMEOUT = 1.2
SERVICE_TIMEOUT = 0.2
SERVICE_TIMEOUT = 1.0
MAX_STRING_LENGTH = 56
PARAM_DESCRIPTION_INDEX = 1
PARAM_SYSTEM_NAME_INDEX = 2
PARAM_DESCRIPTION_NAME = "uavcan.node.description"
PARAM_SYSTEM_NAME = "system.name"

def make_dronecan_node() -> dronecan.node.Node:
node = dronecan.make_node(APP_IFACE, node_id=NODE_ID_TESTER, bitrate=1_000_000, baudrate=1_000_000)
node = dronecan.make_node(
APP_IFACE,
node_id=NODE_ID_TESTER,
bitrate=1_000_000,
baudrate=1_000_000,
node_status_interval=3600,
)
node.mode = dronecan.uavcan.protocol.NodeStatus().MODE_OPERATIONAL
node.health = dronecan.uavcan.protocol.NodeStatus().HEALTH_OK
return node

def request(node: dronecan.node.Node, payload):
response_transfer_event = None

def _callback(transfer_event: dronecan.node.TransferEvent):
nonlocal response_transfer_event
if transfer_event is None or transfer_event.transfer.source_node_id != APP_NODE_ID:
return
response_transfer_event = transfer_event

node.request(payload, APP_NODE_ID, _callback, timeout=SERVICE_TIMEOUT)
deadline = time.monotonic() + SERVICE_TIMEOUT + 0.1
while response_transfer_event is None and time.monotonic() < deadline:
node.spin(0.01)
assert response_transfer_event is not None, f"{payload} response is missing"
return response_transfer_event.response

def as_string(value) -> str:
return str(value).rstrip("\x00")

@pytest.mark.dependency()
def test_node_status():
node = make_dronecan_node()
Expand Down Expand Up @@ -59,5 +94,34 @@ def _get_info_callback(transfer_event: dronecan.node.TransferEvent):
assert get_node_info_response_transfer_event.transfer.dest_node_id == NODE_ID_TESTER
assert get_node_info_response_transfer_event.response.name.decode("utf-8").rstrip("\x00") == "com.example.libdcnode.sitl"

@pytest.mark.dependency(depends=["test_node_status"])
def test_max_length_string_param():
node = make_dronecan_node()
max_string = "S" * MAX_STRING_LENGTH
neighbor_marker = "AFTER_MAX_STRING"

req = dronecan.uavcan.protocol.param.GetSet.Request(name=PARAM_SYSTEM_NAME)
req.value = dronecan.uavcan.protocol.param.Value(string_value=neighbor_marker)
response = request(node, req)
assert as_string(response.name) == PARAM_SYSTEM_NAME
assert as_string(response.value.string_value) == neighbor_marker

req = dronecan.uavcan.protocol.param.GetSet.Request(name=PARAM_DESCRIPTION_NAME)
req.value = dronecan.uavcan.protocol.param.Value(string_value=max_string)
response = request(node, req)
assert as_string(response.name) == PARAM_DESCRIPTION_NAME
assert as_string(response.value.string_value) == max_string

req = dronecan.uavcan.protocol.param.GetSet.Request(index=PARAM_DESCRIPTION_INDEX)
response = request(node, req)
assert as_string(response.name) == PARAM_DESCRIPTION_NAME
assert as_string(response.value.string_value) == max_string
assert neighbor_marker not in as_string(response.value.string_value)

req = dronecan.uavcan.protocol.param.GetSet.Request(index=PARAM_SYSTEM_NAME_INDEX)
response = request(node, req)
assert as_string(response.name) == PARAM_SYSTEM_NAME
assert as_string(response.value.string_value) == neighbor_marker

if __name__ == "__main__":
raise SystemExit(pytest.main([str(Path(__file__).resolve())]))