diff --git a/.gitignore b/.gitignore
index a36ae2a..15df58c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
src/MapClient/node_modules
src/CommunicationControl/.idea
+src/CommunicationControl/.vs
bin
obj
Logs
diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/DevOpsProject.CommunicationControl.Logic.csproj b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/DevOpsProject.CommunicationControl.Logic.csproj
index 6417dac..1b34d91 100644
--- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/DevOpsProject.CommunicationControl.Logic.csproj
+++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/DevOpsProject.CommunicationControl.Logic.csproj
@@ -3,7 +3,7 @@
net8.0
enable
- enable
+ disable
diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs
index d218e32..2e1a753 100644
--- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs
+++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/CommunicationControlService.cs
@@ -1,6 +1,7 @@
using DevOpsProject.CommunicationControl.Logic.Services.Interfaces;
using DevOpsProject.Shared.Clients;
using DevOpsProject.Shared.Configuration;
+using DevOpsProject.Shared.Enums;
using DevOpsProject.Shared.Exceptions;
using DevOpsProject.Shared.Messages;
using DevOpsProject.Shared.Models;
@@ -15,11 +16,12 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
private readonly IRedisKeyValueService _redisService;
private readonly RedisKeys _redisKeys;
private readonly IPublishService _messageBus;
- private readonly HiveHttpClient _hiveHttpClient;
+ private readonly CommunicationControlHttpClient _hiveHttpClient;
private readonly ILogger _logger;
+ private readonly IOptionsMonitor _communicationControlConfiguration;
public CommunicationControlService(ISpatialService spatialService, IRedisKeyValueService redisService, IOptionsSnapshot redisKeysSnapshot,
- IPublishService messageBus, HiveHttpClient hiveHttpClient, ILogger logger)
+ IPublishService messageBus, CommunicationControlHttpClient hiveHttpClient, ILogger logger, IOptionsMonitor communicationControlConfiguration)
{
_spatialService = spatialService;
_redisService = redisService;
@@ -27,6 +29,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
_messageBus = messageBus;
_hiveHttpClient = hiveHttpClient;
_logger = logger;
+ _communicationControlConfiguration = communicationControlConfiguration;
}
public async Task DisconnectHive(string hiveId)
@@ -65,7 +68,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
bool result = await _redisService.SetAsync(GetHiveKey(model.HiveID), model);
if (result)
{
- var operationalArea = await _spatialService.GetHiveOperationalArea(model);
+ var operationalArea = _spatialService.GetHiveOperationalArea(model);
await _messageBus.Publish(new HiveConnectedMessage
{
HiveID = model.HiveID,
@@ -119,7 +122,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
}
- public async Task SendHiveControlSignal(string hiveId, Location destination)
+ public async Task SendHiveControlSignal(string hiveId, Location destination)
{
var hive = await GetHiveModel(hiveId);
if (hive == null)
@@ -131,8 +134,15 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
try
{
- // TODO: Schema can be moved to appsettings
- var result = await _hiveHttpClient.SendHiveControlCommandAsync("http", hive.HiveIP, hive.HivePort, destination);
+ var command = new MoveHiveMindCommand
+ {
+ CommandType = State.Move,
+ Location = destination,
+ Timestamp = DateTime.Now
+ };
+
+ var result = await _hiveHttpClient.SendHiveControlCommandAsync(_communicationControlConfiguration.CurrentValue.RequestScheme,
+ hive.HiveIP, hive.HivePort, _communicationControlConfiguration.CurrentValue.HiveMindPath, command);
isSuccessfullySent = true;
return result;
}
diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs
index 8e3b363..86445cf 100644
--- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs
+++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ICommunicationControlService.cs
@@ -9,6 +9,6 @@ namespace DevOpsProject.CommunicationControl.Logic.Services.Interfaces
Task> GetAllHives();
Task ConnectHive(HiveModel model);
Task AddTelemetry(HiveTelemetryModel model);
- Task SendHiveControlSignal(string hiveId, Location destination);
+ Task SendHiveControlSignal(string hiveId, Location destination);
}
}
diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ISpatialService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ISpatialService.cs
index 7a8d8f8..42a3b67 100644
--- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ISpatialService.cs
+++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/Interfaces/ISpatialService.cs
@@ -4,6 +4,6 @@ namespace DevOpsProject.CommunicationControl.Logic.Services.Interfaces
{
public interface ISpatialService
{
- Task GetHiveOperationalArea(HiveModel hiveModel);
+ HiveOperationalArea GetHiveOperationalArea(HiveModel hiveModel);
}
}
diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/RedisPublishService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/RedisPublishService.cs
index 4c62e93..563bbe7 100644
--- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/RedisPublishService.cs
+++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/RedisPublishService.cs
@@ -11,10 +11,10 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
private readonly IConnectionMultiplexer _connectionMultiplexer;
private readonly RedisOptions _redisOptions;
- public RedisPublishService(IConnectionMultiplexer connectionMultiplexer, IOptions redisOptions)
+ public RedisPublishService(IConnectionMultiplexer connectionMultiplexer, IOptionsMonitor redisOptions)
{
_connectionMultiplexer = connectionMultiplexer;
- _redisOptions = redisOptions.Value;
+ _redisOptions = redisOptions.CurrentValue;
}
public async Task Publish(T message)
@@ -22,7 +22,14 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
var pubsub = _connectionMultiplexer.GetSubscriber();
var messageJson = JsonSerializer.Serialize(message);
- await pubsub.PublishAsync(_redisOptions.PublishChannel, messageJson);
+ if (_redisOptions.PublishChannel != null)
+ {
+ await pubsub.PublishAsync(_redisOptions.PublishChannel, messageJson);
+ }
+ else
+ {
+ throw new Exception($"Error while attempting to publish message to Message Bus, publish channel: {_redisOptions.PublishChannel}");
+ }
}
}
}
diff --git a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/SpatialService.cs b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/SpatialService.cs
index af7f3bd..07d8a7c 100644
--- a/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/SpatialService.cs
+++ b/src/CommunicationControl/DevOpsProject.CommunicationControl.Logic/Services/SpatialService.cs
@@ -13,7 +13,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
_operationalAreaConfig = operationalAreaConfig;
}
- public async Task GetHiveOperationalArea(HiveModel hiveModel)
+ public HiveOperationalArea GetHiveOperationalArea(HiveModel hiveModel)
{
var operationalArea = new HiveOperationalArea
{
diff --git a/src/CommunicationControl/DevOpsProject.Example.MessageListener/DevOpsProject.Example.MessageListener.csproj b/src/CommunicationControl/DevOpsProject.Example.MessageListener/DevOpsProject.Example.MessageListener.csproj
index 32cf93d..119a6ac 100644
--- a/src/CommunicationControl/DevOpsProject.Example.MessageListener/DevOpsProject.Example.MessageListener.csproj
+++ b/src/CommunicationControl/DevOpsProject.Example.MessageListener/DevOpsProject.Example.MessageListener.csproj
@@ -4,7 +4,7 @@
Exe
net8.0
enable
- enable
+ disable
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/Controllers/HiveMindController.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/Controllers/HiveMindController.cs
new file mode 100644
index 0000000..9f50d98
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/Controllers/HiveMindController.cs
@@ -0,0 +1,49 @@
+using Asp.Versioning;
+using DevOpsProject.HiveMind.Logic.Services.Interfaces;
+using DevOpsProject.Shared.Configuration;
+using DevOpsProject.Shared.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+
+namespace DevOpsProject.HiveMind.API.Controllers
+{
+ [ApiVersion("1.0")]
+ [ApiController]
+ [Route("api/v{version:apiVersion}")]
+ public class HiveMindController : Controller
+ {
+ private readonly IHiveMindService _hiveMindService;
+ private readonly IHiveMindMovingService _hiveMindMovingService;
+
+ public HiveMindController(IHiveMindService hiveMindService, IHiveMindMovingService hiveMindMovingService)
+ {
+ _hiveMindService = hiveMindService;
+ _hiveMindMovingService = hiveMindMovingService;
+ }
+
+ [HttpGet("ping")]
+ public IActionResult Ping(IOptionsSnapshot config)
+ {
+ return Ok(new
+ {
+ Timestamp = DateTime.Now,
+ ID = config.Value.HiveID
+ });
+ }
+
+ [HttpPost("connect")]
+ public async Task TriggerConnectHive()
+ {
+ await _hiveMindService.ConnectHive();
+ return Ok();
+ }
+
+ [HttpPost("command")]
+ public IActionResult MoveHideMind(MoveHiveMindCommand command)
+ {
+ _hiveMindMovingService.MoveToLocation(command.Location);
+ return Ok();
+ }
+
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/LogicConfiguration.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/LogicConfiguration.cs
new file mode 100644
index 0000000..5adf5eb
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DI/LogicConfiguration.cs
@@ -0,0 +1,16 @@
+using DevOpsProject.HiveMind.Logic.Services;
+using DevOpsProject.HiveMind.Logic.Services.Interfaces;
+
+namespace DevOpsProject.HiveMind.API.DI
+{
+ public static class LogicConfiguration
+ {
+ public static IServiceCollection AddHiveMindLogic(this IServiceCollection serviceCollection)
+ {
+ serviceCollection.AddTransient();
+ serviceCollection.AddTransient();
+
+ return serviceCollection;
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj b/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj
new file mode 100644
index 0000000..81e123f
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net8.0
+ disable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj.user b/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj.user
new file mode 100644
index 0000000..031db34
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj.user
@@ -0,0 +1,8 @@
+
+
+
+ https
+ MvcControllerEmptyScaffolder
+ root/Common/MVC/Controller
+
+
\ No newline at end of file
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.http b/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.http
new file mode 100644
index 0000000..26ce71f
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.http
@@ -0,0 +1,6 @@
+@DevOpsProject.HiveMind.API_HostAddress = http://localhost:5149
+
+GET {{DevOpsProject.HiveMind.API_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/Middleware/ExceptionHandlingMiddleware.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/Middleware/ExceptionHandlingMiddleware.cs
new file mode 100644
index 0000000..f3404ad
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/Middleware/ExceptionHandlingMiddleware.cs
@@ -0,0 +1,35 @@
+using Microsoft.AspNetCore.Diagnostics;
+using System.Text.Json;
+
+namespace DevOpsProject.HiveMind.API.Middleware
+{
+ public class ExceptionHandlingMiddleware : IExceptionHandler
+ {
+ private readonly ILogger _logger;
+ private readonly IHostEnvironment _hostEnvironment;
+
+ public ExceptionHandlingMiddleware(ILogger logger, IHostEnvironment hostEnvironment)
+ {
+ _hostEnvironment = hostEnvironment;
+ _logger = logger;
+ }
+
+ public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
+ {
+ _logger.LogError(exception, "Unhandled exception occured: {Message}", exception.Message);
+
+ httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
+ httpContext.Response.ContentType = "application/json";
+
+ var errorResponse = new
+ {
+ Message = "Unexpected error occured",
+ Detail = _hostEnvironment.IsDevelopment() ? exception.ToString() : null
+ };
+
+ var jsonResponse = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions { WriteIndented = true });
+ await httpContext.Response.WriteAsync(jsonResponse, cancellationToken);
+ return true;
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs b/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs
new file mode 100644
index 0000000..a5dde80
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/Program.cs
@@ -0,0 +1,94 @@
+using Asp.Versioning;
+using DevOpsProject.HiveMind.API.DI;
+using DevOpsProject.HiveMind.API.Middleware;
+using DevOpsProject.Shared.Clients;
+using DevOpsProject.Shared.Configuration;
+using Microsoft.OpenApi.Models;
+using Polly;
+using Polly.Extensions.Http;
+using Serilog;
+
+internal class Program
+{
+ private static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ builder.Host.UseSerilog((context, services, loggerConfig) =>
+ loggerConfig.ReadFrom.Configuration(context.Configuration)
+ .ReadFrom.Services(services)
+ .Enrich.FromLogContext());
+
+ builder.Services.AddApiVersioning(options =>
+ {
+ options.DefaultApiVersion = new ApiVersion(1, 0);
+ options.AssumeDefaultVersionWhenUnspecified = true;
+ options.ReportApiVersions = true;
+ options.ApiVersionReader = ApiVersionReader.Combine(
+ new UrlSegmentApiVersionReader(),
+ new HeaderApiVersionReader("X-Api-Version")
+ );
+ }).AddApiExplorer(options =>
+ {
+ options.GroupNameFormat = "'v'VVV";
+ options.SubstituteApiVersionInUrl = true;
+ });
+
+ // TODO: double check following approach
+ builder.Services.AddControllers().AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.PropertyNamingPolicy = null;
+ });
+ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen(c =>
+ {
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "HiveMind - V1", Version = "v1.0" });
+ });
+ builder.Services.AddHiveMindLogic();
+
+ builder.Services.Configure(builder.Configuration.GetSection("CommunicationConfiguration"));
+
+ var communicationControlRetryPolicy = HttpPolicyExtensions
+ .HandleTransientHttpError()
+ .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
+ builder.Services.AddHttpClient()
+ .AddPolicyHandler(communicationControlRetryPolicy);
+
+ string corsPolicyName = "HiveMindCorsPolicy";
+ builder.Services.AddCors(options =>
+ {
+ options.AddPolicy(name: corsPolicyName,
+ policy =>
+ {
+ policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins
+ .AllowAnyMethod()
+ .AllowAnyHeader();
+ });
+ });
+
+ builder.Services.AddExceptionHandler();
+ builder.Services.AddProblemDetails();
+
+ var app = builder.Build();
+
+ app.UseExceptionHandler();
+
+ // Configure the HTTP request pipeline.
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ }
+
+ app.UseCors(corsPolicyName);
+
+ //app.UseHttpsRedirection();
+
+ app.UseAuthorization();
+
+ app.MapControllers();
+
+ app.Run();
+ }
+}
\ No newline at end of file
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/Properties/launchSettings.json b/src/CommunicationControl/DevOpsProject.HiveMind.API/Properties/launchSettings.json
new file mode 100644
index 0000000..b63f8cd
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:39179",
+ "sslPort": 44372
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5149",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7167;http://localhost:5149",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.Development.json b/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json b/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json
new file mode 100644
index 0000000..e2b3c90
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.API/appsettings.json
@@ -0,0 +1,49 @@
+{
+ "Serilog": {
+ "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Information",
+ "System": "Information"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "restrictedToMinimumLevel": "Information"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "Logs/log-.txt",
+ "rollingInterval": "Day",
+ "rollOnFileSizeLimit": true,
+ "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
+ "restrictedToMinimumLevel": "Warning"
+ }
+ }
+ ],
+ "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
+ "Properties": {
+ "Application": "DevOpsProject.HiveMind",
+ "Environment": "Development"
+ }
+ },
+ "CommunicationConfiguration": {
+ "RequestSchema": "http",
+ "CommunicationControlIP": "localhost",
+ "CommunicationControlPort": 8080,
+ "CommunicationControlPath": "api/v1/hive",
+ "HiveIP": "localhost",
+ "HivePort": 5149,
+ "HiveID": "1",
+ "InitialLocation": {
+ "Latitude": 48.719547,
+ "Longitude": 38.092680
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/DevOpsProject.HiveMind.Logic.csproj b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/DevOpsProject.HiveMind.Logic.csproj
new file mode 100644
index 0000000..b8963cf
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/DevOpsProject.HiveMind.Logic.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net8.0
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs
new file mode 100644
index 0000000..5633e8a
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindMovingService.cs
@@ -0,0 +1,101 @@
+using DevOpsProject.HiveMind.Logic.Services.Interfaces;
+using DevOpsProject.HiveMind.Logic.State;
+using DevOpsProject.Shared.Models;
+using Microsoft.Extensions.Logging;
+
+namespace DevOpsProject.HiveMind.Logic.Services
+{
+ public class HiveMindMovingService : IHiveMindMovingService
+ {
+ private readonly ILogger _logger;
+ private Timer _movementTimer;
+ public HiveMindMovingService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public void MoveToLocation(Location destination)
+ {
+ lock (typeof(HiveInMemoryState))
+ {
+ if (HiveInMemoryState.OperationalArea == null || HiveInMemoryState.CurrentLocation == null)
+ {
+ _logger.LogWarning("Cannot start moving: OperationalArea or CurrentLocation is not set.");
+ return;
+ }
+
+ // If already moving - stop movement
+ if (HiveInMemoryState.IsMoving)
+ {
+ StopMovement();
+ }
+
+ HiveInMemoryState.Destination = destination;
+ HiveInMemoryState.IsMoving = true;
+
+ _logger.LogInformation($"Received move command: Moving towards {destination}");
+
+ // Start the movement timer if not already running
+ if (_movementTimer == null)
+ {
+ // TODO: Recalculating position each N seconds
+ _movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(3));
+ _logger.LogInformation("Movement timer started.");
+ }
+ }
+ }
+
+ private void UpdateMovement(object state)
+ {
+ lock (typeof(HiveInMemoryState))
+ {
+ var currentLocation = HiveInMemoryState.CurrentLocation;
+ var destination = HiveInMemoryState.Destination;
+
+ if (currentLocation == null || destination == null)
+ {
+ StopMovement();
+ return;
+ }
+
+ if (AreLocationsEqual(currentLocation.Value, destination.Value))
+ {
+ StopMovement();
+ return;
+ }
+
+ Location newLocation = CalculateNextPosition(currentLocation.Value, destination.Value, 0.1f);
+ HiveInMemoryState.CurrentLocation = newLocation;
+
+ _logger.LogInformation($"Moved closer: {newLocation}");
+ }
+ }
+
+ private void StopMovement()
+ {
+ _movementTimer?.Dispose();
+ _movementTimer = null;
+ HiveInMemoryState.IsMoving = false;
+ HiveInMemoryState.Destination = null;
+ _logger.LogInformation("Movement stopped: Reached destination.");
+ }
+
+ private static bool AreLocationsEqual(Location loc1, Location loc2)
+ {
+ const float tolerance = 0.000001f;
+ return Math.Abs(loc1.Latitude - loc2.Latitude) < tolerance &&
+ Math.Abs(loc1.Longitude - loc2.Longitude) < tolerance;
+ }
+
+ private static Location CalculateNextPosition(Location current, Location destination, float stepSize)
+ {
+ float newLat = current.Latitude + (destination.Latitude - current.Latitude) * stepSize;
+ float newLon = current.Longitude + (destination.Longitude - current.Longitude) * stepSize;
+ return new Location
+ {
+ Latitude = newLat,
+ Longitude = newLon
+ };
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs
new file mode 100644
index 0000000..8675912
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/HiveMindService.cs
@@ -0,0 +1,130 @@
+using DevOpsProject.HiveMind.Logic.Services.Interfaces;
+using DevOpsProject.Shared.Clients;
+using DevOpsProject.Shared.Models;
+using DevOpsProject.Shared.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System.Text.Json;
+using DevOpsProject.HiveMind.Logic.State;
+
+namespace DevOpsProject.HiveMind.Logic.Services
+{
+ public class HiveMindService : IHiveMindService
+ {
+ private readonly HiveMindHttpClient _httpClient;
+ private readonly ILogger _logger;
+ private readonly HiveCommunicationConfig _communicationConfigurationOptions;
+ private Timer _telemetryTimer;
+
+ public HiveMindService(HiveMindHttpClient httpClient, ILogger logger, IOptionsSnapshot communicationConfigurationOptions)
+ {
+ _httpClient = httpClient;
+ _logger = logger;
+ _communicationConfigurationOptions = communicationConfigurationOptions.Value;
+ }
+
+ public async Task ConnectHive()
+ {
+ var request = new HiveConnectRequest
+ {
+ HiveIP = _communicationConfigurationOptions.HiveIP,
+ HivePort = _communicationConfigurationOptions.HivePort,
+ HiveID = _communicationConfigurationOptions.HiveID
+ };
+
+ var connectResult = await _httpClient.SendCommunicationControlConnectAsync(_communicationConfigurationOptions.RequestSchema,
+ _communicationConfigurationOptions.CommunicationControlIP, _communicationConfigurationOptions.CommunicationControlPort,
+ _communicationConfigurationOptions.CommunicationControlPath, request);
+
+ _logger.LogInformation($"Connect result for HiveID: {request.HiveID}: {connectResult}");
+
+ if (connectResult != null)
+ {
+ var hiveConnectResponse = JsonSerializer.Deserialize(connectResult);
+
+ if (hiveConnectResponse != null && hiveConnectResponse.ConnectResult)
+ {
+ HiveInMemoryState.OperationalArea = hiveConnectResponse.OperationalArea;
+ HiveInMemoryState.CurrentLocation = _communicationConfigurationOptions.InitialLocation;
+
+ // HERE - we are starting to send telemetry
+ StartTelemetry();
+ }
+ else
+ {
+ _logger.LogInformation($"Connecting hive failed for ID: {request.HiveID}");
+ throw new Exception($"Failed to connect HiveID: {request.HiveID}");
+ }
+ }
+ else
+ {
+ _logger.LogError($"Unable to connect Hive with ID: {request.HiveID}, Port: {request.HivePort}, IP: {request.HiveIP} to Communication Control. \n" +
+ $"Requested IP: {_communicationConfigurationOptions.CommunicationControlIP}, Port: {_communicationConfigurationOptions.HivePort}");
+ throw new Exception($"Failed to connect hive for HiveID: {request.HiveID}");
+ }
+ }
+
+ public void StopAllTelemetry()
+ {
+ StopTelemetry();
+ }
+
+ #region private methods
+ private void StartTelemetry()
+ {
+ if (HiveInMemoryState.IsTelemetryRunning) return;
+ // TODO: Sending telemetry each N seconds
+ _telemetryTimer = new Timer(SendTelemetry, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
+
+ _logger.LogInformation("Telemetry timer started.");
+ }
+
+ private void StopTelemetry()
+ {
+ _telemetryTimer?.Dispose();
+ HiveInMemoryState.IsTelemetryRunning = false;
+
+ _logger.LogInformation("Telemetry timer stopped.");
+ }
+
+ private async void SendTelemetry(object state)
+ {
+ var currentLocation = HiveInMemoryState.CurrentLocation;
+
+ try
+ {
+ var request = new HiveTelemetryRequest
+ {
+ HiveID = _communicationConfigurationOptions.HiveID,
+ Location = HiveInMemoryState.CurrentLocation ?? default,
+ // TODO: MOCKED FOR NOW
+ Height = 5,
+ Speed = 15,
+ State = Shared.Enums.State.Move
+ };
+
+ var connectResult = await _httpClient.SendCommunicationControlTelemetryAsync(_communicationConfigurationOptions.RequestSchema,
+ _communicationConfigurationOptions.CommunicationControlIP, _communicationConfigurationOptions.CommunicationControlPort,
+ _communicationConfigurationOptions.CommunicationControlPath, request);
+
+ _logger.LogInformation($"Telemetry sent for HiveID: {request.HiveID}: {connectResult}");
+
+ if (connectResult != null)
+ {
+ // TODO: Store timestamp
+ var hiveConnectResponse = JsonSerializer.Deserialize(connectResult);
+ }
+ else
+ {
+ _logger.LogError($"Unable to send Hive telemetry for HiveID: {request.HiveID}.");
+ throw new Exception($"Failed to send telemetry for HiveID: {request.HiveID}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError("Error sending telemetry: {Message}", ex.Message);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/Interfaces/IHiveMindMovingService.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/Interfaces/IHiveMindMovingService.cs
new file mode 100644
index 0000000..bc510d6
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/Interfaces/IHiveMindMovingService.cs
@@ -0,0 +1,9 @@
+using DevOpsProject.Shared.Models;
+
+namespace DevOpsProject.HiveMind.Logic.Services.Interfaces
+{
+ public interface IHiveMindMovingService
+ {
+ void MoveToLocation(Location destination);
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/Interfaces/IHiveMindService.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/Interfaces/IHiveMindService.cs
new file mode 100644
index 0000000..a51e87f
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/Services/Interfaces/IHiveMindService.cs
@@ -0,0 +1,10 @@
+using DevOpsProject.Shared.Models;
+
+namespace DevOpsProject.HiveMind.Logic.Services.Interfaces
+{
+ public interface IHiveMindService
+ {
+ Task ConnectHive();
+ void StopAllTelemetry();
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.HiveMind.Logic/State/HiveInMemoryState.cs b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/State/HiveInMemoryState.cs
new file mode 100644
index 0000000..da6adfd
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.HiveMind.Logic/State/HiveInMemoryState.cs
@@ -0,0 +1,98 @@
+using DevOpsProject.Shared.Models;
+
+namespace DevOpsProject.HiveMind.Logic.State
+{
+ public static class HiveInMemoryState
+ {
+ private static readonly object _operationalAreaLock = new();
+ private static readonly object _telemetryLock = new();
+ private static readonly object _movementLock = new();
+
+ private static HiveOperationalArea _operationalArea;
+
+ private static bool _isTelemetryRunning;
+ private static bool _isMoving;
+
+ private static Location? _currentLocation;
+ private static Location? _destination;
+
+
+ public static HiveOperationalArea OperationalArea
+ {
+ get
+ {
+ lock (_operationalAreaLock)
+ {
+ return _operationalArea;
+ }
+ }
+ set
+ {
+ lock (_operationalAreaLock)
+ {
+ _operationalArea = value;
+ }
+ }
+ }
+
+ public static bool IsTelemetryRunning
+ {
+ get
+ {
+ lock (_telemetryLock)
+ {
+ return _isTelemetryRunning;
+ }
+ }
+ set
+ {
+ lock (_telemetryLock)
+ {
+ _isTelemetryRunning = value;
+ }
+ }
+ }
+
+ public static bool IsMoving
+ {
+ get
+ {
+ lock (_movementLock)
+ {
+ return _isMoving;
+ }
+ }
+ set
+ {
+ lock (_movementLock)
+ {
+ _isMoving = value;
+ }
+ }
+ }
+
+ public static Location? CurrentLocation
+ {
+ get
+ {
+ lock (_movementLock) { return _currentLocation; }
+ }
+ set
+ {
+ lock (_movementLock) { _currentLocation = value; }
+ }
+ }
+
+ public static Location? Destination
+ {
+ get
+ {
+ lock (_movementLock) { return _destination; }
+ }
+ set
+ {
+ lock (_movementLock) { _destination = value; }
+ }
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/Clients/HiveHttpClient.cs b/src/CommunicationControl/DevOpsProject.Shared/Clients/CommunicationControlHttpClient.cs
similarity index 62%
rename from src/CommunicationControl/DevOpsProject.Shared/Clients/HiveHttpClient.cs
rename to src/CommunicationControl/DevOpsProject.Shared/Clients/CommunicationControlHttpClient.cs
index d3fd4c5..0a30257 100644
--- a/src/CommunicationControl/DevOpsProject.Shared/Clients/HiveHttpClient.cs
+++ b/src/CommunicationControl/DevOpsProject.Shared/Clients/CommunicationControlHttpClient.cs
@@ -1,36 +1,37 @@
-using System.Text;
-using System.Text.Json;
-
-namespace DevOpsProject.Shared.Clients
-{
- public class HiveHttpClient
- {
- private readonly HttpClient _httpClient;
-
- public HiveHttpClient(HttpClient httpClient)
- {
- _httpClient = httpClient;
- }
-
- public async Task SendHiveControlCommandAsync(string scheme, string ip, int port, object payload)
- {
- var uriBuilder = new UriBuilder
- {
- Scheme = scheme,
- Host = ip,
- Port = port,
- Path = "api/control"
- };
-
- var jsonContent = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
-
- var response = await _httpClient.PostAsync(uriBuilder.Uri, jsonContent);
-
- if (response.IsSuccessStatusCode)
- {
- return await response.Content.ReadAsStringAsync();
- }
- return null;
- }
- }
-}
+using DevOpsProject.Shared.Models;
+using System.Text;
+using System.Text.Json;
+
+namespace DevOpsProject.Shared.Clients
+{
+ public class CommunicationControlHttpClient
+ {
+ private readonly HttpClient _httpClient;
+
+ public CommunicationControlHttpClient(HttpClient httpClient)
+ {
+ _httpClient = httpClient;
+ }
+
+ public async Task SendHiveControlCommandAsync(string scheme, string ip, int port, string path, MoveHiveMindCommand command)
+ {
+ var uriBuilder = new UriBuilder
+ {
+ Scheme = scheme,
+ Host = ip,
+ Port = port,
+ Path = $"{path}/command"
+ };
+
+ var jsonContent = new StringContent(JsonSerializer.Serialize(command), Encoding.UTF8, "application/json");
+
+ var response = await _httpClient.PostAsync(uriBuilder.Uri, jsonContent);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.Content.ReadAsStringAsync();
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/Clients/HiveMindHttpClient.cs b/src/CommunicationControl/DevOpsProject.Shared/Clients/HiveMindHttpClient.cs
new file mode 100644
index 0000000..21be347
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.Shared/Clients/HiveMindHttpClient.cs
@@ -0,0 +1,58 @@
+using DevOpsProject.Shared.Models;
+using System.Text;
+using System.Text.Json;
+
+namespace DevOpsProject.Shared.Clients
+{
+ public class HiveMindHttpClient
+ {
+ private readonly HttpClient _httpClient;
+
+ public HiveMindHttpClient(HttpClient httpClient)
+ {
+ _httpClient = httpClient;
+ }
+
+ public async Task SendCommunicationControlConnectAsync(string requestSchema, string ip, int port, string path, HiveConnectRequest payload)
+ {
+ var uriBuilder = new UriBuilder
+ {
+ Scheme = requestSchema,
+ Host = ip,
+ Port = port,
+ Path = $"{path}/connect"
+ };
+
+ var jsonContent = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
+
+ var response = await _httpClient.PostAsync(uriBuilder.Uri, jsonContent);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.Content.ReadAsStringAsync();
+ }
+ return null;
+ }
+
+ public async Task SendCommunicationControlTelemetryAsync(string requestSchema, string ip, int port, string path, HiveTelemetryRequest payload)
+ {
+ var uriBuilder = new UriBuilder
+ {
+ Scheme = requestSchema,
+ Host = ip,
+ Port = port,
+ Path = $"{path}/telemetry"
+ };
+
+ var jsonContent = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
+
+ var response = await _httpClient.PostAsync(uriBuilder.Uri, jsonContent);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.Content.ReadAsStringAsync();
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs b/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs
new file mode 100644
index 0000000..731d58d
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.Shared/Configuration/ComControlCommunicationConfiguration.cs
@@ -0,0 +1,8 @@
+namespace DevOpsProject.Shared.Configuration
+{
+ public class ComControlCommunicationConfiguration
+ {
+ public string RequestScheme { get; set; }
+ public string HiveMindPath { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/Configuration/HiveCommunicationConfig.cs b/src/CommunicationControl/DevOpsProject.Shared/Configuration/HiveCommunicationConfig.cs
new file mode 100644
index 0000000..e939986
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.Shared/Configuration/HiveCommunicationConfig.cs
@@ -0,0 +1,16 @@
+using DevOpsProject.Shared.Models;
+
+namespace DevOpsProject.Shared.Configuration
+{
+ public class HiveCommunicationConfig
+ {
+ public string RequestSchema { get; set; }
+ public string CommunicationControlIP { get; set; }
+ public int CommunicationControlPort { get; set; }
+ public string CommunicationControlPath { get; set; }
+ public string HiveIP { get; set; }
+ public int HivePort { get; set; }
+ public string HiveID { get; set; }
+ public Location InitialLocation { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/DevOpsProject.Shared.csproj b/src/CommunicationControl/DevOpsProject.Shared/DevOpsProject.Shared.csproj
index bb23fb7..de191f5 100644
--- a/src/CommunicationControl/DevOpsProject.Shared/DevOpsProject.Shared.csproj
+++ b/src/CommunicationControl/DevOpsProject.Shared/DevOpsProject.Shared.csproj
@@ -3,7 +3,7 @@
net8.0
enable
- enable
+ disable
diff --git a/src/CommunicationControl/DevOpsProject/DTO/Hive/Request/HiveConnectRequest.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs
similarity index 53%
rename from src/CommunicationControl/DevOpsProject/DTO/Hive/Request/HiveConnectRequest.cs
rename to src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs
index a62dde8..86a2570 100644
--- a/src/CommunicationControl/DevOpsProject/DTO/Hive/Request/HiveConnectRequest.cs
+++ b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectRequest.cs
@@ -1,9 +1,15 @@
-namespace DevOpsProject.CommunicationControl.API.DTO.Hive.Request
-{
- public class HiveConnectRequest
- {
- public string HiveIP { get; set; }
- public int HivePort { get; set; }
- public string HiveID { get; set; }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DevOpsProject.Shared.Models
+{
+ public class HiveConnectRequest
+ {
+ public string HiveIP { get; set; }
+ public int HivePort { get; set; }
+ public string HiveID { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectResponse.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectResponse.cs
new file mode 100644
index 0000000..cce5a17
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveConnectResponse.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+
+namespace DevOpsProject.Shared.Models
+{
+ public class HiveConnectResponse
+ {
+ public bool ConnectResult { get; set; }
+ public HiveOperationalArea OperationalArea { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject/DTO/Hive/Request/HiveTelemetryRequest.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveTelemetryRequest.cs
similarity index 72%
rename from src/CommunicationControl/DevOpsProject/DTO/Hive/Request/HiveTelemetryRequest.cs
rename to src/CommunicationControl/DevOpsProject.Shared/Models/HiveTelemetryRequest.cs
index 9aac085..c2866cd 100644
--- a/src/CommunicationControl/DevOpsProject/DTO/Hive/Request/HiveTelemetryRequest.cs
+++ b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveTelemetryRequest.cs
@@ -1,14 +1,13 @@
-using DevOpsProject.Shared.Enums;
-using DevOpsProject.Shared.Models;
-
-namespace DevOpsProject.CommunicationControl.API.DTO.Hive.Request
-{
- public class HiveTelemetryRequest
- {
- public string HiveID { get; set; }
- public Location Location { get; set; }
- public float Speed { get; set; }
- public float Height { get; set; }
- public State State { get; set; }
- }
-}
+using DevOpsProject.Shared.Enums;
+
+namespace DevOpsProject.Shared.Models
+{
+ public class HiveTelemetryRequest
+ {
+ public string HiveID { get; set; }
+ public Location Location { get; set; }
+ public float Speed { get; set; }
+ public float Height { get; set; }
+ public State State { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject/DTO/Hive/Response/HiveTelemetryResponse.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveTelemetryResponse.cs
similarity index 56%
rename from src/CommunicationControl/DevOpsProject/DTO/Hive/Response/HiveTelemetryResponse.cs
rename to src/CommunicationControl/DevOpsProject.Shared/Models/HiveTelemetryResponse.cs
index f0f6e31..65601da 100644
--- a/src/CommunicationControl/DevOpsProject/DTO/Hive/Response/HiveTelemetryResponse.cs
+++ b/src/CommunicationControl/DevOpsProject.Shared/Models/HiveTelemetryResponse.cs
@@ -1,7 +1,9 @@
-namespace DevOpsProject.CommunicationControl.API.DTO.Hive.Response
-{
- public class HiveTelemetryResponse
- {
- public DateTime Timestamp { get; set; }
- }
-}
+using System.Text.Json.Serialization;
+
+namespace DevOpsProject.Shared.Models
+{
+ public class HiveTelemetryResponse
+ {
+ public DateTime Timestamp { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.Shared/Models/MoveHiveMindCommand.cs b/src/CommunicationControl/DevOpsProject.Shared/Models/MoveHiveMindCommand.cs
new file mode 100644
index 0000000..ea7f325
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.Shared/Models/MoveHiveMindCommand.cs
@@ -0,0 +1,12 @@
+using DevOpsProject.Shared.Enums;
+
+namespace DevOpsProject.Shared.Models
+{
+ public class MoveHiveMindCommand
+ {
+ public State CommandType { get; set; }
+ // TODO: CLARIFY CommandPayload
+ public Location Location { get; set; }
+ public DateTime Timestamp { get; set; }
+ }
+}
diff --git a/src/CommunicationControl/DevOpsProject.sln b/src/CommunicationControl/DevOpsProject.sln
index 988ed94..f0c8d8a 100644
--- a/src/CommunicationControl/DevOpsProject.sln
+++ b/src/CommunicationControl/DevOpsProject.sln
@@ -11,6 +11,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevOpsProject.Shared", "Dev
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevOpsProject.Example.MessageListener", "DevOpsProject.Example.MessageListener\DevOpsProject.Example.MessageListener.csproj", "{CBB302CE-D22A-4DA0-8811-E4F8FDFC1C4B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevOpsProject.HiveMind.API", "DevOpsProject.HiveMind.API\DevOpsProject.HiveMind.API.csproj", "{65F5C602-86C3-4653-B3B4-326FC2D01B8D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevOpsProject.HiveMind.Logic", "DevOpsProject.HiveMind.Logic\DevOpsProject.HiveMind.Logic.csproj", "{83D3A1EA-64FE-407C-AB3A-A35CC2C9F8EE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +37,14 @@ Global
{CBB302CE-D22A-4DA0-8811-E4F8FDFC1C4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBB302CE-D22A-4DA0-8811-E4F8FDFC1C4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBB302CE-D22A-4DA0-8811-E4F8FDFC1C4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65F5C602-86C3-4653-B3B4-326FC2D01B8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65F5C602-86C3-4653-B3B4-326FC2D01B8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65F5C602-86C3-4653-B3B4-326FC2D01B8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65F5C602-86C3-4653-B3B4-326FC2D01B8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {83D3A1EA-64FE-407C-AB3A-A35CC2C9F8EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {83D3A1EA-64FE-407C-AB3A-A35CC2C9F8EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {83D3A1EA-64FE-407C-AB3A-A35CC2C9F8EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {83D3A1EA-64FE-407C-AB3A-A35CC2C9F8EE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/CommunicationControl/DevOpsProject.slnLaunch.user b/src/CommunicationControl/DevOpsProject.slnLaunch.user
new file mode 100644
index 0000000..f0780a9
--- /dev/null
+++ b/src/CommunicationControl/DevOpsProject.slnLaunch.user
@@ -0,0 +1,19 @@
+[
+ {
+ "Name": "New Profile",
+ "Projects": [
+ {
+ "Path": "DevOpsProject\\DevOpsProject.CommunicationControl.API.csproj",
+ "Action": "Start"
+ },
+ {
+ "Path": "DevOpsProject.Example.MessageListener\\DevOpsProject.Example.MessageListener.csproj",
+ "Action": "Start"
+ },
+ {
+ "Path": "DevOpsProject.HiveMind.API\\DevOpsProject.HiveMind.API.csproj",
+ "Action": "Start"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs b/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs
index 46219ca..0798b0d 100644
--- a/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs
+++ b/src/CommunicationControl/DevOpsProject/Controllers/ClientController.cs
@@ -1,4 +1,5 @@
-using DevOpsProject.CommunicationControl.API.DTO.Client.Request;
+using Asp.Versioning;
+using DevOpsProject.CommunicationControl.API.DTO.Client.Request;
using DevOpsProject.CommunicationControl.Logic.Services.Interfaces;
using DevOpsProject.Shared.Models;
using Microsoft.AspNetCore.Mvc;
@@ -6,22 +7,24 @@ using Microsoft.Extensions.Options;
namespace DevOpsProject.CommunicationControl.API.Controllers
{
-
+ [ApiVersion("1.0")]
[ApiController]
- [Route("api/client")]
+ [Route("api/v{version:apiVersion}/client")]
public class ClientController : Controller
{
private readonly ICommunicationControlService _communicationControlService;
private readonly IOptionsMonitor _operationalAreaConfig;
+ private readonly ILogger _logger;
- public ClientController(ICommunicationControlService communicationControlService, IOptionsMonitor operationalAreaConfig)
+ public ClientController(ICommunicationControlService communicationControlService, IOptionsMonitor operationalAreaConfig, ILogger logger)
{
_communicationControlService = communicationControlService;
_operationalAreaConfig = operationalAreaConfig;
+ _logger = logger;
}
[HttpGet("area")]
- public async Task GetOperationalArea()
+ public IActionResult GetOperationalArea()
{
return Ok(_operationalAreaConfig.CurrentValue);
}
@@ -51,16 +54,27 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
}
[HttpPatch("hive")]
- public async Task SendBulkHiveMovingSignal(MoveHivesRequest request)
+ public IActionResult SendBulkHiveMovingSignal(MoveHivesRequest request)
{
if (request?.Hives == null || !request.Hives.Any())
return BadRequest("No hive IDs provided.");
foreach (var id in request.Hives)
{
- Task.Run(async () => await _communicationControlService.SendHiveControlSignal(id, request.Destination));
+ _ = Task.Run(async () =>
+ {
+ try
+ {
+ await _communicationControlService.SendHiveControlSignal(id, request.Destination);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Failed to send control signal for HiveID: {id}");
+ }
+ });
}
+
return Accepted("Hives are being moved asynchronously.");
}
}
diff --git a/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs b/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs
index e16b0ce..4e4cd00 100644
--- a/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs
+++ b/src/CommunicationControl/DevOpsProject/Controllers/HiveController.cs
@@ -1,13 +1,13 @@
-using DevOpsProject.CommunicationControl.API.DTO.Hive.Request;
-using DevOpsProject.CommunicationControl.API.DTO.Hive.Response;
+using Asp.Versioning;
using DevOpsProject.CommunicationControl.Logic.Services.Interfaces;
using DevOpsProject.Shared.Models;
using Microsoft.AspNetCore.Mvc;
namespace DevOpsProject.CommunicationControl.API.Controllers
{
+ [ApiVersion("1.0")]
[ApiController]
- [Route("api/hive")]
+ [Route("api/v{version:apiVersion}/hive")]
public class HiveController : Controller
{
private readonly ICommunicationControlService _communicationControlService;
diff --git a/src/CommunicationControl/DevOpsProject/DTO/Hive/Response/HiveConnectResponse.cs b/src/CommunicationControl/DevOpsProject/DTO/Hive/Response/HiveConnectResponse.cs
deleted file mode 100644
index 5c79d8a..0000000
--- a/src/CommunicationControl/DevOpsProject/DTO/Hive/Response/HiveConnectResponse.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using DevOpsProject.Shared.Models;
-
-namespace DevOpsProject.CommunicationControl.API.DTO.Hive.Response
-{
- public class HiveConnectResponse
- {
- public bool ConnectResult { get; set; }
- public HiveOperationalArea OperationalArea { get;set; }
- }
-}
diff --git a/src/CommunicationControl/DevOpsProject/DevOpsProject.CommunicationControl.API.csproj b/src/CommunicationControl/DevOpsProject/DevOpsProject.CommunicationControl.API.csproj
index 43a5b2f..b4f5dd4 100644
--- a/src/CommunicationControl/DevOpsProject/DevOpsProject.CommunicationControl.API.csproj
+++ b/src/CommunicationControl/DevOpsProject/DevOpsProject.CommunicationControl.API.csproj
@@ -2,11 +2,20 @@
net8.0
- enable
+ disable
enable
+
+
+
+
+
+
+
+
+
@@ -22,8 +31,4 @@
-
-
-
-
diff --git a/src/CommunicationControl/DevOpsProject/Program.cs b/src/CommunicationControl/DevOpsProject/Program.cs
index 8076fb8..3b276c9 100644
--- a/src/CommunicationControl/DevOpsProject/Program.cs
+++ b/src/CommunicationControl/DevOpsProject/Program.cs
@@ -1,8 +1,11 @@
+using Asp.Versioning;
using DevOpsProject.CommunicationControl.API.DI;
using DevOpsProject.CommunicationControl.API.Middleware;
using DevOpsProject.Shared.Clients;
+using DevOpsProject.Shared.Configuration;
using DevOpsProject.Shared.Models;
using Microsoft.Extensions.Options;
+using Microsoft.OpenApi.Models;
using Polly;
using Polly.Extensions.Http;
using Serilog;
@@ -18,10 +21,31 @@ internal class Program
.ReadFrom.Services(services)
.Enrich.FromLogContext());
- builder.Services.AddControllers();
- // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+ builder.Services.AddApiVersioning(options =>
+ {
+ options.DefaultApiVersion = new ApiVersion(1, 0);
+ options.AssumeDefaultVersionWhenUnspecified = true;
+ options.ReportApiVersions = true;
+ options.ApiVersionReader = ApiVersionReader.Combine(
+ new UrlSegmentApiVersionReader(),
+ new HeaderApiVersionReader("X-Api-Version")
+ );
+ }).AddApiExplorer(options =>
+ {
+ options.GroupNameFormat = "'v'VVV";
+ options.SubstituteApiVersionInUrl = true;
+ });
+
+ // TODO: consider this approach
+ builder.Services.AddControllers().AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.PropertyNamingPolicy = null;
+ }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen();
+ builder.Services.AddSwaggerGen(c =>
+ {
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "CommunicationControl - V1", Version = "v1.0" });
+ });
// TODO: LATER - ADD OpenTelemtry
@@ -29,13 +53,12 @@ internal class Program
builder.Services.AddCommunicationControlLogic();
builder.Services.Configure(builder.Configuration.GetSection("OperationalArea"));
- builder.Services.AddSingleton, OptionsMonitor>();
-
+ builder.Services.Configure(builder.Configuration.GetSection("CommunicationConfiguration"));
var hiveRetryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
- builder.Services.AddHttpClient()
+ builder.Services.AddHttpClient()
.AddPolicyHandler(hiveRetryPolicy);
diff --git a/src/CommunicationControl/DevOpsProject/appsettings.json b/src/CommunicationControl/DevOpsProject/appsettings.json
index 0b17802..602dbc5 100644
--- a/src/CommunicationControl/DevOpsProject/appsettings.json
+++ b/src/CommunicationControl/DevOpsProject/appsettings.json
@@ -10,7 +10,10 @@
},
"WriteTo": [
{
- "Name": "Console"
+ "Name": "Console",
+ "Args": {
+ "restrictedToMinimumLevel": "Information"
+ }
},
{
"Name": "File",
@@ -18,7 +21,8 @@
"path": "Logs/log-.txt",
"rollingInterval": "Day",
"rollOnFileSizeLimit": true,
- "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
+ "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
+ "restrictedToMinimumLevel": "Warning"
}
}
],
@@ -45,6 +49,10 @@
"PingInterval_MS": 15000
},
+ "CommunicationConfiguration": {
+ "RequestScheme": "http",
+ "HiveMindPath": "api/v1"
+ },
"AllowedHosts": "*",
"Urls": "http://0.0.0.0:8080"
}
diff --git a/src/MapClient/.env b/src/MapClient/.env
new file mode 100644
index 0000000..59da698
--- /dev/null
+++ b/src/MapClient/.env
@@ -0,0 +1 @@
+VITE_API_BASE_URL=http://localhost:8080/api/v1/client
\ No newline at end of file
diff --git a/src/MapClient/src/api/mapService.js b/src/MapClient/src/api/mapService.js
index 63cc2a1..6f23e54 100644
--- a/src/MapClient/src/api/mapService.js
+++ b/src/MapClient/src/api/mapService.js
@@ -1,6 +1,6 @@
import axios from "axios";
-const API_BASE_URL = "http://localhost:8080/api/client";
+const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
// Fetch the center coordinates for the initial map load
export const fetchCenterCoordinates = async () => {
@@ -19,9 +19,9 @@ export const fetchHives = async () => {
const response = await axios.get(`${API_BASE_URL}/hive`);
return response.data.map(hive => ({
- id: hive.hiveID,
- lat: hive.telemetry?.location?.latitude ?? null,
- lon: hive.telemetry?.location?.longitude ?? null,
+ id: hive.HiveID,
+ lat: hive.Telemetry?.Location?.Latitude ?? null,
+ lon: hive.Telemetry?.Location?.Longitude ?? null,
})).filter(hive => hive.lat !== null && hive.lon !== null); // Remove invalid locations
} catch (error) {
diff --git a/src/MapClient/src/components/MapView.jsx b/src/MapClient/src/components/MapView.jsx
index 97de528..9a23e44 100644
--- a/src/MapClient/src/components/MapView.jsx
+++ b/src/MapClient/src/components/MapView.jsx
@@ -12,6 +12,8 @@ import { Style, Icon, Text, Fill, Stroke } from "ol/style";
import Popup from "./Popup";
import { fetchCenterCoordinates, fetchHives, moveHives } from "../api/mapService";
+// TEST STAGING
+
// TODO: Hardcoded marker icon path
const MARKER_ICON_URL = "/256x256.png";
@@ -32,7 +34,7 @@ const MapView = () => {
try {
const center = await fetchCenterCoordinates();
if (center) {
- initMap(center.latitude, center.longitude);
+ initMap(center.Latitude, center.Longitude);
await fetchAndDrawHives();
}