"""The wire shape of a `joy` message. dora 1.0 has no standard joystick type, so this schema is the whole contract a consumer gets. It is **declared, never inferred**: pyarrow reads an empty `axes` list as `list`, which would mean the port silently changed type the moment somebody unplugged the pad. It is not `sensor_msgs/Joy`. There is no header, because dora already knows when a message arrived and a second clock in the payload only invites skew questions; buttons are booleans rather than ROS's `int32`, because a button is down or it is not; and `present` is a field rather than an absence of messages, because a consumer stopping a robot needs to tell "the pad is centred" from "the pad is gone" within a tick rather than within a timeout. """ import pyarrow as pa from dora_joy.gamepad import Sample JOY = pa.struct( [ # Raw, exactly as SDL reports it: no deadzone, no expo, no rescaling. # Shaping is a decision about what the pad means, which belongs to # whoever is driving something with it. ("axes", pa.list_(pa.float32())), ("buttons", pa.list_(pa.bool_())), ("present", pa.bool_()), ] ) def pack(sample: Sample) -> pa.Array: """Build one `joy` message from a reading.""" return pa.array( [ { "axes": [float(value) for value in sample.axes], "buttons": [bool(down) for down in sample.buttons], "present": sample.present, } ], type=JOY, )