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 the member interfaceType to determine automatically how many bytes will be consumed from the payload array, and how the conversion will be performed.

For now the initial implementation supports exclusively U8 registers, but could be extended in the future to any base word-size.

Support for a new length attribute is included. This has not yet been ratified in the core device schema, and can be ignored for simple payload specifications. Once the new schema is ratified, length can be used to allow more flexible conversions.

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
    description: "Schedules a task."
    payloadSpec:
      PwmPort:
        offset: 0
        maskType: Port
      DutyCycle:
        offset: 4
        interfaceType: float
      Frequency:
        offset: 8
        interfaceType: float
      EventsEnabled:
        offset: 12
        interfaceType: bool
      Delta:
        offset: 13
        interfaceType: uint

The generated conversion will become:

        static ComplexConfigurationPayload ParsePayload(byte[] payload)
        {
            ComplexConfigurationPayload result;
            result.PwmPort = (Port)new ArraySegment<byte>(payload, 0, 1).ToByte();
            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];
            new ArraySegment<byte>(result, 0, 1).WriteBytes((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 ToByte, ToSingle, ToUInt32 and WriteBytes are auxiliary methods for marshalling multi-byte values to/from arrays.

- 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
@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.

Assumes UTF-8 encoding for strings, and sub-arrays of the same primitive
payload type.
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