Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/http error codes #9

Merged
merged 5 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions SoftPlc/Exceptions/DbAccessException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
namespace SoftPlc.Exceptions;

public abstract class DbAccessException : Exception
{
protected DbAccessException(string message, Exception? innerException = null) : base(message, innerException)
{
}

public abstract int StatusCode { get; }
public abstract string Title { get; }
}

public class InvalidDbSizeException : DbAccessException
{
public InvalidDbSizeException(int size) : base($"DB size must be > 0 but was {size}.")
{
Size = size;
}

public int Size { get; }

public override int StatusCode => StatusCodes.Status400BadRequest;
public override string Title => "Invalid DB size";

public static void ThrowIfInvalid(int size)
{
if (size < 1)
throw new InvalidDbSizeException(size);
}
}

public class DbOutOfRangeException : DbAccessException
{
public DbOutOfRangeException(string message, int dbNo, Exception? innerException = null) : base(message, innerException)
{
DbNo = dbNo;
}

public DbOutOfRangeException(int dbNo) : base($"DB number must be positive, but is {dbNo}.")
{
DbNo = dbNo;
}

public int DbNo { get; }

public override int StatusCode => StatusCodes.Status400BadRequest;
public override string Title => "DB out of range";

public static void ThrowIfInvalid(int dbNo)
{
if (dbNo < 1)
throw new DbOutOfRangeException(dbNo);
}
}

public class DbNotFoundException : DbAccessException
{
public DbNotFoundException(int dbNo) : base($"DB {dbNo} not found.")
{
DbNo = dbNo;
}

public DbNotFoundException(string message, int dbNo, Exception? innerException = null) : base(message, innerException)
{
DbNo = dbNo;
}

public int DbNo { get; }

public override int StatusCode => StatusCodes.Status404NotFound;
public override string Title => "DB not found";
}

public class DbExistsException : DbAccessException
{
public DbExistsException(int dbNo) : base($"DB {dbNo} already exists.")
{
DbNo = dbNo;
}

public int DbNo { get; }

public override int StatusCode => StatusCodes.Status409Conflict;
public override string Title => "DB already exists";
}

public class DateExceedsDbLengthException : DbAccessException
{
public DateExceedsDbLengthException(int dbNo, int dbLength, int dataLength) : base($"Data with {dataLength} bytes exceeds length of DB {dbNo} of {dbLength} bytes.")
{
DbNo = dbNo;
DBLength = dbLength;
DataLength = dataLength;
}

public int DataLength { get; }
public int DBLength { get; }
public int DbNo { get; }

public override int StatusCode => StatusCodes.Status400BadRequest;
public override string Title => "Data exceeds DB length";
}
29 changes: 29 additions & 0 deletions SoftPlc/Exceptions/DbAccessExceptionHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace SoftPlc.Exceptions;

public class DbAccessExceptionHandler : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
if (exception is not DbAccessException dbAccessException)
return false;

var problemDetails =
new ProblemDetails
{
Status = dbAccessException.StatusCode,
Title = dbAccessException.Title,
Detail = dbAccessException.Message
};

httpContext.Response.StatusCode = problemDetails.Status.Value;

await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken);
return true;
}
}
5 changes: 5 additions & 0 deletions SoftPlc/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.OpenApi.Models;
using SoftPlc.Exceptions;
using SoftPlc.Interfaces;
using SoftPlc.Services;

Expand Down Expand Up @@ -28,6 +29,9 @@
}
});
});
builder.Services.AddExceptionHandler<DbAccessExceptionHandler>();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseSwagger();
Expand All @@ -44,6 +48,7 @@

app.UseAuthorization();

app.UseExceptionHandler();
app.MapControllers();

var plcService = app.Services.GetService<IPlcService>();
Expand Down
44 changes: 29 additions & 15 deletions SoftPlc/Services/PlcService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.Configuration;
using System.IO;
using Newtonsoft.Json;
using SoftPlc.Exceptions;

namespace SoftPlc.Services
{
Expand Down Expand Up @@ -116,10 +117,12 @@ public IEnumerable<DatablockDescription> GetDatablocksInfo()
public DatablockDescription GetDatablock(int id)
{
CheckServerRunning();
DatablockDescription db;
var found = datablocks.TryGetValue(id, out db);
if (found) return db;
throw new InvalidOperationException("Datablock not found");
DbOutOfRangeException.ThrowIfInvalid(id);

if (datablocks.TryGetValue(id, out var db))
return db;
else
throw new DbNotFoundException(id);
}

private void AddDatablock(int id, DatablockDescription datablock)
Expand All @@ -131,26 +134,37 @@ private void AddDatablock(int id, DatablockDescription datablock)
public void AddDatablock(int id, int size)
{
CheckServerRunning();
if (id < 1) throw new ArgumentException("Invalid id for datablock - id must be > 1", nameof(id));
if (size < 1) throw new ArgumentException("Invalid size for datablock - size must be > 1", nameof(size));
if (datablocks.ContainsKey(id)) throw new InvalidOperationException($"A Datablock with id = {id} already exists");
DbOutOfRangeException.ThrowIfInvalid(id);
InvalidDbSizeException.ThrowIfInvalid(size);

var db = new DatablockDescription(id, size);
while(!datablocks.TryAdd(id, db)){ }
if (!datablocks.TryAdd(id, db))
throw new DbExistsException(id);

server.RegisterArea(S7Server.srvAreaDB, id, ref datablocks[id].Data, datablocks[id].Data.Length);
}

public void UpdateDatablockData(int id, byte[] data)
{
if (data != null && data.Length > datablocks[id].Data.Length) throw new ArgumentException("Too much data as expected", nameof(data));
if(data != null)
Array.Copy(data, datablocks[id].Data, data.Length);
DbOutOfRangeException.ThrowIfInvalid(id);

if (!datablocks.TryGetValue(id, out var db))
throw new DbNotFoundException(id);

if (data.Length > db.Data.Length)
throw new DateExceedsDbLengthException(id, db.Data.Length, data.Length);

Array.Copy(data, datablocks[id].Data, data.Length);
}

public void RemoveDatablock(int id)
{
server.UnregisterArea(S7Server.srvAreaDB, id);
DatablockDescription datablock;
while (!datablocks.TryRemove(id, out datablock)) { }
}
DbOutOfRangeException.ThrowIfInvalid(id);

if (datablocks.TryRemove(id, out _))
server.UnregisterArea(S7Server.srvAreaDB, id);
else
throw new DbNotFoundException(id);
}
}
}
Loading