Skip to content

Commit 00e3b50

Browse files
author
Patil, Suraj
committed
Merge branch 'feature/pcicCommand_trampoline_class' into 'main'
pybind11: add PCICCommand trampoline for Python command overrides See merge request syntron/support/csr/ifm3d/ifm3d!563
2 parents 63f3905 + 5764135 commit 00e3b50

3 files changed

Lines changed: 106 additions & 7 deletions

File tree

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Add `PCICCommand` trampoline for Python command overrides
810

911
## 2.0.5 - 2026-08-10
1012
### Changed

modules/pybind11/include/ifm3d/pybind11/bindings/o3r.h

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@
1717

1818
namespace py = pybind11;
1919

20+
class PyPCICCommand : public ifm3d::PCICCommand
21+
{
22+
public:
23+
using ifm3d::PCICCommand::PCICCommand;
24+
25+
std::vector<std::uint8_t>
26+
SerializeData() const override
27+
{
28+
PYBIND11_OVERRIDE_PURE_NAME(std::vector<std::uint8_t>,
29+
ifm3d::PCICCommand,
30+
"serialize_data",
31+
SerializeData);
32+
}
33+
};
34+
2035
inline void
2136
bind_o3r(pybind11::module_& m)
2237
{
@@ -99,12 +114,24 @@ bind_o3r(pybind11::module_& m)
99114
self->type);
100115
});
101116

102-
py::class_<ifm3d::PCICCommand>(m,
103-
"PCICCommand",
104-
R"(
105-
Base class for the commands which can be sent to a device over PCIC.
117+
py::class_<ifm3d::PCICCommand, PyPCICCommand>(m,
118+
"PCICCommand",
119+
R"(
120+
Base class for PCIC commands.
106121
107-
See :class:`SetTemporaryApplicationParameter` for a concrete command.
122+
Use this class as a Python base type when implementing custom
123+
command payloads for framegrabber communication.
124+
)")
125+
.def(py::init<>())
126+
.def("serialize_data",
127+
&ifm3d::PCICCommand::SerializeData,
128+
R"(
129+
Serialize this command into the byte payload sent over PCIC.
130+
131+
Returns
132+
-------
133+
list[int]
134+
Serialized payload as byte values.
108135
)");
109136

110137
py::class_<ifm3d::O3R::SetTemporaryApplicationParameter, ifm3d::PCICCommand>(
@@ -128,8 +155,8 @@ bind_o3r(pybind11::module_& m)
128155
parameter : Parameter
129156
The parameter to set
130157
131-
data : bytes
132-
The data to set the parameter to
158+
data : list[int], tuple[int], or bytearray
159+
The data to set the parameter to.
133160
)");
134161

135162
py::native_enum<ifm3d::O3R::SetTemporaryApplicationParameter::Parameter>(
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#
2+
# Copyright 2026-present ifm electronic, gmbh
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
6+
import pytest
7+
from ifm3dpy.device import PCICCommand, Parameter, SetTemporaryApplicationParameter
8+
9+
10+
def _command_from_payload(payload):
11+
class Command(PCICCommand):
12+
def serialize_data(self):
13+
return payload
14+
15+
return Command()
16+
17+
18+
@pytest.mark.parametrize(
19+
"payload",
20+
[
21+
[0x66, 0x30, 0x31],
22+
(0x66, 0x30, 0x31),
23+
bytearray(b"f01"),
24+
],
25+
)
26+
def test_pcic_command_python_override_payload_types(payload):
27+
command = _command_from_payload(payload)
28+
assert PCICCommand.serialize_data(command) == [0x66, 0x30, 0x31]
29+
30+
31+
def test_pcic_command_override_bytes_payload_rejected():
32+
command = _command_from_payload(b"f01")
33+
with pytest.raises((RuntimeError, TypeError)):
34+
PCICCommand.serialize_data(command)
35+
36+
37+
def test_pcic_command_override_invalid_payload_raises_type_error():
38+
command = _command_from_payload("f01")
39+
with pytest.raises((RuntimeError, TypeError)):
40+
PCICCommand.serialize_data(command)
41+
42+
43+
def test_pcic_command_without_override_raises():
44+
class IncompleteCommand(PCICCommand):
45+
pass
46+
47+
command = IncompleteCommand()
48+
with pytest.raises(RuntimeError):
49+
PCICCommand.serialize_data(command)
50+
51+
52+
def test_set_temporary_application_parameter_serialization_layout():
53+
payload = [0x01, 0x02, 0x03]
54+
command = SetTemporaryApplicationParameter(Parameter.ODS_MOTION_DATA,
55+
payload)
56+
57+
serialized = PCICCommand.serialize_data(command)
58+
59+
# Layout: 'f' + 5-digit parameter + '#00000' + payload bytes.
60+
assert bytes(serialized[:12]).decode("ascii") == "f02103#00000"
61+
assert serialized[12:] == payload
62+
63+
64+
def test_set_temporary_application_parameter_ods_motion_data_prefix_encoding():
65+
command = SetTemporaryApplicationParameter(Parameter.ODS_MOTION_DATA,
66+
[0xAA, 0xBB])
67+
serialized = PCICCommand.serialize_data(command)
68+
69+
assert bytes(serialized[:12]).decode("ascii") == "f02103#00000"
70+
assert serialized[-2:] == [0xAA, 0xBB]

0 commit comments

Comments
 (0)