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.
# 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.
## 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
### 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.
**To build the project:**
**To build the entire solution:**
```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:**
```bash
dotnet clean SharpCAT/SharpCATLib.csproj
dotnet clean
```
**To restore packages:**
```bash
dotnet restore SharpCAT/SharpCATLib.csproj
dotnet restore
```
**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