diff --git a/.cursor/rules b/.cursor/rules new file mode 100644 index 0000000..5bdd3e0 --- /dev/null +++ b/.cursor/rules @@ -0,0 +1,184 @@ +# DAQiFi Core - Cursor AI Rules + +## Project Context +You are working on the DAQiFi Core library - a .NET library that provides foundational interfaces and implementations for interacting with DAQiFi hardware devices. This library serves as the core foundation that will be used by desktop applications, web services, and other DAQiFi software. + +## Architecture & Patterns + +### Device Interface Pattern +- All device classes should implement `IDevice` or `IStreamingDevice` interfaces +- Use event-driven architecture with `StatusChanged` and `MessageReceived` events +- Implement virtual methods that can be overridden for testing and customization +- Use generic `Send(IOutboundMessage)` methods for type safety + +### Message System +- All outbound messages implement `IOutboundMessage` with strongly-typed data payloads +- All inbound messages implement `IInboundMessage` +- Use `ScpiMessageProducer` for creating SCPI command messages +- Support multiple message formats: SCPI strings, Protocol Buffers, JSON + +### Connection Management +- Use `ConnectionStatus` enum for device states: Disconnected, Connecting, Connected, Lost +- Implement connection state transitions with proper event notifications +- Always check `IsConnected` before sending messages +- Throw `InvalidOperationException` for operations on disconnected devices + +## Code Style & Standards + +### C# Conventions +- Use nullable reference types (`#nullable enable`) +- Prefer `var` only when type is obvious from right-hand side +- Use expression-bodied members for simple properties and methods +- Follow Microsoft C# naming conventions (PascalCase for public members, camelCase for private) + +### Documentation +- ALL public classes, interfaces, and members MUST have XML documentation +- Use ``, ``, ``, `` tags appropriately +- Include code examples in documentation for complex APIs +- Document thread safety characteristics +- Use `` for cross-references + +### Error Handling +- Use specific exception types (`InvalidOperationException`, `ArgumentException`, etc.) +- Include clear, actionable error messages +- Document all exceptions that public methods can throw +- Validate parameters and throw appropriate exceptions early + +### Testing Patterns +- Create testable versions of classes using inheritance and virtual methods +- Use `TestableDaqifiDevice` pattern for mocking device behavior +- Test all public methods, properties, and events +- Include tests for error conditions and edge cases +- Use meaningful test method names: `MethodName_Scenario_ExpectedResult` + +## Naming Conventions + +### Classes & Interfaces +- Device interfaces: `IDevice`, `IStreamingDevice`, `IDiscoveryService` +- Device implementations: `DaqifiDevice`, `DaqifiStreamingDevice` +- Event args: `DeviceStatusEventArgs`, `MessageReceivedEventArgs` +- Message types: `ScpiMessage`, `ProtobufMessage` +- Producers: `ScpiMessageProducer`, `DeviceDiscoveryService` + +### Methods & Properties +- Connection methods: `Connect()`, `Disconnect()`, `IsConnected` +- Messaging: `Send()`, `OnMessageReceived()` +- Events: `StatusChanged`, `MessageReceived`, `DeviceDiscovered` +- Status: `Status`, `ConnectionStatus`, `IsStreaming` + +## File Organization +``` +src/Daqifi.Core/ +├── Device/ # Device interfaces and implementations +├── Communication/ # Message types and producers +│ ├── Messages/ # IOutboundMessage, IInboundMessage implementations +│ └── Producers/ # Message factory classes +├── Discovery/ # Device discovery services (future) +└── Connection/ # Connection management (future) +``` + +## Multi-Targeting +- Target both .NET 8.0 and .NET 9.0 +- Use `#if` directives sparingly and only for platform-specific code +- Prefer feature detection over version detection +- Test on both target frameworks + +## Hardware Domain Knowledge + +### DAQiFi Device Types +- Basic devices: Connection, messaging, status monitoring +- Streaming devices: Add real-time data acquisition capabilities +- Discovery: Network scanning, mDNS, USB enumeration + +### Communication Protocols +- SCPI (Standard Commands for Programmable Instruments) +- Protocol Buffers for binary data +- TCP/UDP for network communication +- USB for direct connection + +### Device Operations +- Connection lifecycle: Discover → Connect → Configure → Stream → Disconnect +- Status monitoring: Connection health, error states, device capabilities +- Message flow: Commands (outbound) → Responses (inbound) +- Streaming: Start/stop, frequency control, data consumption + +## Best Practices + +### Performance +- Use async/await for I/O operations +- Implement proper disposal patterns for connections +- Consider connection pooling for multiple devices +- Buffer streaming data appropriately + +### Threading +- Document thread safety of all public APIs +- Use locks judiciously and avoid deadlocks +- Consider using `ConcurrentCollections` for shared state +- Event handlers should be thread-safe + +### Extensibility +- Use interfaces for dependency injection +- Make methods virtual when inheritance is expected +- Provide clear extension points for custom implementations +- Support plugin architectures where appropriate + +## Code Examples + +### Device Implementation Template +```csharp +/// +/// Represents a [specific device type] that [brief description]. +/// +public class CustomDevice : DaqifiDevice, ICustomInterface +{ + /// + /// Initializes a new instance of the class. + /// + /// The device name. + /// The device IP address. + public CustomDevice(string name, IPAddress? ipAddress = null) + : base(name, ipAddress) + { + } + + /// + /// Custom operation specific to this device type. + /// + /// Thrown when device is not connected. + public void CustomOperation() + { + if (!IsConnected) + throw new InvalidOperationException("Device is not connected."); + + // Implementation + } +} +``` + +### Test Method Template +```csharp +[Fact] +public void MethodName_WhenCondition_ShouldExpectedBehavior() +{ + // Arrange + var device = new TestableDevice("TestDevice"); + + // Act + var result = device.MethodName(); + + // Assert + Assert.Equal(expectedValue, result); +} +``` + +## Integration Guidelines +- This library will be consumed by `daqifi-desktop` and other applications +- Maintain backward compatibility within major versions +- Consider migration paths when making breaking changes +- Provide clear upgrade documentation + +## Git Conventions +- Feature branches: `feature/descriptive-name` +- Commit messages: `feat:`, `fix:`, `docs:`, `test:`, `refactor:` +- Include issue numbers: `feat: implement device discovery (#123)` +- Keep commits focused and atomic \ No newline at end of file diff --git a/DEVICE_INTERFACES.md b/DEVICE_INTERFACES.md new file mode 100644 index 0000000..6ab7dd0 --- /dev/null +++ b/DEVICE_INTERFACES.md @@ -0,0 +1,264 @@ +# DAQiFi Device Interfaces + +This document provides examples and usage guidance for the DAQiFi device interfaces that were migrated from `daqifi-desktop` to establish the foundation for device interaction in the `daqifi-core` library. + +## Overview + +The device interfaces provide a consistent API for discovering, connecting to, and communicating with DAQiFi hardware devices. They abstract away hardware implementation details and provide a clean interface for application developers. + +## Core Interfaces + +### IDevice +The base interface for all DAQiFi devices, providing fundamental connection and communication capabilities. + +### IStreamingDevice +Extends `IDevice` with data streaming functionality for devices that support continuous data acquisition. + +## Implementation Classes + +### DaqifiDevice +The base implementation of `IDevice` that provides core device functionality. + +### DaqifiStreamingDevice +Extends `DaqifiDevice` with streaming capabilities, implementing `IStreamingDevice`. + +## Usage Examples + +### Basic Device Connection + +```csharp +using Daqifi.Core.Device; +using System.Net; + +// Create a device instance +var device = new DaqifiDevice("My DAQiFi Device", IPAddress.Parse("192.168.1.100")); + +// Subscribe to status changes +device.StatusChanged += (sender, args) => +{ + Console.WriteLine($"Device status changed to: {args.Status}"); +}; + +// Subscribe to messages +device.MessageReceived += (sender, args) => +{ + Console.WriteLine($"Message received: {args.Message.Data}"); +}; + +// Connect to the device +device.Connect(); + +// Send a message +device.Send(ScpiMessageProducer.GetDeviceInfo); + +// Disconnect when done +device.Disconnect(); +``` + +### Streaming Device Usage + +```csharp +using Daqifi.Core.Device; +using Daqifi.Core.Communication.Producers; +using System.Net; + +// Create a streaming device +var streamingDevice = new DaqifiStreamingDevice("My Streaming Device", IPAddress.Parse("192.168.1.100")); + +// Configure streaming frequency +streamingDevice.StreamingFrequency = 1000; // 1000 Hz + +// Connect to the device +streamingDevice.Connect(); + +// Start streaming data +streamingDevice.StartStreaming(); +Console.WriteLine($"Started streaming at {streamingDevice.StreamingFrequency} Hz"); + +// Stream for some time... +await Task.Delay(TimeSpan.FromSeconds(10)); + +// Stop streaming +streamingDevice.StopStreaming(); +Console.WriteLine("Stopped streaming"); + +// Disconnect +streamingDevice.Disconnect(); +``` + +### Error Handling + +```csharp +try +{ + var device = new DaqifiDevice("Test Device"); + + // This will throw an exception because device is not connected + device.Send(ScpiMessageProducer.GetDeviceInfo); +} +catch (InvalidOperationException ex) +{ + Console.WriteLine($"Error: {ex.Message}"); + // Output: Error: Device is not connected. +} +``` + +### Custom Device Implementation + +```csharp +using Daqifi.Core.Device; +using Daqifi.Core.Communication.Messages; + +public class CustomDaqifiDevice : DaqifiDevice +{ + public CustomDaqifiDevice(string name, IPAddress? ipAddress = null) + : base(name, ipAddress) + { + } + + public override void Send(IOutboundMessage message) + { + // Add custom logging + Console.WriteLine($"Sending message: {message.Data}"); + + // Call base implementation + base.Send(message); + } + + protected override void OnMessageReceived(IInboundMessage message) + { + // Add custom message processing + Console.WriteLine($"Processing received message: {message.Data}"); + + // Call base implementation to fire events + base.OnMessageReceived(message); + } +} +``` + +## Connection Status Monitoring + +```csharp +var device = new DaqifiDevice("Monitor Device"); + +device.StatusChanged += (sender, args) => +{ + switch (args.Status) + { + case ConnectionStatus.Disconnected: + Console.WriteLine("Device disconnected"); + break; + case ConnectionStatus.Connecting: + Console.WriteLine("Connecting to device..."); + break; + case ConnectionStatus.Connected: + Console.WriteLine("Device connected successfully"); + break; + } +}; + +// Check connection status +if (device.IsConnected) +{ + Console.WriteLine("Device is currently connected"); +} +``` + +## Message Types + +The device interfaces work with strongly-typed messages: + +```csharp +// String-based SCPI messages +device.Send(ScpiMessageProducer.GetDeviceInfo); +device.Send(ScpiMessageProducer.StartStreaming(1000)); + +// Custom message types can be created +public class CustomMessage : IOutboundMessage +{ + public MyDataType Data { get; set; } + + public byte[] GetBytes() + { + // Custom serialization logic + return Encoding.UTF8.GetBytes(Data.ToString()); + } +} + +device.Send(new CustomMessage { Data = myData }); +``` + +## Thread Safety + +The device implementations are not thread-safe by default. For multi-threaded scenarios, ensure proper synchronization: + +```csharp +private readonly object _deviceLock = new object(); + +public void SafeSendMessage(IOutboundMessage message) +{ + lock (_deviceLock) + { + if (device.IsConnected) + { + device.Send(message); + } + } +} +``` + +## Integration with Application + +```csharp +public class DeviceManager +{ + private readonly List _devices = new(); + + public void AddDevice(IDevice device) + { + device.StatusChanged += OnDeviceStatusChanged; + device.MessageReceived += OnDeviceMessageReceived; + _devices.Add(device); + } + + public async Task ConnectAllDevicesAsync() + { + foreach (var device in _devices) + { + device.Connect(); + // Add delay or wait for connection status + await Task.Delay(1000); + } + } + + private void OnDeviceStatusChanged(object sender, DeviceStatusEventArgs e) + { + var device = (IDevice)sender; + Console.WriteLine($"Device '{device.Name}' status: {e.Status}"); + } + + private void OnDeviceMessageReceived(object sender, MessageReceivedEventArgs e) + { + var device = (IDevice)sender; + Console.WriteLine($"Device '{device.Name}' received: {e.Message.Data}"); + } +} +``` + +## Features + +- **Clean Abstraction**: Hardware details are hidden behind well-defined interfaces +- **Event-Driven**: Status changes and message reception are handled via events +- **Type Safety**: Generic message types provide compile-time safety +- **Extensible**: Interfaces can be implemented for custom device behaviors +- **Modern C#**: Uses nullable reference types and modern C# features +- **Cross-Platform**: Compatible with .NET 8.0 and 9.0 + +## Next Steps + +This interface foundation enables: +- Device discovery services +- Connection management +- Message consumer implementation +- Channel configuration +- Protocol-specific implementations (TCP, UDP, Serial, etc.) \ No newline at end of file diff --git a/src/Daqifi.Core.Tests/Daqifi.Core.Tests.csproj b/src/Daqifi.Core.Tests/Daqifi.Core.Tests.csproj index 4056e1a..a5be741 100644 --- a/src/Daqifi.Core.Tests/Daqifi.Core.Tests.csproj +++ b/src/Daqifi.Core.Tests/Daqifi.Core.Tests.csproj @@ -1,33 +1,33 @@ - - - - net8.0;net9.0 - enable - enable - false - true - cobertura - $(MSBuildThisFileDirectory)../coverage/ - GeneratedCodeAttribute - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - + + + + net8.0;net9.0 + enable + enable + false + true + cobertura + $(MSBuildThisFileDirectory)../coverage/ + GeneratedCodeAttribute + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + diff --git a/src/Daqifi.Core.Tests/Device/DaqifiDeviceTests.cs b/src/Daqifi.Core.Tests/Device/DaqifiDeviceTests.cs new file mode 100644 index 0000000..8ebb8e0 --- /dev/null +++ b/src/Daqifi.Core.Tests/Device/DaqifiDeviceTests.cs @@ -0,0 +1,82 @@ +using Daqifi.Core.Device; +using System.Collections.Generic; +using System.Net; +using Xunit; + +namespace Daqifi.Core.Tests.Device +{ + public class DaqifiDeviceTests + { + [Fact] + public void Constructor_InitializesPropertiesCorrectly() + { + // Arrange + const string deviceName = "TestDevice"; + var ipAddress = IPAddress.Parse("192.168.1.1"); + + // Act + var device = new DaqifiDevice(deviceName, ipAddress); + + // Assert + Assert.Equal(deviceName, device.Name); + Assert.Equal(ipAddress, device.IpAddress); + Assert.Equal(ConnectionStatus.Disconnected, device.Status); + Assert.False(device.IsConnected); + } + + [Fact] + public void Connect_ChangesStatusAndRaisesEvents() + { + // Arrange + var device = new DaqifiDevice("TestDevice"); + var statusChanges = new List(); + device.StatusChanged += (_, args) => + { + statusChanges.Add(args.Status); + }; + + // Act + device.Connect(); + + // Assert + Assert.Equal(2, statusChanges.Count); + Assert.Equal(ConnectionStatus.Connecting, statusChanges[0]); + Assert.Equal(ConnectionStatus.Connected, statusChanges[1]); + Assert.Equal(ConnectionStatus.Connected, device.Status); + Assert.True(device.IsConnected); + } + + [Fact] + public void Disconnect_ChangesStatusAndRaisesEvent() + { + // Arrange + var device = new DaqifiDevice("TestDevice"); + device.Connect(); // Connect first + + var receivedArgs = new List(); + device.StatusChanged += (_, args) => + { + receivedArgs.Add(args); + }; + + // Act + device.Disconnect(); + + // Assert + var arg = Assert.Single(receivedArgs); + Assert.Equal(ConnectionStatus.Disconnected, arg.Status); + Assert.Equal(ConnectionStatus.Disconnected, device.Status); + Assert.False(device.IsConnected); + } + + [Fact] + public void Send_WhenDisconnected_ThrowsInvalidOperationException() + { + // Arrange + var device = new DaqifiDevice("TestDevice"); + + // Act & Assert + Assert.Throws(() => device.Send(new Daqifi.Core.Communication.Messages.ScpiMessage(""))); + } + } +} \ No newline at end of file diff --git a/src/Daqifi.Core.Tests/Device/DaqifiStreamingDeviceTests.cs b/src/Daqifi.Core.Tests/Device/DaqifiStreamingDeviceTests.cs new file mode 100644 index 0000000..a6d5911 --- /dev/null +++ b/src/Daqifi.Core.Tests/Device/DaqifiStreamingDeviceTests.cs @@ -0,0 +1,104 @@ +using Daqifi.Core.Communication.Messages; +using Daqifi.Core.Communication.Producers; +using Daqifi.Core.Device; +using System.Collections.Generic; +using System.Net; +using Xunit; + +namespace Daqifi.Core.Tests.Device +{ + public class DaqifiStreamingDeviceTests + { + [Fact] + public void Constructor_InitializesStreamingFrequency() + { + // Arrange & Act + var device = new DaqifiStreamingDevice("TestDevice"); + + // Assert + Assert.Equal(100, device.StreamingFrequency); + Assert.False(device.IsStreaming); + } + + [Fact] + public void StartStreaming_WhenConnected_SendsCorrectCommandAndSetsIsStreaming() + { + // Arrange + var device = new TestableDaqifiStreamingDevice("TestDevice"); + device.Connect(); + device.StreamingFrequency = 200; + + // Act + device.StartStreaming(); + + // Assert + Assert.True(device.IsStreaming); + var sentMessage = Assert.Single(device.SentMessages); + var expectedMessage = ScpiMessageProducer.StartStreaming(200); + Assert.Equal(expectedMessage.Data, sentMessage.Data); + } + + [Fact] + public void StopStreaming_WhenConnected_SendsCorrectCommandAndSetsIsStreaming() + { + // Arrange + var device = new TestableDaqifiStreamingDevice("TestDevice"); + device.Connect(); + device.StartStreaming(); + device.SentMessages.Clear(); + + // Act + device.StopStreaming(); + + // Assert + Assert.False(device.IsStreaming); + var sentMessage = Assert.Single(device.SentMessages); + var expectedMessage = ScpiMessageProducer.StopStreaming; + Assert.Equal(expectedMessage.Data, sentMessage.Data); + } + + [Fact] + public void StartStreaming_WhenDisconnected_ThrowsInvalidOperationException() + { + // Arrange + var device = new DaqifiStreamingDevice("TestDevice"); + + // Act & Assert + var exception = Assert.Throws(() => device.StartStreaming()); + Assert.Equal("Device is not connected.", exception.Message); + } + + [Fact] + public void StopStreaming_WhenDisconnected_ThrowsInvalidOperationException() + { + // Arrange + var device = new DaqifiStreamingDevice("TestDevice"); + + // Act & Assert + var exception = Assert.Throws(() => device.StopStreaming()); + Assert.Equal("Device is not connected.", exception.Message); + } + + /// + /// A testable version of DaqifiStreamingDevice that captures sent messages. + /// + private class TestableDaqifiStreamingDevice : DaqifiStreamingDevice + { + public List> SentMessages { get; } = new(); + + public TestableDaqifiStreamingDevice(string name, IPAddress? ipAddress = null) : base(name, ipAddress) + { + } + + public override void Send(IOutboundMessage message) + { + // Override to capture the message instead of sending it. + // This avoids the base class's check for a real connection. + if (message is IOutboundMessage stringMessage) + { + SentMessages.Add(stringMessage); + } + } + } + } +} \ No newline at end of file diff --git a/src/Daqifi.Core/Daqifi.Core.csproj b/src/Daqifi.Core/Daqifi.Core.csproj index 12f3b08..095e853 100644 --- a/src/Daqifi.Core/Daqifi.Core.csproj +++ b/src/Daqifi.Core/Daqifi.Core.csproj @@ -1,34 +1,34 @@ - - - - net8.0;net9.0 - enable - enable - - - Daqifi.Core - DAQiFi - Core library for interacting with DAQiFi devices - MIT - https://github.com/daqifi/daqifi-core - README.md - daqifi;daq;data-acquisition;measurement;instrumentation - https://github.com/daqifi/daqifi-core - git - - - true - true - snupkg - true - true - true - - - - - - - - - + + + + net8.0;net9.0 + enable + enable + + + Daqifi.Core + DAQiFi + Core library for interacting with DAQiFi devices + MIT + https://github.com/daqifi/daqifi-core + README.md + daqifi;daq;data-acquisition;measurement;instrumentation + https://github.com/daqifi/daqifi-core + git + + + true + true + snupkg + true + true + true + + + + + + + + + diff --git a/src/Daqifi.Core/Device/ConnectionStatus.cs b/src/Daqifi.Core/Device/ConnectionStatus.cs new file mode 100644 index 0000000..c2b02a3 --- /dev/null +++ b/src/Daqifi.Core/Device/ConnectionStatus.cs @@ -0,0 +1,28 @@ +namespace Daqifi.Core.Device +{ + /// + /// Represents the connection status of a device. + /// + public enum ConnectionStatus + { + /// + /// The device is disconnected. + /// + Disconnected, + + /// + /// The device is in the process of connecting. + /// + Connecting, + + /// + /// The device is connected. + /// + Connected, + + /// + /// The device connection has been lost. + /// + Lost + } +} \ No newline at end of file diff --git a/src/Daqifi.Core/Device/DaqifiDevice.cs b/src/Daqifi.Core/Device/DaqifiDevice.cs new file mode 100644 index 0000000..0fa974a --- /dev/null +++ b/src/Daqifi.Core/Device/DaqifiDevice.cs @@ -0,0 +1,111 @@ +using Daqifi.Core.Communication.Messages; +using System; +using System.Net; + +#nullable enable + +namespace Daqifi.Core.Device +{ + /// + /// Represents a DAQiFi device that can be connected to and communicated with. + /// This is the base implementation of the IDevice interface. + /// + public class DaqifiDevice : IDevice + { + /// + /// Gets the name of the device. + /// + public string Name { get; } + + /// + /// Gets the IP address of the device, if known. + /// + public IPAddress? IpAddress { get; } + + /// + /// Gets a value indicating whether the device is currently connected. + /// + public bool IsConnected => Status == ConnectionStatus.Connected; + + private ConnectionStatus _status; + + /// + /// Gets the current connection status of the device. + /// + public ConnectionStatus Status + { + get => _status; + private set + { + if (_status == value) return; + _status = value; + StatusChanged?.Invoke(this, new DeviceStatusEventArgs(_status)); + } + } + + /// + /// Occurs when the device status changes. + /// + public event EventHandler? StatusChanged; + + /// + /// Occurs when a message is received from the device. + /// + public event EventHandler? MessageReceived; + + /// + /// Initializes a new instance of the class. + /// + /// The name of the device. + /// The IP address of the device, if known. + public DaqifiDevice(string name, IPAddress? ipAddress = null) + { + Name = name; + IpAddress = ipAddress; + _status = ConnectionStatus.Disconnected; + } + + /// + /// Connects to the device. + /// + public void Connect() + { + // TODO: Add implementation + Status = ConnectionStatus.Connecting; + Status = ConnectionStatus.Connected; + } + + /// + /// Disconnects from the device. + /// + public void Disconnect() + { + // TODO: Add implementation + Status = ConnectionStatus.Disconnected; + } + + /// + /// Sends a message to the device. + /// + /// The type of the message data payload. + /// The message to send to the device. + /// Thrown when the device is not connected. + public virtual void Send(IOutboundMessage message) + { + // TODO: Add implementation + if (!IsConnected) + { + throw new InvalidOperationException("Device is not connected."); + } + } + + /// + /// Raises the event when a message is received from the device. + /// + /// The message received from the device. + protected virtual void OnMessageReceived(IInboundMessage message) + { + MessageReceived?.Invoke(this, new MessageReceivedEventArgs(message)); + } + } +} \ No newline at end of file diff --git a/src/Daqifi.Core/Device/DaqifiStreamingDevice.cs b/src/Daqifi.Core/Device/DaqifiStreamingDevice.cs new file mode 100644 index 0000000..d8279f5 --- /dev/null +++ b/src/Daqifi.Core/Device/DaqifiStreamingDevice.cs @@ -0,0 +1,70 @@ +using Daqifi.Core.Communication.Messages; +using Daqifi.Core.Communication.Producers; +using System; +using System.Net; + +#nullable enable + +namespace Daqifi.Core.Device +{ + /// + /// Represents a DAQiFi device that supports data streaming functionality. + /// Extends the base DaqifiDevice with streaming-specific operations. + /// + public class DaqifiStreamingDevice : DaqifiDevice, IStreamingDevice + { + /// + /// Gets a value indicating whether the device is currently streaming data. + /// + public bool IsStreaming { get; private set; } + + /// + /// Gets or sets the streaming frequency in Hz (samples per second). + /// + public int StreamingFrequency { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the device. + /// The IP address of the device, if known. + public DaqifiStreamingDevice(string name, IPAddress? ipAddress = null) : base(name, ipAddress) + { + StreamingFrequency = 100; + } + + /// + /// Starts streaming data from the device at the configured frequency. + /// + /// Thrown when the device is not connected. + public void StartStreaming() + { + if (!IsConnected) + { + throw new InvalidOperationException("Device is not connected."); + } + + if (IsStreaming) return; + + IsStreaming = true; + Send(ScpiMessageProducer.StartStreaming(StreamingFrequency)); + } + + /// + /// Stops streaming data from the device. + /// + /// Thrown when the device is not connected. + public void StopStreaming() + { + if (!IsConnected) + { + throw new InvalidOperationException("Device is not connected."); + } + + if (!IsStreaming) return; + + IsStreaming = false; + Send(ScpiMessageProducer.StopStreaming); + } + } +} \ No newline at end of file diff --git a/src/Daqifi.Core/Device/DeviceStatusEventArgs.cs b/src/Daqifi.Core/Device/DeviceStatusEventArgs.cs new file mode 100644 index 0000000..252828b --- /dev/null +++ b/src/Daqifi.Core/Device/DeviceStatusEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace Daqifi.Core.Device +{ + /// + /// Provides data for the event. + /// + public class DeviceStatusEventArgs : EventArgs + { + /// + /// Gets the connection status. + /// + public ConnectionStatus Status { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The connection status. + public DeviceStatusEventArgs(ConnectionStatus status) + { + Status = status; + } + } +} \ No newline at end of file diff --git a/src/Daqifi.Core/Device/IDevice.cs b/src/Daqifi.Core/Device/IDevice.cs new file mode 100644 index 0000000..dc9e32a --- /dev/null +++ b/src/Daqifi.Core/Device/IDevice.cs @@ -0,0 +1,60 @@ +using Daqifi.Core.Communication.Messages; +using System; +using System.Net; + +#nullable enable + +namespace Daqifi.Core.Device +{ + /// + /// Base interface for all DAQiFi devices. + /// + public interface IDevice + { + /// + /// Gets the name of the device. + /// + string Name { get; } + + /// + /// Gets the IP address of the device. + /// + IPAddress? IpAddress { get; } + + /// + /// Gets a value indicating whether the device is connected. + /// + bool IsConnected { get; } + + /// + /// Gets the current connection status of the device. + /// + ConnectionStatus Status { get; } + + /// + /// Occurs when the device status changes. + /// + event EventHandler StatusChanged; + + /// + /// Occurs when a message is received from the device. + /// + event EventHandler MessageReceived; + + /// + /// Connects to the device. + /// + void Connect(); + + /// + /// Disconnects from the device. + /// + void Disconnect(); + + /// + /// Sends a message to the device. + /// + /// The message to send. + void Send(IOutboundMessage message); + } +} // test change diff --git a/src/Daqifi.Core/Device/IStreamingDevice.cs b/src/Daqifi.Core/Device/IStreamingDevice.cs new file mode 100644 index 0000000..de35edd --- /dev/null +++ b/src/Daqifi.Core/Device/IStreamingDevice.cs @@ -0,0 +1,28 @@ +namespace Daqifi.Core.Device +{ + /// + /// Represents a device that supports data streaming. + /// + public interface IStreamingDevice : IDevice + { + /// + /// Gets or sets the streaming frequency in Hz. + /// + int StreamingFrequency { get; set; } + + /// + /// Gets a value indicating whether the device is currently streaming data. + /// + bool IsStreaming { get; } + + /// + /// Starts streaming data from the device. + /// + void StartStreaming(); + + /// + /// Stops streaming data from the device. + /// + void StopStreaming(); + } +} \ No newline at end of file diff --git a/src/Daqifi.Core/Device/MessageReceivedEventArgs.cs b/src/Daqifi.Core/Device/MessageReceivedEventArgs.cs new file mode 100644 index 0000000..f0f3606 --- /dev/null +++ b/src/Daqifi.Core/Device/MessageReceivedEventArgs.cs @@ -0,0 +1,25 @@ +using Daqifi.Core.Communication.Messages; +using System; + +namespace Daqifi.Core.Device +{ + /// + /// Provides data for the event. + /// + public class MessageReceivedEventArgs : EventArgs + { + /// + /// Gets the inbound message. + /// + public IInboundMessage Message { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The inbound message. + public MessageReceivedEventArgs(IInboundMessage message) + { + Message = message; + } + } +} \ No newline at end of file