Skip to content

Conversation

@glopesdev
Copy link
Contributor

@glopesdev glopesdev commented Nov 13, 2025

This PR is a proposal to resolve harp-tech/protocol#40 and provide automatically generated type converters for payload specs composed of heterogeneous member types.

This was already partially supported at the payload declaration level, where a different interfaceType could be assigned to specific members. However, the offset attribute was limited to indexing exactly one element, so e.g. we could apply a mask to a U16 payload element to extract two U8 fields, but we could not do the opposite and compose a U16 field out of two U8 elements.

Here we propose generalizing type conversion to allow for a new length attribute to determine how many bytes will be consumed from the payload array. For now the initial implementation provides conversions from U8 registers, but could be extended in the future to any base word-size.

Example mixed configuration spec

The following mixed payload spec includes a group mask, a boolean, two floats and one unsigned integer:

  ComplexConfiguration:
    address: 33
    type: U8
    access: Write
    length: 17
    payloadSpec:
      PwmPort:
        offset: 0
        maskType: Port
      DutyCycle:
        offset: 4
        length: 4
        interfaceType: float
      Frequency:
        offset: 8
        length: 4
        interfaceType: float
      EventsEnabled:
        offset: 12
        interfaceType: bool
      Delta:
        offset: 13
        length: 4
        interfaceType: uint

The generated conversion will become:

        static ComplexConfigurationPayload ParsePayload(byte[] payload)
        {
            ComplexConfigurationPayload result;
            result.PwmPort = (PwmPort)payload[0];
            result.DutyCycle = new ArraySegment<byte>(payload, 4, 4).ToSingle();
            result.Frequency = new ArraySegment<byte>(payload, 8, 4).ToSingle();
            result.EventsEnabled = payload[12] != 0;
            result.Delta = new ArraySegment<byte>(payload, 13, 4).ToUInt32();
            return result;
        }

        static byte[] FormatPayload(ComplexConfigurationPayload value)
        {
            byte[] result;
            result = new byte[17];
            result[0] = (byte)value.PwmPort;
            new ArraySegment<byte>(result, 4, 4).WriteBytes(value.DutyCycle);
            new ArraySegment<byte>(result, 8, 4).WriteBytes(value.Frequency);
            result[12] = (byte)(value.EventsEnabled ? 1 : 0);
            new ArraySegment<byte>(result, 13, 4).WriteBytes(value.Delta);
            return result;
        }

Where ToSingle, ToUInt32 and WriteBytes are auxiliary methods for marshalling multi-byte values to/from arrays.

@glopesdev
Copy link
Contributor Author

@Poofjunior suggested member length inference rules should be made clear in a documentation table, so it can inform both users of the generator and developers of other interface generators, e.g. Python.

@glopesdev
Copy link
Contributor Author

Feedback from SRM:

  • Consider adding well-known interfaceType values into JSON schema as examples.

@glopesdev glopesdev force-pushed the mixed-payloadspec branch 3 times, most recently from f2c541a to 29d1a06 Compare January 12, 2026 13:51
- Auto-generate converters for payload spec with mixed types
- Support for explicit member length
- Support for inferring member length based on interface type
- AllowUnsafeBlocks is now required for building generated helper code
Assumes UTF-8 encoding for strings, and sub-arrays of the same primitive
payload type.
Conversion code for custom interface types can often be shared across
registers.

Here we make the internal conversion class partial and provide a simple
heuristic for generating conversion method calls based on the interface
type name.
Also added regression test for a complex register mixing mask types.
The current spec assumes fixed-length strings but built-in encoding
methods throw if indices are out of bounds.

Here we ensure longer strings are trimmed to fit register length on
encoding. Conversely, any null-terminators are removed during decoding.
In addition to fixed-length string payload members we want to allow
fixed-length string registers.
@glopesdev
Copy link
Contributor Author

glopesdev commented Jan 13, 2026

@Poofjunior @bruno-f-cruz In the end it just isn't possible to do automatic size inference for some important types, e.g. string, so we decided to require that heterogeneous multi-byte fields always declare their size using the length attribute.

We should still talk about and provide completion for these built-in interface types in the spec, but it will probably be easier to start explicit for the purposes of these mixed payload converters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New planned feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Heterogenous register types in yaml schema

2 participants