From be24467b33037733175f7b9c0baf3d69f5d881df Mon Sep 17 00:00:00 2001 From: David K Date: Tue, 14 Nov 2023 15:55:00 +0000 Subject: [PATCH 1/4] #7 Captures controller implemented Now turning to linking it to the rest of the program. --- ServoSkull.sln | 11 ++++ skullOS.API/Controllers/CapturesController.cs | 63 +++++++++++++++++++ skullOS.API/Program.cs | 37 +++++++++++ skullOS.API/Properties/launchSettings.json | 41 ++++++++++++ skullOS.API/appsettings.Development.json | 8 +++ skullOS.API/appsettings.json | 9 +++ skullOS.API/skullOS.API.csproj | 18 ++++++ skullOS/Data/CoreSettings.txt | 1 + skullOS/Data/Modules.txt | 2 +- skullOS/Program.cs | 6 +- 10 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 skullOS.API/Controllers/CapturesController.cs create mode 100644 skullOS.API/Program.cs create mode 100644 skullOS.API/Properties/launchSettings.json create mode 100644 skullOS.API/appsettings.Development.json create mode 100644 skullOS.API/appsettings.json create mode 100644 skullOS.API/skullOS.API.csproj create mode 100644 skullOS/Data/CoreSettings.txt diff --git a/ServoSkull.sln b/ServoSkull.sln index bec65e6..581a923 100644 --- a/ServoSkull.sln +++ b/ServoSkull.sln @@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS.Core", "skullOS.Cor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS", "skullOS\skullOS.csproj", "{6D3C5D36-0002-4EFD-A3B9-0924D6D3CCD8}" ProjectSection(ProjectDependencies) = postProject + {5E1EC385-4B61-4E8C-82C7-46695A33AC72} = {5E1EC385-4B61-4E8C-82C7-46695A33AC72} {9AC8960C-539A-4B2C-9D24-5169AF4337FA} = {9AC8960C-539A-4B2C-9D24-5169AF4337FA} EndProjectSection EndProject @@ -49,6 +50,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Pinout.md = Pinout.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skullOS.API", "skullOS.API\skullOS.API.csproj", "{5E1EC385-4B61-4E8C-82C7-46695A33AC72}" + ProjectSection(ProjectDependencies) = postProject + {464B162B-62FC-49CC-A6AB-72DBE959DD8C} = {464B162B-62FC-49CC-A6AB-72DBE959DD8C} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,11 +105,16 @@ Global {E6CD1206-E775-44B6-93C1-CB2DEFB3C89B}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6CD1206-E775-44B6-93C1-CB2DEFB3C89B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6CD1206-E775-44B6-93C1-CB2DEFB3C89B}.Release|Any CPU.Build.0 = Release|Any CPU + {5E1EC385-4B61-4E8C-82C7-46695A33AC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E1EC385-4B61-4E8C-82C7-46695A33AC72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E1EC385-4B61-4E8C-82C7-46695A33AC72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E1EC385-4B61-4E8C-82C7-46695A33AC72}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {7EA7E587-5434-4E16-8CDD-217D36A401D9} = {EE38A10C-F0BB-40B5-B1E6-8CB16C6E452C} {9B4C6E1D-BE16-40F3-AFB7-369ACA5BFDBC} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} {45513FCC-E0E9-4C2A-A48F-239A380347BC} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} {237A08B2-E8F4-4384-A894-4C356CE45E92} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} diff --git a/skullOS.API/Controllers/CapturesController.cs b/skullOS.API/Controllers/CapturesController.cs new file mode 100644 index 0000000..8d01886 --- /dev/null +++ b/skullOS.API/Controllers/CapturesController.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Mvc; +using skullOS.Core; + +namespace skullOS.API.Controllers +{ + [Route("Captures")] + [ApiController] + public class CapturesController : ControllerBase + { + private readonly ILogger _logger; + string capturesDirectory; + DirectoryInfo capturesDirectoryInfo; + + public CapturesController(ILogger logger) + { + _logger = logger; + capturesDirectory = @FileManager.GetSkullDirectory() + @"/Captures"; + capturesDirectoryInfo = new DirectoryInfo(capturesDirectory); + } + + [HttpGet("MostRecent")] + public IActionResult GetNewest() + { + var mostRecentImage = capturesDirectoryInfo.GetFiles().OrderByDescending(f => f.LastAccessTime).FirstOrDefault(); + var image = System.IO.File.OpenRead(mostRecentImage.FullName); + return File(image, "image/jpeg"); + } + + [HttpGet("All")] + public List GetAll() + { + return capturesDirectoryInfo.GetFiles().ToList(); + } + + [HttpGet("AllPictures")] + public List GetAllPictures() + { + return capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpeg").ToList(); + } + + [HttpGet("AllVideos")] + public List GetAllVideos() + { + return capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".mp4").ToList(); + } + + [HttpGet("Image")] + public IActionResult GetImage(string fileId) + { + var file = capturesDirectoryInfo.GetFiles().Where(f => f.Name == fileId).FirstOrDefault(); + var image = System.IO.File.OpenRead(file.FullName); + return File(image, "image/jpeg"); + } + + [HttpGet("Video")] + public IActionResult GetVideo(string fileId) + { + var file = capturesDirectoryInfo.GetFiles().Where(f => f.Name == fileId).FirstOrDefault(); + var image = System.IO.File.OpenRead(file.FullName); + return File(image, "video/mp4"); + } + } +} diff --git a/skullOS.API/Program.cs b/skullOS.API/Program.cs new file mode 100644 index 0000000..fbb56f0 --- /dev/null +++ b/skullOS.API/Program.cs @@ -0,0 +1,37 @@ + +namespace skullOS.API +{ + public class Program + { + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + + builder.Services.AddControllers(); + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + builder.WebHost.UseUrls("http://*:5000;https://*:5001"); + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + + app.MapControllers(); + + app.Run(); + } + } +} \ No newline at end of file diff --git a/skullOS.API/Properties/launchSettings.json b/skullOS.API/Properties/launchSettings.json new file mode 100644 index 0000000..efd8b73 --- /dev/null +++ b/skullOS.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:24049", + "sslPort": 44308 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5185", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7201;http://localhost:5185", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/skullOS.API/appsettings.Development.json b/skullOS.API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/skullOS.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/skullOS.API/appsettings.json b/skullOS.API/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/skullOS.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/skullOS.API/skullOS.API.csproj b/skullOS.API/skullOS.API.csproj new file mode 100644 index 0000000..fd8e757 --- /dev/null +++ b/skullOS.API/skullOS.API.csproj @@ -0,0 +1,18 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + diff --git a/skullOS/Data/CoreSettings.txt b/skullOS/Data/CoreSettings.txt new file mode 100644 index 0000000..8ab5d35 --- /dev/null +++ b/skullOS/Data/CoreSettings.txt @@ -0,0 +1 @@ +API = TRUE \ No newline at end of file diff --git a/skullOS/Data/Modules.txt b/skullOS/Data/Modules.txt index 9cbcc29..dd4d2c7 100644 --- a/skullOS/Data/Modules.txt +++ b/skullOS/Data/Modules.txt @@ -1,3 +1,3 @@ Camera = True Output = True -Interlink = True \ No newline at end of file +Interlink = True diff --git a/skullOS/Program.cs b/skullOS/Program.cs index a7911c3..681d1f6 100644 --- a/skullOS/Program.cs +++ b/skullOS/Program.cs @@ -43,14 +43,14 @@ static void Run(Modules modulesToLoad = null, bool shouldCreateDirectory = true) } GpioController controller = new(); + //Need to redo i2c bits const int busId = 1; I2cConnectionSettings i2cSettings = new(busId, Bme280.DefaultI2cAddress); I2cDevice i2cDevice = I2cDevice.Create(i2cSettings); + //---- List systemsLoaded = LoadModules(modulesToLoad); - SetupModules(systemsLoaded, controller, i2cDevice); - RunModules(systemsLoaded, controller); } @@ -93,7 +93,7 @@ public static List LoadModules(Modules modulesToLoad) List subSystems = new(); if (modulesToLoad == null) { - modules = new(); + modules = new Modules(); } else { From 1ddc4b172fa00ae00bc4919f5900e6ffca8b08e7 Mon Sep 17 00:00:00 2001 From: David K Date: Wed, 15 Nov 2023 12:29:11 +0000 Subject: [PATCH 2/4] #7 API is sort of working * Is currently separate from the core program * Need to investigate whether 'f => f.Extension == ???' is being used correctly * Need to fix the passed in variables --- .gitignore | 2 ++ skullOS.API/.config/dotnet-tools.json | 5 +++++ skullOS.API/Controllers/CapturesController.cs | 15 +++++++++++---- skullOS.API/Program.cs | 5 ++++- skullOS.API/appsettings.Development.json | 3 ++- skullOS.Core/FileManager.cs | 10 ++++++++-- skullOS.Tests/ProgramTests.cs | 14 +++++++------- skullOS/Program.cs | 9 +++++++++ skullOS/skullOS.csproj | 3 +++ 9 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 skullOS.API/.config/dotnet-tools.json diff --git a/.gitignore b/.gitignore index 3b0d065..4624c72 100644 --- a/.gitignore +++ b/.gitignore @@ -368,3 +368,5 @@ skullOS/skullOs/ skullOS/skullOSselfContained/ skullOS/builds/ + +skullOS.API/builds/ diff --git a/skullOS.API/.config/dotnet-tools.json b/skullOS.API/.config/dotnet-tools.json new file mode 100644 index 0000000..b0e38ab --- /dev/null +++ b/skullOS.API/.config/dotnet-tools.json @@ -0,0 +1,5 @@ +{ + "version": 1, + "isRoot": true, + "tools": {} +} \ No newline at end of file diff --git a/skullOS.API/Controllers/CapturesController.cs b/skullOS.API/Controllers/CapturesController.cs index 8d01886..ae38e1f 100644 --- a/skullOS.API/Controllers/CapturesController.cs +++ b/skullOS.API/Controllers/CapturesController.cs @@ -14,31 +14,38 @@ public class CapturesController : ControllerBase public CapturesController(ILogger logger) { _logger = logger; + FileManager.CreateSkullDirectory(); capturesDirectory = @FileManager.GetSkullDirectory() + @"/Captures"; capturesDirectoryInfo = new DirectoryInfo(capturesDirectory); } + [HttpGet("Directory")] + public string GetDirectory() + { + return capturesDirectoryInfo.FullName; + } + [HttpGet("MostRecent")] public IActionResult GetNewest() { - var mostRecentImage = capturesDirectoryInfo.GetFiles().OrderByDescending(f => f.LastAccessTime).FirstOrDefault(); + var mostRecentImage = capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpeg").OrderByDescending(f => f.LastAccessTime).FirstOrDefault(); var image = System.IO.File.OpenRead(mostRecentImage.FullName); return File(image, "image/jpeg"); } - [HttpGet("All")] + [HttpGet("All")] //Not working public List GetAll() { return capturesDirectoryInfo.GetFiles().ToList(); } - [HttpGet("AllPictures")] + [HttpGet("AllPictures")] //Returns, but there's nothing in it public List GetAllPictures() { return capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpeg").ToList(); } - [HttpGet("AllVideos")] + [HttpGet("AllVideos")] //No videos yet public List GetAllVideos() { return capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".mp4").ToList(); diff --git a/skullOS.API/Program.cs b/skullOS.API/Program.cs index fbb56f0..77be0d3 100644 --- a/skullOS.API/Program.cs +++ b/skullOS.API/Program.cs @@ -23,8 +23,11 @@ public static void Main(string[] args) app.UseSwagger(); app.UseSwaggerUI(); } + if (!app.Environment.IsDevelopment()) + { + app.UseHttpsRedirection(); + } - app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/skullOS.API/appsettings.Development.json b/skullOS.API/appsettings.Development.json index 0c208ae..10f68b8 100644 --- a/skullOS.API/appsettings.Development.json +++ b/skullOS.API/appsettings.Development.json @@ -4,5 +4,6 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - } + }, + "AllowedHosts": "*" } diff --git a/skullOS.Core/FileManager.cs b/skullOS.Core/FileManager.cs index b5cc852..f8ee37d 100644 --- a/skullOS.Core/FileManager.cs +++ b/skullOS.Core/FileManager.cs @@ -1,4 +1,6 @@ -namespace skullOS.Core +using System.Runtime.InteropServices; + +namespace skullOS.Core { public static class FileManager { @@ -10,7 +12,11 @@ public static void CreateSkullDirectory() string pathToPersonalDir = @Environment.GetFolderPath(Environment.SpecialFolder.Personal); try { - rootDirectory = Directory.CreateDirectory(@pathToPersonalDir + @"/skullOS", unixCreateMode: UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + rootDirectory = Directory.CreateDirectory(@pathToPersonalDir + @"/skullOS", + unixCreateMode: UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); + } } catch (Exception e) { diff --git a/skullOS.Tests/ProgramTests.cs b/skullOS.Tests/ProgramTests.cs index 0075ecb..7f04230 100644 --- a/skullOS.Tests/ProgramTests.cs +++ b/skullOS.Tests/ProgramTests.cs @@ -19,13 +19,13 @@ public void TestLoadModules() Assert.NotNull(modulesLoaded); } - [Fact] - public void TestSetupModules() - { - var mockController = new MockGpioController(); - var modulesLoaded = CreateTestModules(); - Assert.True(Program.SetupModules(modulesLoaded, mockController)); - } + //[Fact] + //public void TestSetupModules() + //{ + // var mockController = new MockGpioController(); + // var modulesLoaded = CreateTestModules(); + // Assert.True(Program.SetupModules(modulesLoaded, mockController)); + //} [Fact] public void TestRunModules() diff --git a/skullOS/Program.cs b/skullOS/Program.cs index 681d1f6..5cc0381 100644 --- a/skullOS/Program.cs +++ b/skullOS/Program.cs @@ -41,6 +41,15 @@ static void Run(Modules modulesToLoad = null, bool shouldCreateDirectory = true) { FileManager.CreateSkullDirectory(); } + var settings = SettingsLoader.LoadConfig(@"Data/CoreSettings.txt"); + if (settings.TryGetValue("API", out string useAPI)) + { + if (bool.Parse(useAPI)) + { + //Start the API here + } + } + GpioController controller = new(); //Need to redo i2c bits diff --git a/skullOS/skullOS.csproj b/skullOS/skullOS.csproj index 9b7dee9..4d90779 100644 --- a/skullOS/skullOS.csproj +++ b/skullOS/skullOS.csproj @@ -15,6 +15,9 @@ + + Always + Always From bad4d85b3c92a7f6201ff7d8dc9473b8d15153b5 Mon Sep 17 00:00:00 2001 From: David K Date: Wed, 15 Nov 2023 15:13:46 +0000 Subject: [PATCH 3/4] #7 Captures api gets implemented --- skullOS.API/Controllers/CapturesController.cs | 39 ++++++++++++++----- skullOS.API/Data Objects/Capture.cs | 33 ++++++++++++++++ 2 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 skullOS.API/Data Objects/Capture.cs diff --git a/skullOS.API/Controllers/CapturesController.cs b/skullOS.API/Controllers/CapturesController.cs index ae38e1f..204125a 100644 --- a/skullOS.API/Controllers/CapturesController.cs +++ b/skullOS.API/Controllers/CapturesController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using skullOS.API.Data_Objects; using skullOS.Core; namespace skullOS.API.Controllers @@ -28,27 +29,45 @@ public string GetDirectory() [HttpGet("MostRecent")] public IActionResult GetNewest() { - var mostRecentImage = capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpeg").OrderByDescending(f => f.LastAccessTime).FirstOrDefault(); + var mostRecentImage = capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpg").OrderByDescending(f => f.LastAccessTime).FirstOrDefault(); var image = System.IO.File.OpenRead(mostRecentImage.FullName); return File(image, "image/jpeg"); } - [HttpGet("All")] //Not working - public List GetAll() + [HttpGet("All")] + public List GetAll() { - return capturesDirectoryInfo.GetFiles().ToList(); + List Files = new(); + foreach (var item in capturesDirectoryInfo.GetFiles()) + { + Capture file = new(item.FullName, item.CreationTime.ToShortTimeString()); + Files.Add(file); + } + return Files; } - [HttpGet("AllPictures")] //Returns, but there's nothing in it - public List GetAllPictures() + [HttpGet("AllPictures")] + public List GetAllPictures() { - return capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpeg").ToList(); + List Files = new(); + foreach (var item in capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".jpg")) + { + Capture file = new(item.FullName, item.CreationTime.ToShortTimeString()); + Files.Add(file); + } + return Files; } - [HttpGet("AllVideos")] //No videos yet - public List GetAllVideos() + [HttpGet("AllVideos")] + public List GetAllVideos() { - return capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".mp4").ToList(); + List Files = new(); + foreach (var item in capturesDirectoryInfo.GetFiles().Where(f => f.Extension == ".mp4")) + { + Capture file = new(item.FullName, item.CreationTime.ToShortTimeString()); + Files.Add(file); + } + return Files; } [HttpGet("Image")] diff --git a/skullOS.API/Data Objects/Capture.cs b/skullOS.API/Data Objects/Capture.cs new file mode 100644 index 0000000..42232bb --- /dev/null +++ b/skullOS.API/Data Objects/Capture.cs @@ -0,0 +1,33 @@ +namespace skullOS.API.Data_Objects +{ + public class Capture + { + private string filename; + private string time; + + public Capture(string Name, string time) + { + filename = Name; + this.time = time; + } + + public Capture(string Name, DateTime time) + { + filename = Name; + this.time = time.ToShortTimeString(); + } + + public string Time + { + get { return time; } + set { time = value; } + } + + public string Filename + { + get { return filename; } + set { filename = value; } + } + + } +} From 75951d6f66999336703ae648ea8e18ce90616c92 Mon Sep 17 00:00:00 2001 From: David K Date: Wed, 15 Nov 2023 15:51:40 +0000 Subject: [PATCH 4/4] #7 Delete Capture endpoint added So that the remote app can delete if neccessary --- skullOS.API/Controllers/CapturesController.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/skullOS.API/Controllers/CapturesController.cs b/skullOS.API/Controllers/CapturesController.cs index 204125a..a054ab0 100644 --- a/skullOS.API/Controllers/CapturesController.cs +++ b/skullOS.API/Controllers/CapturesController.cs @@ -20,6 +20,7 @@ public CapturesController(ILogger logger) capturesDirectoryInfo = new DirectoryInfo(capturesDirectory); } + #region Get [HttpGet("Directory")] public string GetDirectory() { @@ -85,5 +86,20 @@ public IActionResult GetVideo(string fileId) var image = System.IO.File.OpenRead(file.FullName); return File(image, "video/mp4"); } + #endregion + + [HttpDelete("Delete")] + public bool DeleteCapture(string filePath) + { + if (!System.IO.File.Exists(filePath)) + { + return false; + } + else + { + System.IO.File.Delete(filePath); + return true; + } + } } }