jefffhaynes / BinarySerializer

A declarative serialization framework for controlling formatting of data at the byte and bit level using field bindings, converters, and code.
MIT License
290 stars 62 forks source link

[Feature Request] Variable 'unit' for FieldLength mappings #204

Closed bevanweiss closed 1 year ago

bevanweiss commented 1 year ago

First up, great library. Really amazing work.

To set some context. I've started using this library to put together a driver for an industrial protocol called Ethernet/IP (CIP). Which is used by Rockwell PLCs (amongst others... the standard is now hosted by OVDA).

At a certain point within the weird nesting of the protocols around this (Ethernet/IP hosts CIP, hosts other proprietary services) certain fields stop being references to bytes, and are instead references to 16-bit integers. So it will be something like

  [FieldOrder(0)]
  public UInt16 Length;

  [FieldOrder[1])
  [FieldLength(nameof(Length))]
  public LogixServiceRequest Request;

but... instead of the Length really being in terms of bytes, it's in terms of UInt16s ( i.e. CEIL(0.5 * bytes) )

Whilst I could go with a custom formatter to solve this problem I suspect, I was wondering if it might be nicer to support some additional (optional) arguments to the FieldLengthAttribute. Something like

  [FieldLength(nameof(Length), sizeof(UInt16))]
  public LogixServiceRequest Request;

I'm happy to go about implementing this if it's something that you think might have merit. If there's an incredibly trivial way to achieve what I'm looking for that would be better, then I'm absolutely happy to hear about this also.

bevanweiss commented 1 year ago

This was easy enough without requiring any modifications to the existing FieldLength stuff.

        [FieldOrder(0)]
        public byte RequestPathSize;

        [FieldOrder(1)]
        [FieldLength(nameof(RequestPathSize), 
            ConverterType = typeof(FieldLengthConverter), 
            ConverterParameter = sizeof(UInt16))]
        public CipEpath RequestPath;

...

    public class FieldLengthConverter : IValueConverter
    {
        // Read
        public object Convert(object value, object converterParameter, BinarySerializationContext context)
        {
            return System.Convert.ToUInt64(value) * System.Convert.ToUInt64(converterParameter);
        }

        // Write
        public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
        {
            return System.Convert.ToUInt64(value) / System.Convert.ToUInt64(converterParameter);
        }
    }