Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/Firmware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -167,16 +168,20 @@ public class PortPinInfo
/// <summary>
/// Specifies the microcontroller port where the pin is located.
/// </summary>
[YamlMember(Order = -1)]
public string Port;

/// <summary>
/// Specifies the unique pin number in the defined port.
/// </summary>
[YamlMember(Order = -1, DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public int PinNumber;

/// <summary>
/// Specifies whether the pin will be used as input or output.
/// </summary>
[YamlConverter(typeof(LowerCaseEnumTypeConverter))]
[YamlMember(Order = -1, DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public PinDirection Direction;

/// <summary>
Expand All @@ -193,21 +198,27 @@ public class InputPinInfo : PortPinInfo
/// <summary>
/// Specifies how the input pin is configured to handle floating inputs.
/// </summary>
[YamlConverter(typeof(LowerCaseEnumTypeConverter))]
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public InputPinMode PinMode;

/// <summary>
/// Specifies when the interrupt event for this pin should be triggered.
/// </summary>
[YamlConverter(typeof(LowerCaseEnumTypeConverter))]
public TriggerMode TriggerMode;

/// <summary>
/// Specifies the priority of the interrupt event for this pin.
/// </summary>
[YamlConverter(typeof(LowerCaseEnumTypeConverter))]
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public InterruptPriority InterruptPriority;

/// <summary>
/// Specifies the interrupt number associated with this pin.
/// </summary>
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public int InterruptNumber;
}

Expand All @@ -219,16 +230,20 @@ public class OutputPinInfo : PortPinInfo
/// <summary>
/// Specifies whether reading the state of the output pin is allowed.
/// </summary>
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public bool AllowRead;

/// <summary>
/// Specifies the output pin wiring configuration.
/// </summary>
[YamlConverter(typeof(CamelCaseEnumTypeConverter))]
public OutputPinMode PinMode;

/// <summary>
/// Specifies the initial state of the output pin at boot time.
/// </summary>
[YamlConverter(typeof(LowerCaseEnumTypeConverter))]
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.Preserve)]
public LogicState InitialState;

/// <summary>
Expand Down Expand Up @@ -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())));
}
}

/// <summary>
/// Translates property names into screaming snake case following the current
/// ATxmega register naming convention.
Expand Down
2 changes: 2 additions & 0 deletions src/MetadataDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 2 additions & 0 deletions src/MetadataSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
50 changes: 24 additions & 26 deletions tests/Metadata/device.ios.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
%YAML 1.1
---
# yaml-language-server: $schema=https://harp-tech.org/draft-02/schema/ios.json
DO3:
port: PORTC
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
9 changes: 5 additions & 4 deletions tests/MetadataSerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, PortPinInfo>))]
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<DeviceInfo>(metadataContents);
var deviceMetadata = MetadataDeserializer.Instance.Deserialize(metadataContents, type);
var roundTripContents = MetadataSerializer.Instance.Serialize(deviceMetadata);
try
{
Expand Down
Loading