Implement ASP.NET Core Web API server for SharpCAT with REST endpoints

Co-authored-by: ekinnee <1707617+ekinnee@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-08-06 23:26:53 +00:00
parent ce86879a3b
commit c20d3a12bb
13 changed files with 1055 additions and 5 deletions

View file

@ -1,10 +1,31 @@
# This like many, are ideas. There may or may not be progress as inspiration and time allows. # This like many, are ideas. There may or may not be progress as inspiration and time allows.
# SharpCAT # SharpCAT
C#, .NET Standard based CAT control library. C#, .NET Standard based CAT control library with ASP.NET Core Web API server.
I am targeting .Net Standard so that the assembly may be used with .Net Core or the .Net framework. I am targeting .Net Standard so that the assembly may be used with .Net Core or the .Net framework.
## Components
### SharpCAT Library
The core .NET Standard 2.0 library for CAT (Computer Aided Transceiver) control.
### SharpCAT Server
A cross-platform ASP.NET Core Web API server that provides REST endpoints for CAT control operations. Located in `Server/SharpCAT.Server/`.
**Key Features:**
- REST API for serial port management and CAT commands
- Cross-platform support (Windows, Linux, macOS)
- Swagger/OpenAPI documentation
- Built-in error handling and logging
**Quick Start:**
```bash
cd Server/SharpCAT.Server
dotnet run
```
Then visit `http://localhost:5188` for the Swagger UI.
## Development Setup ## Development Setup
### Prerequisites ### Prerequisites
@ -16,19 +37,30 @@ I am targeting .Net Standard so that the assembly may be used with .Net Core or
This project is configured for development in Visual Studio Code with the .NET CLI. This project is configured for development in Visual Studio Code with the .NET CLI.
**To build the project:** **To build the entire solution:**
```bash ```bash
dotnet build SharpCAT/SharpCATLib.csproj dotnet build
```
**To build just the library:**
```bash
dotnet build SharpCAT/SharpCAT.csproj
```
**To build and run the Web API server:**
```bash
cd Server/SharpCAT.Server
dotnet run
``` ```
**To clean the project:** **To clean the project:**
```bash ```bash
dotnet clean SharpCAT/SharpCATLib.csproj dotnet clean
``` ```
**To restore packages:** **To restore packages:**
```bash ```bash
dotnet restore SharpCAT/SharpCATLib.csproj dotnet restore
``` ```
**VS Code Tasks:** **VS Code Tasks:**

View file

@ -0,0 +1,292 @@
using Microsoft.AspNetCore.Mvc;
using SharpCAT.Server.Models;
using SharpCAT.Server.Services;
namespace SharpCAT.Server.Controllers
{
/// <summary>
/// Controller for CAT (Computer Aided Transceiver) operations
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class CatController : ControllerBase
{
private readonly ISerialCommunicationService _serialService;
private readonly ILogger<CatController> _logger;
public CatController(ISerialCommunicationService serialService, ILogger<CatController> logger)
{
_serialService = serialService ?? throw new ArgumentNullException(nameof(serialService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Gets all available serial ports on the system
/// </summary>
/// <returns>List of available serial ports</returns>
/// <response code="200">Returns the list of available serial ports</response>
[HttpGet("ports")]
[ProducesResponseType(typeof(PortListResponse), StatusCodes.Status200OK)]
public ActionResult<PortListResponse> GetPorts()
{
try
{
_logger.LogInformation("Getting available serial ports");
var ports = _serialService.GetAvailablePorts();
var response = new PortListResponse
{
Ports = ports
};
_logger.LogInformation("Found {PortCount} available ports", response.Count);
return Ok(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting available ports");
return StatusCode(500, new ErrorResponse
{
Error = "Internal server error while getting ports",
Details = ex.Message
});
}
}
/// <summary>
/// Opens and configures a serial port for communication
/// </summary>
/// <param name="request">Port configuration parameters</param>
/// <returns>Result of the port open operation</returns>
/// <response code="200">Port opened successfully</response>
/// <response code="400">Invalid request parameters</response>
/// <response code="500">Internal server error</response>
[HttpPost("open")]
[ProducesResponseType(typeof(PortOperationResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<PortOperationResponse>> OpenPort([FromBody] OpenPortRequest request)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(new ErrorResponse
{
Error = "Invalid request parameters",
Details = string.Join("; ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage))
});
}
_logger.LogInformation("Attempting to open port {PortName} with baud rate {BaudRate}",
request.PortName, request.BaudRate);
var success = await _serialService.OpenPortAsync(
request.PortName,
request.BaudRate,
request.Parity,
request.StopBits,
request.Handshake);
var response = new PortOperationResponse
{
Success = success,
PortName = request.PortName,
IsOpen = _serialService.IsPortOpen(),
Message = success
? $"Port {request.PortName} opened successfully"
: $"Failed to open port {request.PortName}"
};
if (success)
{
_logger.LogInformation("Successfully opened port {PortName}", request.PortName);
return Ok(response);
}
else
{
_logger.LogWarning("Failed to open port {PortName}", request.PortName);
return StatusCode(500, new ErrorResponse
{
Error = response.Message
});
}
}
catch (ArgumentException ex)
{
_logger.LogWarning(ex, "Invalid argument for opening port: {PortName}", request.PortName);
return BadRequest(new ErrorResponse
{
Error = "Invalid port parameters",
Details = ex.Message
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error opening port: {PortName}", request.PortName);
return StatusCode(500, new ErrorResponse
{
Error = "Internal server error while opening port",
Details = ex.Message
});
}
}
/// <summary>
/// Closes the currently opened serial port
/// </summary>
/// <returns>Result of the port close operation</returns>
/// <response code="200">Port closed successfully</response>
/// <response code="500">Internal server error</response>
[HttpPost("close")]
[ProducesResponseType(typeof(PortOperationResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<PortOperationResponse>> ClosePort()
{
try
{
var currentPortName = _serialService.GetCurrentPortName();
_logger.LogInformation("Attempting to close port {PortName}", currentPortName ?? "unknown");
await _serialService.ClosePortAsync();
var response = new PortOperationResponse
{
Success = true,
PortName = currentPortName,
IsOpen = _serialService.IsPortOpen(),
Message = "Port closed successfully"
};
_logger.LogInformation("Successfully closed port");
return Ok(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error closing port");
return StatusCode(500, new ErrorResponse
{
Error = "Internal server error while closing port",
Details = ex.Message
});
}
}
/// <summary>
/// Sends a CAT command to the connected radio
/// </summary>
/// <param name="request">CAT command to send</param>
/// <returns>Result of the command operation including any response from the radio</returns>
/// <response code="200">Command sent successfully</response>
/// <response code="400">Invalid request parameters or no port open</response>
/// <response code="500">Internal server error</response>
[HttpPost("command")]
[ProducesResponseType(typeof(CommandResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<CommandResponse>> SendCommand([FromBody] SendCommandRequest request)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(new ErrorResponse
{
Error = "Invalid request parameters",
Details = string.Join("; ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage))
});
}
if (!_serialService.IsPortOpen())
{
return BadRequest(new ErrorResponse
{
Error = "No serial port is currently open",
Details = "You must open a serial port before sending commands"
});
}
_logger.LogInformation("Sending CAT command: {Command}", request.Command);
var radioResponse = await _serialService.SendCommandAsync(request.Command);
var response = new CommandResponse
{
Success = true,
Command = request.Command,
Response = radioResponse,
Message = "Command sent successfully",
Timestamp = DateTime.UtcNow
};
_logger.LogInformation("CAT command sent successfully. Response: {Response}", radioResponse);
return Ok(response);
}
catch (ArgumentException ex)
{
_logger.LogWarning(ex, "Invalid command: {Command}", request.Command);
return BadRequest(new ErrorResponse
{
Error = "Invalid command",
Details = ex.Message
});
}
catch (InvalidOperationException ex)
{
_logger.LogWarning(ex, "Cannot send command - port not open");
return BadRequest(new ErrorResponse
{
Error = "Cannot send command",
Details = ex.Message
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending CAT command: {Command}", request.Command);
return StatusCode(500, new ErrorResponse
{
Error = "Internal server error while sending command",
Details = ex.Message
});
}
}
/// <summary>
/// Gets the current status of the serial port connection
/// </summary>
/// <returns>Current port status</returns>
/// <response code="200">Returns current port status</response>
[HttpGet("status")]
[ProducesResponseType(typeof(PortOperationResponse), StatusCodes.Status200OK)]
public ActionResult<PortOperationResponse> GetStatus()
{
try
{
var isOpen = _serialService.IsPortOpen();
var currentPort = _serialService.GetCurrentPortName();
var response = new PortOperationResponse
{
Success = true,
PortName = currentPort,
IsOpen = isOpen,
Message = isOpen
? $"Port {currentPort} is open"
: "No port is currently open"
};
return Ok(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting port status");
return StatusCode(500, new ErrorResponse
{
Error = "Internal server error while getting status",
Details = ex.Message
});
}
}
}
}

View file

@ -0,0 +1,143 @@
using System.ComponentModel.DataAnnotations;
using System.IO.Ports;
namespace SharpCAT.Server.Models
{
/// <summary>
/// Response model for listing available serial ports
/// </summary>
public class PortListResponse
{
/// <summary>
/// Array of available serial port names
/// </summary>
public string[] Ports { get; set; } = Array.Empty<string>();
/// <summary>
/// Number of available ports
/// </summary>
public int Count => Ports.Length;
}
/// <summary>
/// Request model for opening a serial port
/// </summary>
public class OpenPortRequest
{
/// <summary>
/// Name of the serial port to open (e.g., "COM1", "/dev/ttyUSB0")
/// </summary>
[Required]
public string PortName { get; set; } = string.Empty;
/// <summary>
/// Baud rate for communication (default: 9600)
/// </summary>
public int BaudRate { get; set; } = 9600;
/// <summary>
/// Parity setting (default: None)
/// </summary>
public Parity Parity { get; set; } = Parity.None;
/// <summary>
/// Stop bits setting (default: One)
/// </summary>
public StopBits StopBits { get; set; } = StopBits.One;
/// <summary>
/// Handshake setting (default: None)
/// </summary>
public Handshake Handshake { get; set; } = Handshake.None;
}
/// <summary>
/// Response model for port operations
/// </summary>
public class PortOperationResponse
{
/// <summary>
/// Indicates if the operation was successful
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Human-readable message about the operation
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// Name of the port that was operated on
/// </summary>
public string? PortName { get; set; }
/// <summary>
/// Current status of the port (open/closed)
/// </summary>
public bool IsOpen { get; set; }
}
/// <summary>
/// Request model for sending CAT commands
/// </summary>
public class SendCommandRequest
{
/// <summary>
/// CAT command string to send to the radio
/// </summary>
[Required]
public string Command { get; set; } = string.Empty;
}
/// <summary>
/// Response model for CAT command operations
/// </summary>
public class CommandResponse
{
/// <summary>
/// Indicates if the command was sent successfully
/// </summary>
public bool Success { get; set; }
/// <summary>
/// The command that was sent
/// </summary>
public string Command { get; set; } = string.Empty;
/// <summary>
/// Response received from the radio (if any)
/// </summary>
public string? Response { get; set; }
/// <summary>
/// Human-readable message about the operation
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// Timestamp when the command was executed
/// </summary>
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
/// <summary>
/// Response model for errors
/// </summary>
public class ErrorResponse
{
/// <summary>
/// Error message
/// </summary>
public string Error { get; set; } = string.Empty;
/// <summary>
/// Additional details about the error
/// </summary>
public string? Details { get; set; }
/// <summary>
/// Timestamp when the error occurred
/// </summary>
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
}

View file

@ -0,0 +1,67 @@
using SharpCAT.Server.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "SharpCAT Server API",
Version = "v1",
Description = "ASP.NET Core Web API for CAT (Computer Aided Transceiver) control using SharpCAT library",
Contact = new Microsoft.OpenApi.Models.OpenApiContact
{
Name = "SharpCAT Project"
}
});
// Include XML comments if available
var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFilename);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
});
// Register the serial communication service as a singleton
// to maintain connection state across requests
builder.Services.AddSingleton<ISerialCommunicationService, SerialCommunicationService>();
// Add CORS policy for cross-origin requests
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "SharpCAT Server API v1");
c.RoutePrefix = string.Empty; // Set Swagger UI at app's root
});
}
app.UseCors("AllowAll");
app.UseRouting();
app.MapControllers();
// Add a simple health check endpoint
app.MapGet("/health", () => new { status = "healthy", timestamp = DateTime.UtcNow })
.WithName("HealthCheck")
.WithOpenApi();
app.Run();

View file

@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:14425",
"sslPort": 0
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5188",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View file

@ -0,0 +1,150 @@
# SharpCAT Server
A cross-platform ASP.NET Core Web API server that provides REST endpoints for CAT (Computer Aided Transceiver) control using the SharpCAT library.
## Features
- **Cross-platform**: Works on Windows, Linux, and macOS
- **REST API**: Clean, documented REST endpoints for radio control
- **Serial Communication**: Leverages the SharpCAT core library for serial port operations
- **Swagger Documentation**: Built-in API documentation and testing interface
- **Error Handling**: Comprehensive error handling and logging
## API Endpoints
### Serial Port Management
- `GET /api/cat/ports` - List all available serial ports
- `POST /api/cat/open` - Open and configure a serial port
- `POST /api/cat/close` - Close the currently opened port
- `GET /api/cat/status` - Get current port connection status
### CAT Commands
- `POST /api/cat/command` - Send arbitrary CAT commands to the radio
### Health Check
- `GET /health` - Simple health check endpoint
## Quick Start
### Prerequisites
- [.NET 8.0 SDK](https://dotnet.microsoft.com/download) or later
### Running the Server
1. Navigate to the server directory:
```bash
cd Server/SharpCAT.Server
```
2. Build and run the server:
```bash
dotnet run
```
3. The server will start and listen on `http://localhost:5188` by default
4. Open your browser to `http://localhost:5188` to access the Swagger UI for interactive API documentation
### Example Usage
#### List Available Ports
```bash
curl http://localhost:5188/api/cat/ports
```
#### Open a Serial Port
```bash
curl -X POST http://localhost:5188/api/cat/open \
-H "Content-Type: application/json" \
-d '{
"portName": "COM3",
"baudRate": 9600,
"parity": "None",
"stopBits": "One",
"handshake": "None"
}'
```
#### Send a CAT Command
```bash
curl -X POST http://localhost:5188/api/cat/command \
-H "Content-Type: application/json" \
-d '{"command": "FA;"}'
```
#### Check Status
```bash
curl http://localhost:5188/api/cat/status
```
## Configuration
### Port Configuration Options
When opening a port, you can configure:
- **Port Name**: Serial port name (e.g., "COM1" on Windows, "/dev/ttyUSB0" on Linux)
- **Baud Rate**: Communication speed (default: 9600)
- **Parity**: Error checking method (None, Odd, Even, Mark, Space)
- **Stop Bits**: Number of stop bits (None, One, Two, OnePointFive)
- **Handshake**: Flow control method (None, XOnXOff, RequestToSend, RequestToSendXOnXOff)
### Logging
The server uses ASP.NET Core's built-in logging. Log levels can be configured in `appsettings.json`:
```json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"SharpCAT.Server": "Debug"
}
}
}
```
## Development
### Building from Source
1. Ensure you have the .NET 8.0 SDK installed
2. Clone the repository
3. Navigate to the project root
4. Build the solution:
```bash
dotnet build
```
### Project Structure
```
Server/SharpCAT.Server/
├── Controllers/
│ └── CatController.cs # Main API controller
├── Models/
│ └── ApiModels.cs # Request/response models
├── Services/
│ ├── ISerialCommunicationService.cs # Service interface
│ └── SerialCommunicationService.cs # Service implementation
├── Program.cs # Application entry point
└── SharpCAT.Server.csproj # Project file
```
### Dependencies
- **Microsoft.AspNetCore.OpenApi** - OpenAPI support
- **Swashbuckle.AspNetCore** - Swagger documentation
- **SharpCAT** - Core CAT control library
## License
This project follows the same license as the parent SharpCAT project.
## Contributing
Contributions are welcome! Please follow the existing code style and add appropriate tests for new features.

View file

@ -0,0 +1,51 @@
using System.IO.Ports;
namespace SharpCAT.Server.Services
{
/// <summary>
/// Interface for serial communication services
/// </summary>
public interface ISerialCommunicationService
{
/// <summary>
/// Gets all available serial ports on the system
/// </summary>
/// <returns>Array of port names</returns>
string[] GetAvailablePorts();
/// <summary>
/// Opens and configures a serial port
/// </summary>
/// <param name="portName">Name of the port to open</param>
/// <param name="baudRate">Baud rate for communication</param>
/// <param name="parity">Parity setting</param>
/// <param name="stopBits">Stop bits setting</param>
/// <param name="handshake">Handshake setting</param>
/// <returns>True if port opened successfully</returns>
Task<bool> OpenPortAsync(string portName, int baudRate, Parity parity, StopBits stopBits, Handshake handshake);
/// <summary>
/// Closes the currently opened port
/// </summary>
Task ClosePortAsync();
/// <summary>
/// Sends a CAT command to the radio
/// </summary>
/// <param name="command">CAT command string to send</param>
/// <returns>Response from the radio, if any</returns>
Task<string> SendCommandAsync(string command);
/// <summary>
/// Gets the status of the current port connection
/// </summary>
/// <returns>True if port is open and connected</returns>
bool IsPortOpen();
/// <summary>
/// Gets the name of the currently opened port
/// </summary>
/// <returns>Port name or null if no port is open</returns>
string? GetCurrentPortName();
}
}

View file

@ -0,0 +1,208 @@
using System.IO.Ports;
using System.Text;
namespace SharpCAT.Server.Services
{
/// <summary>
/// Service for managing serial communication with radios using SharpCAT library
/// </summary>
public class SerialCommunicationService : ISerialCommunicationService, IDisposable
{
private readonly ILogger<SerialCommunicationService> _logger;
private Serial? _serialConnection;
private SerialPort? _directSerialPort;
private string? _currentPortName;
private readonly SemaphoreSlim _semaphore = new(1, 1);
private bool _disposed = false;
public SerialCommunicationService(ILogger<SerialCommunicationService> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <inheritdoc />
public string[] GetAvailablePorts()
{
try
{
var sharpCat = new SharpCAT();
var ports = sharpCat.PortNames;
_logger.LogInformation("Found {PortCount} available serial ports", ports?.Length ?? 0);
return ports ?? Array.Empty<string>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting available serial ports");
return Array.Empty<string>();
}
}
/// <inheritdoc />
public async Task<bool> OpenPortAsync(string portName, int baudRate, Parity parity, StopBits stopBits, Handshake handshake)
{
if (string.IsNullOrWhiteSpace(portName))
throw new ArgumentException("Port name cannot be null or empty", nameof(portName));
await _semaphore.WaitAsync();
try
{
// Close existing connection if any
await ClosePortInternalAsync();
_logger.LogInformation("Attempting to open port {PortName} with baud rate {BaudRate}", portName, baudRate);
// Create direct SerialPort for more control over communication
_directSerialPort = new SerialPort
{
PortName = portName,
BaudRate = baudRate,
Parity = parity,
StopBits = stopBits,
Handshake = handshake,
DataBits = 8,
ReadTimeout = 5000,
WriteTimeout = 5000,
Encoding = Encoding.ASCII
};
_directSerialPort.Open();
_currentPortName = portName;
_logger.LogInformation("Successfully opened port {PortName}", portName);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to open port {PortName}", portName);
_directSerialPort?.Dispose();
_directSerialPort = null;
_currentPortName = null;
return false;
}
finally
{
_semaphore.Release();
}
}
/// <inheritdoc />
public async Task ClosePortAsync()
{
await _semaphore.WaitAsync();
try
{
await ClosePortInternalAsync();
}
finally
{
_semaphore.Release();
}
}
/// <inheritdoc />
public async Task<string> SendCommandAsync(string command)
{
if (string.IsNullOrWhiteSpace(command))
throw new ArgumentException("Command cannot be null or empty", nameof(command));
await _semaphore.WaitAsync();
try
{
if (_directSerialPort == null || !_directSerialPort.IsOpen)
{
throw new InvalidOperationException("No serial port is currently open");
}
_logger.LogDebug("Sending command: {Command}", command);
// Send the command
await Task.Run(() => _directSerialPort.WriteLine(command));
// Wait a bit for response
await Task.Delay(100);
// Try to read response
string response = "";
if (_directSerialPort.BytesToRead > 0)
{
response = await Task.Run(() =>
{
try
{
return _directSerialPort.ReadExisting();
}
catch (TimeoutException)
{
return "";
}
});
}
_logger.LogDebug("Command response: {Response}", response);
return response.Trim();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending command: {Command}", command);
throw;
}
finally
{
_semaphore.Release();
}
}
/// <inheritdoc />
public bool IsPortOpen()
{
return _directSerialPort?.IsOpen == true;
}
/// <inheritdoc />
public string? GetCurrentPortName()
{
return _currentPortName;
}
private async Task ClosePortInternalAsync()
{
if (_directSerialPort != null)
{
try
{
if (_directSerialPort.IsOpen)
{
await Task.Run(() => _directSerialPort.Close());
}
_directSerialPort.Dispose();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error closing serial port");
}
finally
{
_directSerialPort = null;
_currentPortName = null;
}
}
if (_serialConnection != null)
{
_serialConnection = null;
}
_logger.LogInformation("Serial port closed");
}
public void Dispose()
{
if (!_disposed)
{
ClosePortInternalAsync().Wait();
_semaphore?.Dispose();
_disposed = true;
}
}
}
}

View file

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.18" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SharpCAT\SharpCAT.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,6 @@
@SharpCAT.Server_HostAddress = http://localhost:5188
GET {{SharpCAT.Server_HostAddress}}/weatherforecast/
Accept: application/json
###

View file

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View file

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

33
SharpCAT.sln Normal file
View file

@ -0,0 +1,33 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCAT", "SharpCAT\SharpCAT.csproj", "{3EA807EF-B181-4C54-8502-0A2A3EACE984}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{43CFF66C-84E6-4EC2-AE4F-005FB80D74D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCAT.Server", "Server\SharpCAT.Server\SharpCAT.Server.csproj", "{22E4F655-252E-42DB-ADD5-494BC825A97C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EA807EF-B181-4C54-8502-0A2A3EACE984}.Release|Any CPU.Build.0 = Release|Any CPU
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22E4F655-252E-42DB-ADD5-494BC825A97C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{22E4F655-252E-42DB-ADD5-494BC825A97C} = {43CFF66C-84E6-4EC2-AE4F-005FB80D74D5}
EndGlobalSection
EndGlobal