diff --git a/Generators/Generators.csproj b/Generators/Generators.csproj
index 9e36287..9323234 100644
--- a/Generators/Generators.csproj
+++ b/Generators/Generators.csproj
@@ -15,7 +15,7 @@
..\Firmware\Harp.LedArray
-
+
diff --git a/Interface/Harp.LedArray/AsyncDevice.Generated.cs b/Interface/Harp.LedArray/AsyncDevice.Generated.cs
index 4df5654..cab6dce 100644
--- a/Interface/Harp.LedArray/AsyncDevice.Generated.cs
+++ b/Interface/Harp.LedArray/AsyncDevice.Generated.cs
@@ -14,15 +14,18 @@ public partial class Device
///
/// The name of the serial port used to communicate with the Harp device.
///
+ ///
+ /// A which can be used to cancel the operation.
+ ///
///
/// A task that represents the asynchronous initialization operation. The value of
/// the parameter contains a new instance of
/// the class.
///
- public static async Task CreateAsync(string portName)
+ public static async Task CreateAsync(string portName, CancellationToken cancellationToken = default)
{
var device = new AsyncDevice(portName);
- var whoAmI = await device.ReadWhoAmIAsync();
+ var whoAmI = await device.ReadWhoAmIAsync(cancellationToken);
if (whoAmI != Device.WhoAmI)
{
var errorMessage = string.Format(
@@ -1278,7 +1281,7 @@ public async Task> ReadTimestampedLed1PwmRealAsync(Cancellati
}
///
- /// Asynchronously reads the contents of the LedD1PwmDutyCycleReal register.
+ /// Asynchronously reads the contents of the Led1PwmDutyCycleReal register.
///
///
/// A which can be used to cancel the operation.
@@ -1287,14 +1290,14 @@ public async Task> ReadTimestampedLed1PwmRealAsync(Cancellati
/// A task that represents the asynchronous read operation. The
/// property contains the register payload.
///
- public async Task ReadLedD1PwmDutyCycleRealAsync(CancellationToken cancellationToken = default)
+ public async Task ReadLed1PwmDutyCycleRealAsync(CancellationToken cancellationToken = default)
{
- var reply = await CommandAsync(HarpCommand.ReadSingle(LedD1PwmDutyCycleReal.Address), cancellationToken);
- return LedD1PwmDutyCycleReal.GetPayload(reply);
+ var reply = await CommandAsync(HarpCommand.ReadSingle(Led1PwmDutyCycleReal.Address), cancellationToken);
+ return Led1PwmDutyCycleReal.GetPayload(reply);
}
///
- /// Asynchronously reads the timestamped contents of the LedD1PwmDutyCycleReal register.
+ /// Asynchronously reads the timestamped contents of the Led1PwmDutyCycleReal register.
///
///
/// A which can be used to cancel the operation.
@@ -1303,10 +1306,10 @@ public async Task ReadLedD1PwmDutyCycleRealAsync(CancellationToken cancel
/// A task that represents the asynchronous read operation. The
/// property contains the timestamped register payload.
///
- public async Task> ReadTimestampedLedD1PwmDutyCycleRealAsync(CancellationToken cancellationToken = default)
+ public async Task> ReadTimestampedLed1PwmDutyCycleRealAsync(CancellationToken cancellationToken = default)
{
- var reply = await CommandAsync(HarpCommand.ReadSingle(LedD1PwmDutyCycleReal.Address), cancellationToken);
- return LedD1PwmDutyCycleReal.GetTimestampedPayload(reply);
+ var reply = await CommandAsync(HarpCommand.ReadSingle(Led1PwmDutyCycleReal.Address), cancellationToken);
+ return Led1PwmDutyCycleReal.GetTimestampedPayload(reply);
}
///
diff --git a/Interface/Harp.LedArray/Device.Generated.cs b/Interface/Harp.LedArray/Device.Generated.cs
index a154781..0bde045 100644
--- a/Interface/Harp.LedArray/Device.Generated.cs
+++ b/Interface/Harp.LedArray/Device.Generated.cs
@@ -65,7 +65,7 @@ public Device() : base(WhoAmI) { }
{ 57, typeof(Led0PwmReal) },
{ 58, typeof(Led0PwmDutyCycleReal) },
{ 59, typeof(Led1PwmReal) },
- { 60, typeof(LedD1PwmDutyCycleReal) },
+ { 60, typeof(Led1PwmDutyCycleReal) },
{ 61, typeof(AuxDigitalOutputState) },
{ 62, typeof(AuxLedPower) },
{ 63, typeof(DigitalOutputState) },
@@ -93,7 +93,7 @@ static string GetDeviceMetadata()
/// describing the device registers.
///
[Description("Returns the contents of the metadata file describing the LedArray device registers.")]
- public partial class GetMetadata : Source
+ public partial class GetDeviceMetadata : Source
{
///
/// Returns an observable sequence with the contents of the metadata file
@@ -130,6 +130,156 @@ public override IObservable> Process(IObse
}
}
+ ///
+ /// Represents an operator that writes the sequence of " messages
+ /// to the standard Harp storage format.
+ ///
+ [Description("Writes the sequence of LedArray messages to the standard Harp storage format.")]
+ public partial class DeviceDataWriter : Sink, INamedElement
+ {
+ const string BinaryExtension = ".bin";
+ const string MetadataFileName = "device.yml";
+ readonly Bonsai.Harp.MessageWriter writer = new();
+
+ string INamedElement.Name => nameof(LedArray) + "DataWriter";
+
+ ///
+ /// Gets or sets the relative or absolute path on which to save the message data.
+ ///
+ [Description("The relative or absolute path of the directory on which to save the message data.")]
+ [Editor("Bonsai.Design.SaveFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)]
+ public string Path
+ {
+ get => System.IO.Path.GetDirectoryName(writer.FileName);
+ set => writer.FileName = System.IO.Path.Combine(value, nameof(LedArray) + BinaryExtension);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether element writing should be buffered. If ,
+ /// the write commands will be queued in memory as fast as possible and will be processed
+ /// by the writer in a different thread. Otherwise, writing will be done in the same
+ /// thread in which notifications arrive.
+ ///
+ [Description("Indicates whether writing should be buffered.")]
+ public bool Buffered
+ {
+ get => writer.Buffered;
+ set => writer.Buffered = value;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether to overwrite the output file if it already exists.
+ ///
+ [Description("Indicates whether to overwrite the output file if it already exists.")]
+ public bool Overwrite
+ {
+ get => writer.Overwrite;
+ set => writer.Overwrite = value;
+ }
+
+ ///
+ /// Gets or sets a value specifying how the message filter will use the matching criteria.
+ ///
+ [Description("Specifies how the message filter will use the matching criteria.")]
+ public FilterType FilterType
+ {
+ get => writer.FilterType;
+ set => writer.FilterType = value;
+ }
+
+ ///
+ /// Gets or sets a value specifying the expected message type. If no value is
+ /// specified, all messages will be accepted.
+ ///
+ [Description("Specifies the expected message type. If no value is specified, all messages will be accepted.")]
+ public MessageType? MessageType
+ {
+ get => writer.MessageType;
+ set => writer.MessageType = value;
+ }
+
+ private IObservable WriteDeviceMetadata(IObservable source)
+ {
+ var basePath = Path;
+ if (string.IsNullOrEmpty(basePath))
+ return source;
+
+ var metadataPath = System.IO.Path.Combine(basePath, MetadataFileName);
+ return Observable.Create(observer =>
+ {
+ Bonsai.IO.PathHelper.EnsureDirectory(metadataPath);
+ if (System.IO.File.Exists(metadataPath) && !Overwrite)
+ {
+ throw new System.IO.IOException(string.Format("The file '{0}' already exists.", metadataPath));
+ }
+
+ System.IO.File.WriteAllText(metadataPath, Device.Metadata);
+ return source.SubscribeSafe(observer);
+ });
+ }
+
+ ///
+ /// Writes each Harp message in the sequence to the specified binary file, and the
+ /// contents of the device metadata file to a separate text file.
+ ///
+ /// The sequence of messages to write to the file.
+ ///
+ /// An observable sequence that is identical to the
+ /// sequence but where there is an additional side effect of writing the
+ /// messages to a raw binary file, and the contents of the device metadata file
+ /// to a separate text file.
+ ///
+ public override IObservable Process(IObservable source)
+ {
+ return source.Publish(ps => ps.Merge(
+ WriteDeviceMetadata(writer.Process(ps.GroupBy(message => message.Address)))
+ .IgnoreElements()
+ .Cast()));
+ }
+
+ ///
+ /// Writes each Harp message in the sequence of observable groups to the
+ /// corresponding binary file, where the name of each file is generated from
+ /// the common group register address. The contents of the device metadata file are
+ /// written to a separate text file.
+ ///
+ ///
+ /// A sequence of observable groups, each of which corresponds to a unique register
+ /// address.
+ ///
+ ///
+ /// An observable sequence that is identical to the
+ /// sequence but where there is an additional side effect of writing the Harp
+ /// messages in each group to the corresponding file, and the contents of the device
+ /// metadata file to a separate text file.
+ ///
+ public IObservable> Process(IObservable> source)
+ {
+ return WriteDeviceMetadata(writer.Process(source));
+ }
+
+ ///
+ /// Writes each Harp message in the sequence of observable groups to the
+ /// corresponding binary file, where the name of each file is generated from
+ /// the common group register name. The contents of the device metadata file are
+ /// written to a separate text file.
+ ///
+ ///
+ /// A sequence of observable groups, each of which corresponds to a unique register
+ /// type.
+ ///
+ ///
+ /// An observable sequence that is identical to the
+ /// sequence but where there is an additional side effect of writing the Harp
+ /// messages in each group to the corresponding file, and the contents of the device
+ /// metadata file to a separate text file.
+ ///
+ public IObservable> Process(IObservable> source)
+ {
+ return WriteDeviceMetadata(writer.Process(source));
+ }
+ }
+
///
/// Represents an operator that filters register-specific messages
/// reported by the device.
@@ -162,7 +312,7 @@ public override IObservable> Process(IObse
///
///
///
- ///
+ ///
///
///
///
@@ -195,7 +345,7 @@ public override IObservable> Process(IObse
[XmlInclude(typeof(Led0PwmReal))]
[XmlInclude(typeof(Led0PwmDutyCycleReal))]
[XmlInclude(typeof(Led1PwmReal))]
- [XmlInclude(typeof(LedD1PwmDutyCycleReal))]
+ [XmlInclude(typeof(Led1PwmDutyCycleReal))]
[XmlInclude(typeof(AuxDigitalOutputState))]
[XmlInclude(typeof(AuxLedPower))]
[XmlInclude(typeof(DigitalOutputState))]
@@ -249,7 +399,7 @@ string INamedElement.Name
///
///
///
- ///
+ ///
///
///
///
@@ -282,7 +432,7 @@ string INamedElement.Name
[XmlInclude(typeof(Led0PwmReal))]
[XmlInclude(typeof(Led0PwmDutyCycleReal))]
[XmlInclude(typeof(Led1PwmReal))]
- [XmlInclude(typeof(LedD1PwmDutyCycleReal))]
+ [XmlInclude(typeof(Led1PwmDutyCycleReal))]
[XmlInclude(typeof(AuxDigitalOutputState))]
[XmlInclude(typeof(AuxLedPower))]
[XmlInclude(typeof(DigitalOutputState))]
@@ -315,7 +465,7 @@ string INamedElement.Name
[XmlInclude(typeof(TimestampedLed0PwmReal))]
[XmlInclude(typeof(TimestampedLed0PwmDutyCycleReal))]
[XmlInclude(typeof(TimestampedLed1PwmReal))]
- [XmlInclude(typeof(TimestampedLedD1PwmDutyCycleReal))]
+ [XmlInclude(typeof(TimestampedLed1PwmDutyCycleReal))]
[XmlInclude(typeof(TimestampedAuxDigitalOutputState))]
[XmlInclude(typeof(TimestampedAuxLedPower))]
[XmlInclude(typeof(TimestampedDigitalOutputState))]
@@ -366,7 +516,7 @@ public Parse()
///
///
///
- ///
+ ///
///
///
///
@@ -399,7 +549,7 @@ public Parse()
[XmlInclude(typeof(Led0PwmReal))]
[XmlInclude(typeof(Led0PwmDutyCycleReal))]
[XmlInclude(typeof(Led1PwmReal))]
- [XmlInclude(typeof(LedD1PwmDutyCycleReal))]
+ [XmlInclude(typeof(Led1PwmDutyCycleReal))]
[XmlInclude(typeof(AuxDigitalOutputState))]
[XmlInclude(typeof(AuxLedPower))]
[XmlInclude(typeof(DigitalOutputState))]
@@ -3165,25 +3315,25 @@ public static Timestamped GetPayload(HarpMessage message)
/// Represents a register that get the real duty cycle (%) of LED1 when in Pwm mode.
///
[Description("Get the real duty cycle (%) of LED1 when in Pwm mode.")]
- public partial class LedD1PwmDutyCycleReal
+ public partial class Led1PwmDutyCycleReal
{
///
- /// Represents the address of the register. This field is constant.
+ /// Represents the address of the register. This field is constant.
///
public const int Address = 60;
///
- /// Represents the payload type of the register. This field is constant.
+ /// Represents the payload type of the register. This field is constant.
///
public const PayloadType RegisterType = PayloadType.Float;
///
- /// Represents the length of the register. This field is constant.
+ /// Represents the length of the register. This field is constant.
///
public const int RegisterLength = 1;
///
- /// Returns the payload data for register messages.
+ /// Returns the payload data for register messages.
///
/// A object representing the register message.
/// A value representing the message payload.
@@ -3193,7 +3343,7 @@ public static float GetPayload(HarpMessage message)
}
///
- /// Returns the timestamped payload data for register messages.
+ /// Returns the timestamped payload data for register messages.
///
/// A object representing the register message.
/// A value representing the timestamped message payload.
@@ -3203,12 +3353,12 @@ public static Timestamped GetTimestampedPayload(HarpMessage message)
}
///
- /// Returns a Harp message for the register.
+ /// Returns a Harp message for the register.
///
/// The type of the Harp message.
/// The value to be stored in the message payload.
///
- /// A object for the register
+ /// A object for the register
/// with the specified message type and payload.
///
public static HarpMessage FromPayload(MessageType messageType, float value)
@@ -3217,14 +3367,14 @@ public static HarpMessage FromPayload(MessageType messageType, float value)
}
///
- /// Returns a timestamped Harp message for the
+ /// Returns a timestamped Harp message for the
/// register.
///
/// The timestamp of the message payload, in seconds.
/// The type of the Harp message.
/// The value to be stored in the message payload.
///
- /// A object for the register
+ /// A object for the register
/// with the specified message type, timestamp, and payload.
///
public static HarpMessage FromPayload(double timestamp, MessageType messageType, float value)
@@ -3235,25 +3385,25 @@ public static HarpMessage FromPayload(double timestamp, MessageType messageType,
///
/// Provides methods for manipulating timestamped messages from the
- /// LedD1PwmDutyCycleReal register.
+ /// Led1PwmDutyCycleReal register.
///
- ///
- [Description("Filters and selects timestamped messages from the LedD1PwmDutyCycleReal register.")]
- public partial class TimestampedLedD1PwmDutyCycleReal
+ ///
+ [Description("Filters and selects timestamped messages from the Led1PwmDutyCycleReal register.")]
+ public partial class TimestampedLed1PwmDutyCycleReal
{
///
- /// Represents the address of the register. This field is constant.
+ /// Represents the address of the register. This field is constant.
///
- public const int Address = LedD1PwmDutyCycleReal.Address;
+ public const int Address = Led1PwmDutyCycleReal.Address;
///
- /// Returns timestamped payload data for register messages.
+ /// Returns timestamped payload data for register messages.
///
/// A object representing the register message.
/// A value representing the timestamped message payload.
public static Timestamped GetPayload(HarpMessage message)
{
- return LedD1PwmDutyCycleReal.GetTimestampedPayload(message);
+ return Led1PwmDutyCycleReal.GetTimestampedPayload(message);
}
}
@@ -3698,7 +3848,7 @@ public static Timestamped GetPayload(HarpMessage message)
///
///
///
- ///
+ ///
///
///
///
@@ -3731,7 +3881,7 @@ public static Timestamped GetPayload(HarpMessage message)
[XmlInclude(typeof(CreateLed0PwmRealPayload))]
[XmlInclude(typeof(CreateLed0PwmDutyCycleRealPayload))]
[XmlInclude(typeof(CreateLed1PwmRealPayload))]
- [XmlInclude(typeof(CreateLedD1PwmDutyCycleRealPayload))]
+ [XmlInclude(typeof(CreateLed1PwmDutyCycleRealPayload))]
[XmlInclude(typeof(CreateAuxDigitalOutputStatePayload))]
[XmlInclude(typeof(CreateAuxLedPowerPayload))]
[XmlInclude(typeof(CreateDigitalOutputStatePayload))]
@@ -3764,7 +3914,7 @@ public static Timestamped GetPayload(HarpMessage message)
[XmlInclude(typeof(CreateTimestampedLed0PwmRealPayload))]
[XmlInclude(typeof(CreateTimestampedLed0PwmDutyCycleRealPayload))]
[XmlInclude(typeof(CreateTimestampedLed1PwmRealPayload))]
- [XmlInclude(typeof(CreateTimestampedLedD1PwmDutyCycleRealPayload))]
+ [XmlInclude(typeof(CreateTimestampedLed1PwmDutyCycleRealPayload))]
[XmlInclude(typeof(CreateTimestampedAuxDigitalOutputStatePayload))]
[XmlInclude(typeof(CreateTimestampedAuxLedPowerPayload))]
[XmlInclude(typeof(CreateTimestampedDigitalOutputStatePayload))]
@@ -5362,33 +5512,33 @@ public HarpMessage GetMessage(double timestamp, MessageType messageType)
/// Represents an operator that creates a message payload
/// that get the real duty cycle (%) of LED1 when in Pwm mode.
///
- [DisplayName("LedD1PwmDutyCycleRealPayload")]
+ [DisplayName("Led1PwmDutyCycleRealPayload")]
[Description("Creates a message payload that get the real duty cycle (%) of LED1 when in Pwm mode.")]
- public partial class CreateLedD1PwmDutyCycleRealPayload
+ public partial class CreateLed1PwmDutyCycleRealPayload
{
///
/// Gets or sets the value that get the real duty cycle (%) of LED1 when in Pwm mode.
///
[Description("The value that get the real duty cycle (%) of LED1 when in Pwm mode.")]
- public float LedD1PwmDutyCycleReal { get; set; }
+ public float Led1PwmDutyCycleReal { get; set; }
///
- /// Creates a message payload for the LedD1PwmDutyCycleReal register.
+ /// Creates a message payload for the Led1PwmDutyCycleReal register.
///
/// The created message payload value.
public float GetPayload()
{
- return LedD1PwmDutyCycleReal;
+ return Led1PwmDutyCycleReal;
}
///
/// Creates a message that get the real duty cycle (%) of LED1 when in Pwm mode.
///
/// Specifies the type of the created message.
- /// A new message for the LedD1PwmDutyCycleReal register.
+ /// A new message for the Led1PwmDutyCycleReal register.
public HarpMessage GetMessage(MessageType messageType)
{
- return Harp.LedArray.LedD1PwmDutyCycleReal.FromPayload(messageType, GetPayload());
+ return Harp.LedArray.Led1PwmDutyCycleReal.FromPayload(messageType, GetPayload());
}
}
@@ -5396,19 +5546,19 @@ public HarpMessage GetMessage(MessageType messageType)
/// Represents an operator that creates a timestamped message payload
/// that get the real duty cycle (%) of LED1 when in Pwm mode.
///
- [DisplayName("TimestampedLedD1PwmDutyCycleRealPayload")]
+ [DisplayName("TimestampedLed1PwmDutyCycleRealPayload")]
[Description("Creates a timestamped message payload that get the real duty cycle (%) of LED1 when in Pwm mode.")]
- public partial class CreateTimestampedLedD1PwmDutyCycleRealPayload : CreateLedD1PwmDutyCycleRealPayload
+ public partial class CreateTimestampedLed1PwmDutyCycleRealPayload : CreateLed1PwmDutyCycleRealPayload
{
///
/// Creates a timestamped message that get the real duty cycle (%) of LED1 when in Pwm mode.
///
/// The timestamp of the message payload, in seconds.
/// Specifies the type of the created message.
- /// A new timestamped message for the LedD1PwmDutyCycleReal register.
+ /// A new timestamped message for the Led1PwmDutyCycleReal register.
public HarpMessage GetMessage(double timestamp, MessageType messageType)
{
- return Harp.LedArray.LedD1PwmDutyCycleReal.FromPayload(timestamp, messageType, GetPayload());
+ return Harp.LedArray.Led1PwmDutyCycleReal.FromPayload(timestamp, messageType, GetPayload());
}
}
diff --git a/Interface/Harp.LedArray/Harp.LedArray.csproj b/Interface/Harp.LedArray/Harp.LedArray.csproj
index 1adb6c6..f3a7968 100644
--- a/Interface/Harp.LedArray/Harp.LedArray.csproj
+++ b/Interface/Harp.LedArray/Harp.LedArray.csproj
@@ -18,7 +18,7 @@
LICENSE
..\bin\$(Configuration)
net462;netstandard2.0
- 0.2.0
+ 0.3.0
9.0
diff --git a/device.yml b/device.yml
index cc9006d..f50db56 100644
--- a/device.yml
+++ b/device.yml
@@ -207,7 +207,7 @@ registers:
access: Read
type: Float
description: Get the real frequency (Hz) of LED1 when in Pwm mode.
- LedD1PwmDutyCycleReal:
+ Led1PwmDutyCycleReal:
address: 60
access: Read
type: Float