diff --git a/src/Firmware.cs b/src/Firmware.cs
index fab0e47..235887c 100644
--- a/src/Firmware.cs
+++ b/src/Firmware.cs
@@ -5,6 +5,7 @@
using System.Text.RegularExpressions;
using Bonsai.Harp;
using YamlDotNet.Core;
+using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@@ -167,16 +168,20 @@ public class PortPinInfo
///
/// Specifies the microcontroller port where the pin is located.
///
+ [YamlMember(Order = -1)]
public string Port;
///
/// Specifies the unique pin number in the defined port.
///
+ [YamlMember(Order = -1, DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public int PinNumber;
///
/// Specifies whether the pin will be used as input or output.
///
+ [YamlConverter(typeof(LowerCaseEnumTypeConverter))]
+ [YamlMember(Order = -1, DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public PinDirection Direction;
///
@@ -193,21 +198,27 @@ public class InputPinInfo : PortPinInfo
///
/// Specifies how the input pin is configured to handle floating inputs.
///
+ [YamlConverter(typeof(LowerCaseEnumTypeConverter))]
+ [YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public InputPinMode PinMode;
///
/// Specifies when the interrupt event for this pin should be triggered.
///
+ [YamlConverter(typeof(LowerCaseEnumTypeConverter))]
public TriggerMode TriggerMode;
///
/// Specifies the priority of the interrupt event for this pin.
///
+ [YamlConverter(typeof(LowerCaseEnumTypeConverter))]
+ [YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public InterruptPriority InterruptPriority;
///
/// Specifies the interrupt number associated with this pin.
///
+ [YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public int InterruptNumber;
}
@@ -219,16 +230,20 @@ public class OutputPinInfo : PortPinInfo
///
/// Specifies whether reading the state of the output pin is allowed.
///
+ [YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public bool AllowRead;
///
/// Specifies the output pin wiring configuration.
///
+ [YamlConverter(typeof(CamelCaseEnumTypeConverter))]
public OutputPinMode PinMode;
///
/// Specifies the initial state of the output pin at boot time.
///
+ [YamlConverter(typeof(LowerCaseEnumTypeConverter))]
+ [YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public LogicState InitialState;
///
@@ -373,6 +388,38 @@ public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerialize
}
}
+class LowerCaseEnumTypeConverter : IYamlTypeConverter
+{
+ public static readonly LowerCaseEnumTypeConverter Instance = new();
+
+ public bool Accepts(Type type) => false;
+
+ public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) => rootDeserializer(type);
+
+ public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
+ {
+ var scalarStyle = ScalarStyle.Any;
+ var scalarValue = LowerCaseNamingConvention.Instance.Apply(value.ToString());
+ if (scalarValue == "off")
+ scalarStyle = ScalarStyle.DoubleQuoted;
+ emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, scalarValue, scalarStyle, true, true));
+ }
+}
+
+class CamelCaseEnumTypeConverter : IYamlTypeConverter
+{
+ public static readonly CamelCaseEnumTypeConverter Instance = new();
+
+ public bool Accepts(Type type) => false;
+
+ public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) => rootDeserializer(type);
+
+ public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
+ {
+ emitter.Emit(new Scalar(CamelCaseNamingConvention.Instance.Apply(value.ToString())));
+ }
+}
+
///
/// Translates property names into screaming snake case following the current
/// ATxmega register naming convention.
diff --git a/src/MetadataDeserializer.cs b/src/MetadataDeserializer.cs
index 52cdbcc..1fb58e6 100644
--- a/src/MetadataDeserializer.cs
+++ b/src/MetadataDeserializer.cs
@@ -18,6 +18,8 @@ public static class MetadataDeserializer
.WithTypeConverter(MaskValueTypeConverter.Instance)
.WithTypeConverter(HarpVersionTypeConverter.Instance)
.WithTypeConverter(HexValueTypeConverter.Instance)
+ .WithTypeConverter(LowerCaseEnumTypeConverter.Instance)
+ .WithTypeConverter(CamelCaseEnumTypeConverter.Instance)
.WithPortPinInfoTypeConverter()
.Build();
diff --git a/src/MetadataSerializer.cs b/src/MetadataSerializer.cs
index e12df9b..9c7ee9a 100644
--- a/src/MetadataSerializer.cs
+++ b/src/MetadataSerializer.cs
@@ -18,6 +18,8 @@ public static class MetadataSerializer
.WithTypeConverter(MaskValueTypeConverter.Instance)
.WithTypeConverter(HarpVersionTypeConverter.Instance)
.WithTypeConverter(HexValueTypeConverter.Instance)
+ .WithTypeConverter(LowerCaseEnumTypeConverter.Instance)
+ .WithTypeConverter(CamelCaseEnumTypeConverter.Instance)
.ConfigureDefaultValuesHandling(
DefaultValuesHandling.OmitNull |
DefaultValuesHandling.OmitDefaults |
diff --git a/tests/Metadata/device.ios.yml b/tests/Metadata/device.ios.yml
index 01284ab..1cabdf6 100644
--- a/tests/Metadata/device.ios.yml
+++ b/tests/Metadata/device.ios.yml
@@ -1,5 +1,3 @@
-%YAML 1.1
----
# yaml-language-server: $schema=https://harp-tech.org/draft-02/schema/ios.json
DO3:
port: PORTC
@@ -56,19 +54,19 @@ POKE0_IR:
port: PORTD
pinNumber: 4
direction: input
- interruptNumber: 0
- interruptPriority: low
- triggerMode: toggle
pinMode: pullup
+ triggerMode: toggle
+ interruptPriority: low
+ interruptNumber: 0
description: Poke 0 infrared
POKE0_IO:
port: PORTD
pinNumber: 5
direction: input
- interruptNumber: 0
- interruptPriority: "off"
- triggerMode: toggle
pinMode: pullup
+ triggerMode: toggle
+ interruptPriority: "off"
+ interruptNumber: 0
description: Poke 0 DIO
POKE0_LED:
port: PORTD
@@ -88,19 +86,19 @@ POKE1_IR:
port: PORTE
pinNumber: 4
direction: input
- interruptNumber: 0
- interruptPriority: low
- triggerMode: toggle
pinMode: pullup
+ triggerMode: toggle
+ interruptPriority: low
+ interruptNumber: 0
description: Poke 1 infrared
POKE1_IO:
port: PORTE
pinNumber: 5
direction: input
- interruptNumber: 0
- interruptPriority: "off"
- triggerMode: toggle
pinMode: pullup
+ triggerMode: toggle
+ interruptPriority: "off"
+ interruptNumber: 0
description: Poke 1 DIO
POKE1_LED:
port: PORTE
@@ -120,19 +118,19 @@ POKE2_IR:
port: PORTF
pinNumber: 4
direction: input
- interruptNumber: 0
- interruptPriority: low
- triggerMode: toggle
pinMode: pullup
+ triggerMode: toggle
+ interruptPriority: low
+ interruptNumber: 0
description: Poke 2 infrared
POKE2_IO:
port: PORTF
pinNumber: 5
direction: input
- interruptNumber: 0
- interruptPriority: "off"
- triggerMode: toggle
pinMode: pullup
+ triggerMode: toggle
+ interruptPriority: "off"
+ interruptNumber: 0
description: Poke 2 DIO
POKE2_LED:
port: PORTF
@@ -152,17 +150,17 @@ ADC1_AVAILABLE:
port: PORTJ
pinNumber: 0
direction: input
- interruptNumber: 0
- interruptPriority: "off"
- triggerMode: toggle
pinMode: pulldown
+ triggerMode: toggle
+ interruptPriority: "off"
+ interruptNumber: 0
description: ADC1 is available on hardware
DI3:
port: PORTH
pinNumber: 0
direction: input
- interruptNumber: 0
- interruptPriority: low
- triggerMode: toggle
pinMode: tristate
+ triggerMode: toggle
+ interruptPriority: low
+ interruptNumber: 0
description: Input DI3
diff --git a/tests/MetadataSerializerTests.cs b/tests/MetadataSerializerTests.cs
index b5916bf..f5bb566 100644
--- a/tests/MetadataSerializerTests.cs
+++ b/tests/MetadataSerializerTests.cs
@@ -34,15 +34,16 @@ private static string NormalizeYaml(string contents)
}
[DataTestMethod]
- [DataRow("core.yml")]
- [DataRow("device.yml")]
- public void DeviceMetadata_RoundTripSerializes(string metadataFileName)
+ [DataRow("core.yml", typeof(DeviceInfo))]
+ [DataRow("device.yml", typeof(DeviceInfo))]
+ [DataRow("device.ios.yml", typeof(Dictionary))]
+ public void Metadata_RoundTripSerializes(string metadataFileName, Type type)
{
metadataFileName = TestHelper.GetMetadataPath(metadataFileName);
var metadataContents = File.ReadAllText(metadataFileName);
metadataContents = NormalizeYaml(metadataContents);
- var deviceMetadata = MetadataDeserializer.Instance.Deserialize(metadataContents);
+ var deviceMetadata = MetadataDeserializer.Instance.Deserialize(metadataContents, type);
var roundTripContents = MetadataSerializer.Instance.Serialize(deviceMetadata);
try
{