Skip to content

Commit

Permalink
feat: Add logging to the TCP and USB connection.
Browse files Browse the repository at this point in the history
  • Loading branch information
vojtech committed Sep 12, 2023
1 parent 0971f15 commit e0ebd37
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
22 changes: 21 additions & 1 deletion ScpiNet/TcpScpiConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.Extensions.Logging;

namespace ScpiNet
{
Expand Down Expand Up @@ -35,17 +36,29 @@ public class TcpScpiConnection : IScpiConnection
/// </summary>
public int Port { get; }

/// <summary>
/// Default response timeout in milliseconds.
/// </summary>
public const int DefaultTimeout = 250;

/// <summary>
/// Logger instance.
/// </summary>
protected ILogger<TcpScpiConnection> Logger { get; }

/// <summary>
/// Creates an instance of TcpScpiConnection.
/// </summary>
/// <param name="host">DNS name or IP address of the SCPI device.</param>
/// <param name="port">Target TCP port of the connected SCPI device.</param>
/// <param name="timeout">Read/write operation timeout in milliseconds.</param>
public TcpScpiConnection(string host, int port, int timeout = 250)
/// <param name="logger">Instance of a logger abstraction.</param>
public TcpScpiConnection(string host, int port, int timeout = DefaultTimeout, ILogger<TcpScpiConnection> logger = null)
{
Host = host;
Port = port;
Timeout = timeout;
Logger = logger;
}

/// <summary>
Expand All @@ -62,6 +75,7 @@ public async Task Open(CancellationToken cancellationToken = default)
};

// Start asynchronous connection and connection timeout task:
Logger?.LogInformation($"Creating TCP connection to {Host}:{Port}...", Host, Port);
Task timeoutTask = Task.Delay(100, cancellationToken);
Task connTask = _Client.ConnectAsync(Host, Port);

Expand All @@ -70,24 +84,29 @@ public async Task Open(CancellationToken cancellationToken = default)

// Check for cancelling:
if (timeoutTask.IsCanceled) {
Logger?.LogWarning("Connection to the remote device has been cancelled.");
_Client.Dispose();
_Client = null;
throw new OperationCanceledException();
}

// Check for timeout:
if (timeoutTask.IsCompleted) {
Logger?.LogError($"Connection to the remote device {Host}:{Port} timed out.");
_Client.Dispose();
_Client = null;
throw new TimeoutException($"Connection to the remote device {Host}:{Port} timed out.");
}

Logger?.LogInformation("Connection succeeded.");
}

/// <summary>
/// Closes active connection.
/// </summary>
public void Close()
{
Logger?.LogInformation("Closing the TCP connection.");
_Client?.Dispose();
_Client = null;
}
Expand Down Expand Up @@ -163,6 +182,7 @@ public async Task ClearBuffers(CancellationToken cancellationToken = default)
throw new InvalidOperationException("Cannot clear buffers, the connection is not open.");
}

Logger?.LogDebug("Clearing input buffer.");
NetworkStream stream = _Client.GetStream();
while (stream.DataAvailable) {
await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
Expand Down
23 changes: 19 additions & 4 deletions ScpiNet/UsbScpiConnection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Win32.SafeHandles;
using Microsoft.Extensions.Logging;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -365,6 +366,11 @@ private struct SP_DEVICE_INTERFACE_DETAIL_DATA
/// </summary>
public bool IsOpen => DevHandle?.IsInvalid == false && !DevHandle.IsClosed;

/// <summary>
/// Logger instance.
/// </summary>
protected ILogger<UsbScpiConnection> Logger { get; }

/// <summary>
/// Gets the list of connected USB devices.
/// </summary>
Expand Down Expand Up @@ -428,11 +434,13 @@ public static List<string> GetUsbDeviceList()
/// Creates an instance of UsbTmc device driver and opens the device..
/// </summary>
/// <param name="devPath">Device path to open.</param>
public UsbScpiConnection(string devPath)
/// <param name="logger">Instance of a logger abstraction.</param>
public UsbScpiConnection(string devPath, ILogger<UsbScpiConnection> logger = null)
{
DevicePath = devPath;
DevHandle = null;
Tag = 1;
Logger = logger;
}

/// <summary>
Expand All @@ -445,6 +453,7 @@ public Task Open(CancellationToken cancellationToken = default)
Close();

// Open the device for exclusive asynchronous access:
Logger?.LogInformation($"Opening USB TMC device '{DevicePath}'...");
DevHandle = CreateFile(
DevicePath,
EFileAccess.GenericRead | EFileAccess.GenericWrite,
Expand All @@ -466,6 +475,7 @@ public Task Open(CancellationToken cancellationToken = default)
// it breaks communication with Keysight multimeter.
//ResetPipe();

Logger?.LogInformation("USB TMC connection succeeded.");
return Task.CompletedTask;
}

Expand All @@ -474,8 +484,11 @@ public Task Open(CancellationToken cancellationToken = default)
/// </summary>
public void Close()
{
DevHandle?.Close();
DevHandle = null;
if (DevHandle != null) {
Logger?.LogInformation("Closing USB TMC device.");
DevHandle.Close();
DevHandle = null;
}
}

/// <summary>
Expand All @@ -490,6 +503,7 @@ public async Task ClearBuffers(CancellationToken cancellationToken = default)

// Read all data from the device until it is empty. The other way is to use the ResetPipe() method,
// but it does not work with all TMC devices.
Logger?.LogDebug("Clearing input buffer.");
ReadResult result;
byte[] buffer = new byte[1024];
do {
Expand All @@ -504,6 +518,7 @@ public void ResetPipe()
{
byte[] pipeType = new byte[4] { (byte)UsbTmcPipeType.AllPipes, 0, 0, 0 };

Logger?.LogDebug("Reset pipe.");
bool ret = DeviceIoControl(DevHandle, IoctlUsbTmc.ResetPipe, pipeType, pipeType.Length, IntPtr.Zero, 0, out int _, IntPtr.Zero);
if (!ret) {
throw new Exception(string.Format("ResetPipe() failed. Last error: {0}.", Marshal.GetLastWin32Error()));
Expand Down

0 comments on commit e0ebd37

Please sign in to comment.