diff --git a/fgs.api/Controllers/LogController.cs b/fgs.api/Controllers/LogController.cs new file mode 100644 index 0000000..e9ecb9a --- /dev/null +++ b/fgs.api/Controllers/LogController.cs @@ -0,0 +1,32 @@ +using System.Text.Json.Serialization; +using fgs.api.models; +using fgs.database.Context; +using fgs.database.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace fgs.api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class LogController(FilamentguardContext dbContext) : ControllerBase + { + [HttpPost] + [Route("create")] + public void Logging(LogRequest logRequest) + { + Log log = new Log(); + log.Category = logRequest.Category; + log.Data = logRequest.Data; + log.DeviceId = logRequest.deviceId; + log.FirmwareVersion = logRequest.FirmwareVersion; + log.Hostname = logRequest.Hostname; + log.Level = logRequest.Level; + log.Message = logRequest.Message; + log.UptimeMilliseconds = (long)logRequest.UptimeMilliseconds; + log.TagUid = logRequest.tagUid; + dbContext.Logs.Add(log); + dbContext.SaveChanges(); + } + } +} diff --git a/fgs.api/Controllers/TagController.cs b/fgs.api/Controllers/TagController.cs new file mode 100644 index 0000000..30105e5 --- /dev/null +++ b/fgs.api/Controllers/TagController.cs @@ -0,0 +1,76 @@ +using fgs.api.models; +using fgs.database.Context; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Tag = fgs.database.Models.Tag; + +namespace fgs.api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TagController(FilamentguardContext dbContext) : ControllerBase + { + [HttpPost] + [Route("detected")] + public IActionResult Detected([FromBody] OpenPrintTag openPrintTag) + { + Tag? currentTag = dbContext.Tags + .SingleOrDefault(c => c.DeviceId.Equals(openPrintTag.device.deviceId) && c.TagUid.Equals(openPrintTag.tag.tagUid)); + + if (currentTag == null) + { + currentTag = new Tag(); + dbContext.Tags.Add(currentTag); + } + currentTag.DeviceId = openPrintTag.device?.deviceId; + currentTag.HostName = openPrintTag.device?.hostname; + currentTag.TagUid = openPrintTag.tag.tagUid; + currentTag.InstanceUid = openPrintTag.tag.instanceUuid; + currentTag.MaterialName = openPrintTag.tag.materialName; + currentTag.MaterialType = openPrintTag.tag.materialType; + currentTag.Brand = openPrintTag.tag.brand; + currentTag.ColorHex = openPrintTag.tag.colorHex; + currentTag.ActualWeightGrams = openPrintTag.tag.actualWeightGrams; + currentTag.NominalWeightGrams = openPrintTag.tag.nominalWeightGrams; + currentTag.SpoolWeightGrams = openPrintTag.tag.spoolWeightGrams; + currentTag.ConsumedWeightGrams = openPrintTag.tag.consumedWeightGrams; + currentTag.RemainingWeightGrams = openPrintTag.tag.remainingWeightGrams; + currentTag.HasActualWeight = openPrintTag.tag.hasActualWeight; + currentTag.HasNominalWeight = openPrintTag.tag.hasNominalWeight; + currentTag.HasSpoolWeight = openPrintTag.tag.hasSpoolWeight; + currentTag.HasConsumedWeight = openPrintTag.tag.hasConsumedWeight; + currentTag.IsActive = true; + + dbContext.SaveChanges(); + return Ok(); + } + + [HttpPost] + [Route("removed")] + public IActionResult Removed([FromBody] OpenPrintTagRemoved openPrintTagRemoved) + { + Tag? currentTag = dbContext.Tags + .SingleOrDefault(c => c.DeviceId.Equals(openPrintTagRemoved.device.deviceId) && c.TagUid.Equals(openPrintTagRemoved.tag.tagUid)); + if (currentTag != null) + { + currentTag.IsActive = false; + dbContext.SaveChanges(); + return Ok(new + { + message = "Tag removal received, tag removed", + tagUid = openPrintTagRemoved.tag.tagUid, + deviceId = openPrintTagRemoved.device?.deviceId + }); + + } + else + { + return Ok(new + { + message = "Tag removal received, but no tag to remove" + }); + } + + } + } +} diff --git a/fgs.api/Program.cs b/fgs.api/Program.cs index 1e5f1b9..c22ed81 100644 --- a/fgs.api/Program.cs +++ b/fgs.api/Program.cs @@ -1,4 +1,9 @@ +using fgs.database.Context; +using fgs.database.Models; +using Microsoft.EntityFrameworkCore; +using Scalar.AspNetCore; + namespace fgs.api { public class Program @@ -13,15 +18,23 @@ namespace fgs.api // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); + builder.Services.AddDbContext(options => + options.UseSqlServer( + builder.Configuration.GetConnectionString("FilamentGuardDatabase"))); + var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.MapOpenApi(); + app.MapScalarApiReference(); } - app.UseHttpsRedirection(); + if (!app.Environment.IsDevelopment()) + { + app.UseHttpsRedirection(); + } app.UseAuthorization(); diff --git a/fgs.api/Properties/launchSettings.json b/fgs.api/Properties/launchSettings.json index fe9f643..5f1801b 100644 --- a/fgs.api/Properties/launchSettings.json +++ b/fgs.api/Properties/launchSettings.json @@ -6,7 +6,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:5011" + "applicationUrl": "http://0.0.0.0:5011" }, "https": { "commandName": "Project", @@ -14,7 +14,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7157;http://localhost:5011" + "applicationUrl": "https://0.0.0.0:7157;http://0.0.0.0:5011" }, "Container (Dockerfile)": { "commandName": "Docker", diff --git a/fgs.api/appsettings.Development.json b/fgs.api/appsettings.Development.json index 0c208ae..e683902 100644 --- a/fgs.api/appsettings.Development.json +++ b/fgs.api/appsettings.Development.json @@ -4,5 +4,9 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "ConnectionStrings": { + "FilamentGuardDatabase": "Server=192.168.3.2;Database=filamentguard;User Id=filamentguard;Password=rlsBN2TJMp5WWGW0lm;TrustServerCertificate=True;" + } } diff --git a/fgs.api/fgs.api.csproj b/fgs.api/fgs.api.csproj index a3d7c6a..9605df3 100644 --- a/fgs.api/fgs.api.csproj +++ b/fgs.api/fgs.api.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -10,7 +10,13 @@ + + + + + + diff --git a/fgs.api/fgs.api.http b/fgs.api/fgs.api.http index 197173d..7469e53 100644 --- a/fgs.api/fgs.api.http +++ b/fgs.api/fgs.api.http @@ -4,3 +4,18 @@ GET {{fgs.api_HostAddress}}/weatherforecast/ Accept: application/json ### + +POST {{fgs.api_HostAddress}}/api/Log/create +Content-Type: application/json +Accept: application/json + +{ + "DeviceId": "ESP32-B4EDF8F57630", + "Hostname": "filamentguard-f57630", + "FirmwareVersion": "1.0.1", + "Level": "Info", + "Category": "NfcReader", + "Message": "OpenPrintTag registreret.", + "Data": "", + "UptimeMilliseconds": 45231 +} \ No newline at end of file diff --git a/fgs.api/models/LogRequest.cs b/fgs.api/models/LogRequest.cs new file mode 100644 index 0000000..32834be --- /dev/null +++ b/fgs.api/models/LogRequest.cs @@ -0,0 +1,23 @@ +namespace fgs.api.models +{ + public class LogRequest + { + + public required string deviceId { get; init; } + public required string tagUid { get; init; } + + public required string Hostname { get; init; } + + public required string FirmwareVersion { get; init; } + + public required string Level { get; init; } + + public required string Category { get; init; } + + public required string Message { get; init; } + + public string? Data { get; init; } + + public ulong UptimeMilliseconds { get; init; } + } +} diff --git a/fgs.api/models/OpenPrintTag.cs b/fgs.api/models/OpenPrintTag.cs new file mode 100644 index 0000000..4e29005 --- /dev/null +++ b/fgs.api/models/OpenPrintTag.cs @@ -0,0 +1,33 @@ +namespace fgs.api.models +{ + public class OpenPrintTag + { + public Device device { get; set; } + public Tag tag { get; set; } + } + + public class Device + { + public string deviceId { get; set; } + public string hostname { get; set; } + } + + public class Tag + { + public string tagUid { get; set; } + public string instanceUuid { get; set; } + public string materialName { get; set; } + public string materialType { get; set; } + public string brand { get; set; } + public string colorHex { get; set; } + public int actualWeightGrams { get; set; } + public int nominalWeightGrams { get; set; } + public int spoolWeightGrams { get; set; } + public int consumedWeightGrams { get; set; } + public int remainingWeightGrams { get; set; } + public bool hasActualWeight { get; set; } + public bool hasNominalWeight { get; set; } + public bool hasSpoolWeight { get; set; } + public bool hasConsumedWeight { get; set; } + } +} diff --git a/fgs.api/models/OpenPrintTagRemoved.cs b/fgs.api/models/OpenPrintTagRemoved.cs new file mode 100644 index 0000000..c7246a4 --- /dev/null +++ b/fgs.api/models/OpenPrintTagRemoved.cs @@ -0,0 +1,19 @@ +namespace fgs.api.models +{ + public class OpenPrintTagRemoved + { + public DeviceRemoved device { get; set; } + public TagRemoved tag { get; set; } + } + + public class DeviceRemoved + { + public string deviceId { get; set; } + public string hostname { get; set; } + } + + public class TagRemoved + { + public string tagUid { get; set; } + } +} diff --git a/fgs.api/models/json.cs b/fgs.api/models/json.cs new file mode 100644 index 0000000..e02abfc --- /dev/null +++ b/fgs.api/models/json.cs @@ -0,0 +1 @@ + diff --git a/fgs.apire/AppHost.cs b/fgs.apire/AppHost.cs index 9c27c2c..aa40912 100644 --- a/fgs.apire/AppHost.cs +++ b/fgs.apire/AppHost.cs @@ -1,7 +1,31 @@ +using System.Net; +using System.Net.Sockets; + var builder = DistributedApplication.CreateBuilder(args); builder.AddProject("fgs-web"); -builder.AddProject("fgs-api"); +string ipAddress = Dns.GetHostEntry(Dns.GetHostName()) + .AddressList + .First(address => + address.AddressFamily == AddressFamily.InterNetwork && + !IPAddress.IsLoopback(address)) + .ToString(); + + +int port = 5051; +builder.AddProject("fgs-api") + .WithEndpoint("http", endpoint => + { + endpoint.Port = port; + endpoint.IsExternal = true; + }) + .WithUrlForEndpoint( + "http", + url => + { + url.Url = "/scalar/v1"; + url.DisplayText = $"http://{ipAddress}:{port}/{url.Url}"; + }); builder.Build().Run(); diff --git a/fgs.apire/fgs.apire.csproj b/fgs.apire/fgs.apire.csproj index 2d37c98..bb0d39f 100644 --- a/fgs.apire/fgs.apire.csproj +++ b/fgs.apire/fgs.apire.csproj @@ -8,6 +8,10 @@ 0879501a-f55f-4876-aa9f-cae6c0e16786 + + + + diff --git a/fgs.database/Class1.cs b/fgs.database/Class1.cs deleted file mode 100644 index b0e6705..0000000 --- a/fgs.database/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace fgs.database -{ - public class Class1 - { - - } -} diff --git a/fgs.database/Context/FilamentguardContext.cs b/fgs.database/Context/FilamentguardContext.cs new file mode 100644 index 0000000..d51dfc6 --- /dev/null +++ b/fgs.database/Context/FilamentguardContext.cs @@ -0,0 +1,27 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using fgs.database.Models; + +namespace fgs.database.Context; + +public partial class FilamentguardContext : DbContext +{ + public FilamentguardContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Logs { get; set; } + + public virtual DbSet Tags { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} \ No newline at end of file diff --git a/fgs.database/Models/Log.cs b/fgs.database/Models/Log.cs new file mode 100644 index 0000000..07f1a95 --- /dev/null +++ b/fgs.database/Models/Log.cs @@ -0,0 +1,50 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace fgs.database.Models; + +[Table("Log")] +public partial class Log +{ + [Key] + public int Id { get; set; } + + [Required] + [Column("deviceId", TypeName = "text")] + public string DeviceId { get; set; } + + [Required] + [Column("tagUid", TypeName = "text")] + public string TagUid { get; set; } + + [Required] + [Column("hostname", TypeName = "text")] + public string Hostname { get; set; } + + [Required] + [Column("firmwareVersion", TypeName = "text")] + public string FirmwareVersion { get; set; } + + [Required] + [Column("level", TypeName = "text")] + public string Level { get; set; } + + [Required] + [Column("category", TypeName = "text")] + public string Category { get; set; } + + [Required] + [Column("message", TypeName = "text")] + public string Message { get; set; } + + [Column("data", TypeName = "text")] + public string Data { get; set; } + + [Column("uptimeMilliseconds")] + public long UptimeMilliseconds { get; set; } +} \ No newline at end of file diff --git a/fgs.database/Models/Tag.cs b/fgs.database/Models/Tag.cs new file mode 100644 index 0000000..c408871 --- /dev/null +++ b/fgs.database/Models/Tag.cs @@ -0,0 +1,55 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace fgs.database.Models; + +[Table("Tag")] +public partial class Tag +{ + [Key] + public int Id { get; set; } + + [Required] + public string DeviceId { get; set; } + + public bool IsActive { get; set; } + + [Required] + public string TagUid { get; set; } + + [Required] + public string HostName { get; set; } + + public string InstanceUid { get; set; } + + public string MaterialName { get; set; } + + public string MaterialType { get; set; } + + public string Brand { get; set; } + + public string ColorHex { get; set; } + + public int? ActualWeightGrams { get; set; } + + public int? NominalWeightGrams { get; set; } + + public int? SpoolWeightGrams { get; set; } + + public int? ConsumedWeightGrams { get; set; } + + public int? RemainingWeightGrams { get; set; } + + public bool HasActualWeight { get; set; } + + public bool HasNominalWeight { get; set; } + + public bool HasSpoolWeight { get; set; } + + public bool HasConsumedWeight { get; set; } +} \ No newline at end of file diff --git a/fgs.database/efpt.config.json b/fgs.database/efpt.config.json new file mode 100644 index 0000000..16c8192 --- /dev/null +++ b/fgs.database/efpt.config.json @@ -0,0 +1,58 @@ +{ + "CodeGenerationMode": 6, + "ContextClassName": "FilamentguardContext", + "ContextNamespace": null, + "FilterSchemas": false, + "IncludeConnectionString": false, + "IrregularWords": null, + "MinimumProductVersion": "2.6.1604", + "ModelNamespace": null, + "OutputContextPath": "Context", + "OutputPath": "Models", + "PluralRules": null, + "PreserveCasingWithRegex": true, + "ProjectRootNamespace": "fgs.database", + "Schemas": null, + "SelectedHandlebarsLanguage": 2, + "SelectedToBeGenerated": 0, + "SingularRules": null, + "T4TemplatePath": null, + "Tables": [ + { + "Name": "[dbo].[Log]", + "ObjectType": 0 + }, + { + "Name": "[dbo].[Tag]", + "ObjectType": 0 + } + ], + "UiHint": null, + "UncountableWords": null, + "UseAsyncStoredProcedureCalls": true, + "UseBoolPropertiesWithoutDefaultSql": false, + "UseDatabaseNames": false, + "UseDatabaseNamesForRoutines": true, + "UseDateOnlyTimeOnly": true, + "UseDbContextSplitting": false, + "UseDecimalDataAnnotationForSprocResult": true, + "UseFluentApiOnly": false, + "UseHandleBars": false, + "UseHierarchyId": false, + "UseInflector": true, + "UseInternalAccessModifiersForSprocsAndFunctions": false, + "UseLegacyPluralizer": false, + "UseManyToManyEntity": false, + "UseNoDefaultConstructor": false, + "UseNoNavigations": false, + "UseNoObjectFilter": false, + "UseNodaTime": false, + "UseNullableReferences": false, + "UsePrefixNavigationNaming": false, + "UseSchemaFolders": false, + "UseSchemaNamespaces": false, + "UseSpatial": false, + "UseT4": false, + "UseT4Split": false, + "UseTypedTvpParameters": true +} \ No newline at end of file diff --git a/fgs.database/fgs.database.csproj b/fgs.database/fgs.database.csproj index b760144..ef09473 100644 --- a/fgs.database/fgs.database.csproj +++ b/fgs.database/fgs.database.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/fgs.web/fgs.web.csproj b/fgs.web/fgs.web.csproj index 1276ca7..77b2ad1 100644 --- a/fgs.web/fgs.web.csproj +++ b/fgs.web/fgs.web.csproj @@ -8,6 +8,7 @@ +