Merge branch 'Cleanup' into 'main'
Program.cs cleanup, HiveModel change, log level change See merge request kzotkin/hiveemulator!3
This commit is contained in:
commit
08f30fc7fc
|
@ -37,7 +37,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
|
||||||
bool isSuccessfullyDisconnected = false;
|
bool isSuccessfullyDisconnected = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await _redisService.DeleteAsync(hiveId);
|
var result = await _redisService.DeleteAsync(GetHiveKey(hiveId));
|
||||||
isSuccessfullyDisconnected = result;
|
isSuccessfullyDisconnected = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -65,9 +65,11 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
|
||||||
|
|
||||||
public async Task<HiveOperationalArea> ConnectHive(HiveModel model)
|
public async Task<HiveOperationalArea> ConnectHive(HiveModel model)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Trying to connect Hive: {@model}", model);
|
||||||
bool result = await _redisService.SetAsync(GetHiveKey(model.HiveID), model);
|
bool result = await _redisService.SetAsync(GetHiveKey(model.HiveID), model);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Successfully connected Hive: {@model}", model);
|
||||||
var operationalArea = _spatialService.GetHiveOperationalArea(model);
|
var operationalArea = _spatialService.GetHiveOperationalArea(model);
|
||||||
await _messageBus.Publish(new HiveConnectedMessage
|
await _messageBus.Publish(new HiveConnectedMessage
|
||||||
{
|
{
|
||||||
|
@ -80,27 +82,34 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _messageBus.Publish(new HiveConnectedMessage
|
_logger.LogError("Failed to connect Hive: {@model}", model);
|
||||||
{
|
|
||||||
HiveID = model.HiveID,
|
|
||||||
Hive = model,
|
|
||||||
IsSuccessfullyConnected = result
|
|
||||||
});
|
|
||||||
throw new HiveConnectionException($"Failed to connect hive for HiveId: {model.HiveID}");
|
throw new HiveConnectionException($"Failed to connect hive for HiveId: {model.HiveID}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsHiveConnected(string hiveId)
|
||||||
|
{
|
||||||
|
string hiveKey = GetHiveKey(hiveId);
|
||||||
|
return await _redisService.CheckIfKeyExists(hiveKey);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<DateTime> AddTelemetry(HiveTelemetryModel model)
|
public async Task<DateTime> AddTelemetry(HiveTelemetryModel model)
|
||||||
{
|
{
|
||||||
string hiveKey = GetHiveKey(model.HiveID);
|
string hiveKey = GetHiveKey(model.HiveID);
|
||||||
bool hiveExists = await _redisService.CheckIfKeyExists(hiveKey);
|
|
||||||
if (hiveExists)
|
|
||||||
{
|
|
||||||
bool result = await _redisService.UpdateAsync(hiveKey, (HiveModel hive) =>
|
bool result = await _redisService.UpdateAsync(hiveKey, (HiveModel hive) =>
|
||||||
{
|
{
|
||||||
hive.Telemetry = model;
|
hive.Telemetry = model;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Telemetry updated for HiveID: {hiveId}. Updated telemetry timestamp: {timestamp}", model.HiveID, model.Timestamp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Failed to update Telemetry - Redis update issue. HiveID: {hiveId}, Telemetry model: {@telemetry}", model.HiveID, model);
|
||||||
|
}
|
||||||
|
|
||||||
await _messageBus.Publish(new TelemetrySentMessage
|
await _messageBus.Publish(new TelemetrySentMessage
|
||||||
{
|
{
|
||||||
HiveID = model.HiveID,
|
HiveID = model.HiveID,
|
||||||
|
@ -109,44 +118,33 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
|
||||||
});
|
});
|
||||||
return model.Timestamp;
|
return model.Timestamp;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await _messageBus.Publish(new TelemetrySentMessage
|
|
||||||
{
|
|
||||||
HiveID = model.HiveID,
|
|
||||||
Telemetry = model,
|
|
||||||
IsSuccessfullySent = false
|
|
||||||
});
|
|
||||||
throw new HiveNotFoundException($"Hive not found for id: {model.HiveID}");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> SendHiveControlSignal(string hiveId, Location destination)
|
public async Task<string> SendHiveControlSignal(string hiveId, Location destination)
|
||||||
{
|
{
|
||||||
var hive = await GetHiveModel(hiveId);
|
var hive = await GetHiveModel(hiveId);
|
||||||
if (hive == null)
|
if (hive == null)
|
||||||
{
|
{
|
||||||
throw new Exception($"Hive control signal error: cannot find hive with id: {hiveId}");
|
_logger.LogError("Sending Hive Control signal: Hive not found for HiveID: {hiveId}", hiveId);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSuccessfullySent = false;
|
bool isSuccessfullySent = false;
|
||||||
|
string hiveMindPath = _communicationControlConfiguration.CurrentValue.HiveMindPath;
|
||||||
try
|
|
||||||
{
|
|
||||||
var command = new MoveHiveMindCommand
|
var command = new MoveHiveMindCommand
|
||||||
{
|
{
|
||||||
CommandType = State.Move,
|
CommandType = State.Move,
|
||||||
Location = destination,
|
Location = destination,
|
||||||
Timestamp = DateTime.Now
|
Timestamp = DateTime.Now
|
||||||
};
|
};
|
||||||
|
try
|
||||||
var result = await _hiveHttpClient.SendHiveControlCommandAsync(_communicationControlConfiguration.CurrentValue.RequestScheme,
|
{
|
||||||
hive.HiveIP, hive.HivePort, _communicationControlConfiguration.CurrentValue.HiveMindPath, command);
|
var result = await _hiveHttpClient.SendHiveControlCommandAsync(hive.HiveSchema, hive.HiveIP, hive.HivePort, hiveMindPath, command);
|
||||||
isSuccessfullySent = true;
|
isSuccessfullySent = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (isSuccessfullySent)
|
||||||
{
|
{
|
||||||
await _messageBus.Publish(new MoveHiveMessage
|
await _messageBus.Publish(new MoveHiveMessage
|
||||||
{
|
{
|
||||||
|
@ -155,6 +153,12 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
|
||||||
HiveID = hiveId
|
HiveID = hiveId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Failed to send control command for Hive: {@hive}, path: {path}, \n Command: {@command}", hive, hiveMindPath, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetHiveKey(string hiveId)
|
private string GetHiveKey(string hiveId)
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services.Interfaces
|
||||||
Task<HiveModel> GetHiveModel(string hiveId);
|
Task<HiveModel> GetHiveModel(string hiveId);
|
||||||
Task<List<HiveModel>> GetAllHives();
|
Task<List<HiveModel>> GetAllHives();
|
||||||
Task<HiveOperationalArea> ConnectHive(HiveModel model);
|
Task<HiveOperationalArea> ConnectHive(HiveModel model);
|
||||||
|
Task<bool> IsHiveConnected(string hiveId);
|
||||||
Task<DateTime> AddTelemetry(HiveTelemetryModel model);
|
Task<DateTime> AddTelemetry(HiveTelemetryModel model);
|
||||||
Task<string> SendHiveControlSignal(string hiveId, Location destination);
|
Task<string> SendHiveControlSignal(string hiveId, Location destination);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
using DevOpsProject.HiveMind.Logic.Services.Interfaces;
|
||||||
|
using DevOpsProject.HiveMind.Logic.Services;
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
|
namespace DevOpsProject.HiveMind.API.DI
|
||||||
|
{
|
||||||
|
public static class ApiVersioningConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddApiVersioningConfiguration(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
serviceCollection.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;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace DevOpsProject.HiveMind.API.DI
|
||||||
|
{
|
||||||
|
public static class CorsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddCorsConfiguration(this IServiceCollection serviceCollection, string corsPolicyName)
|
||||||
|
{
|
||||||
|
serviceCollection.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy(name: corsPolicyName,
|
||||||
|
policy =>
|
||||||
|
{
|
||||||
|
policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using DevOpsProject.Shared.Clients;
|
||||||
|
using Polly.Extensions.Http;
|
||||||
|
using Polly;
|
||||||
|
|
||||||
|
namespace DevOpsProject.HiveMind.API.DI
|
||||||
|
{
|
||||||
|
public static class HttpClientsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddHttpClientsConfiguration(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
var communicationControlTelemetryPolicy = HttpPolicyExtensions
|
||||||
|
.HandleTransientHttpError()
|
||||||
|
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
||||||
|
serviceCollection.AddHttpClient<HiveMindHttpClient>()
|
||||||
|
.AddPolicyHandler(communicationControlTelemetryPolicy);
|
||||||
|
serviceCollection.AddHttpClient("HiveConnectClient");
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using DevOpsProject.Shared.Configuration;
|
||||||
|
|
||||||
|
namespace DevOpsProject.HiveMind.API.DI
|
||||||
|
{
|
||||||
|
public static class OptionsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddOptionsConfiguration(this IServiceCollection serviceCollection, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
serviceCollection.Configure<HiveCommunicationConfig>(configuration.GetSection("CommunicationConfiguration"));
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,10 @@ using Asp.Versioning.Builder;
|
||||||
using DevOpsProject.HiveMind.API.DI;
|
using DevOpsProject.HiveMind.API.DI;
|
||||||
using DevOpsProject.HiveMind.API.Middleware;
|
using DevOpsProject.HiveMind.API.Middleware;
|
||||||
using DevOpsProject.HiveMind.Logic.Services.Interfaces;
|
using DevOpsProject.HiveMind.Logic.Services.Interfaces;
|
||||||
using DevOpsProject.Shared.Clients;
|
|
||||||
using DevOpsProject.Shared.Configuration;
|
using DevOpsProject.Shared.Configuration;
|
||||||
using DevOpsProject.Shared.Models;
|
using DevOpsProject.Shared.Models;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Polly;
|
|
||||||
using Polly.Extensions.Http;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
@ -19,52 +16,23 @@ builder.Host.UseSerilog((context, services, loggerConfig) =>
|
||||||
.ReadFrom.Services(services)
|
.ReadFrom.Services(services)
|
||||||
.Enrich.FromLogContext());
|
.Enrich.FromLogContext());
|
||||||
|
|
||||||
builder.Services.AddApiVersioning(options =>
|
builder.Services.AddApiVersioningConfiguration();
|
||||||
{
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddAuthorization();
|
builder.Services.AddAuthorization();
|
||||||
builder.Services.AddSwaggerGen(c =>
|
builder.Services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HiveMind - V1", Version = "v1.0" });
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HiveMind - V1", Version = "v1.0" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddOptionsConfiguration(builder.Configuration);
|
||||||
|
|
||||||
builder.Services.AddHiveMindLogic();
|
builder.Services.AddHiveMindLogic();
|
||||||
|
|
||||||
builder.Services.Configure<HiveCommunicationConfig>(builder.Configuration.GetSection("CommunicationConfiguration"));
|
builder.Services.AddHttpClientsConfiguration();
|
||||||
|
|
||||||
var communicationControlTelemetryPolicy = HttpPolicyExtensions
|
|
||||||
.HandleTransientHttpError()
|
|
||||||
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
|
||||||
builder.Services.AddHttpClient<HiveMindHttpClient>()
|
|
||||||
.AddPolicyHandler(communicationControlTelemetryPolicy);
|
|
||||||
|
|
||||||
// register NAMED client for connect request
|
|
||||||
builder.Services.AddHttpClient("HiveConnectClient");
|
|
||||||
|
|
||||||
string corsPolicyName = "HiveMindCorsPolicy";
|
string corsPolicyName = "HiveMindCorsPolicy";
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCorsConfiguration(corsPolicyName);
|
||||||
{
|
|
||||||
options.AddPolicy(name: corsPolicyName,
|
|
||||||
policy =>
|
|
||||||
{
|
|
||||||
policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddExceptionHandler<ExceptionHandlingMiddleware>();
|
builder.Services.AddExceptionHandler<ExceptionHandlingMiddleware>();
|
||||||
builder.Services.AddProblemDetails();
|
builder.Services.AddProblemDetails();
|
||||||
|
@ -82,7 +50,7 @@ using (var scope = app.Services.CreateScope())
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError($"Error occured while connecting Hive to Communication Control. \nException text: {ex.Message}");
|
logger.LogError($"Error occured while connecting Hive to Communication Control. \nException text: {ex.Message}");
|
||||||
System.Diagnostics.Process.GetCurrentProcess().Kill();
|
Environment.Exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +73,6 @@ ApiVersionSet apiVersionSet = app.NewApiVersionSet()
|
||||||
.HasApiVersion(new ApiVersion(1))
|
.HasApiVersion(new ApiVersion(1))
|
||||||
.ReportApiVersions()
|
.ReportApiVersions()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
RouteGroupBuilder groupBuilder = app.MapGroup("api/v{apiVersion:apiVersion}").WithApiVersionSet(apiVersionSet);
|
RouteGroupBuilder groupBuilder = app.MapGroup("api/v{apiVersion:apiVersion}").WithApiVersionSet(apiVersionSet);
|
||||||
|
|
||||||
groupBuilder.MapGet("ping", (IOptionsSnapshot<HiveCommunicationConfig> config) =>
|
groupBuilder.MapGet("ping", (IOptionsSnapshot<HiveCommunicationConfig> config) =>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
"rollingInterval": "Day",
|
"rollingInterval": "Day",
|
||||||
"rollOnFileSizeLimit": true,
|
"rollOnFileSizeLimit": true,
|
||||||
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
|
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
|
||||||
"restrictedToMinimumLevel": "Warning"
|
"restrictedToMinimumLevel": "Information"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
// If already moving - stop movement
|
// If already moving - stop movement
|
||||||
if (HiveInMemoryState.IsMoving)
|
if (HiveInMemoryState.IsMoving)
|
||||||
{
|
{
|
||||||
|
_logger.LogWarning("Previous movement command terminated. Previous destination: {@destination}, Current Location: {@current}, new destination: {@destination}", HiveInMemoryState.Destination, HiveInMemoryState.CurrentLocation, destination);
|
||||||
StopMovement();
|
StopMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,8 +40,9 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
if (_movementTimer == null)
|
if (_movementTimer == null)
|
||||||
{
|
{
|
||||||
// TODO: Recalculating position each N seconds
|
// TODO: Recalculating position each N seconds
|
||||||
_movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(3));
|
int intervalFromSeconds = 3;
|
||||||
_logger.LogInformation("Movement timer started.");
|
_movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(intervalFromSeconds));
|
||||||
|
_logger.LogInformation("Movement timer started. Destination: {@destination}, recalculation interval: {interval}", destination, intervalFromSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,14 +62,13 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
|
|
||||||
if (AreLocationsEqual(currentLocation.Value, destination.Value))
|
if (AreLocationsEqual(currentLocation.Value, destination.Value))
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Reached destination. Current location: {@currentLocation}, Destination: {@destination}", currentLocation, destination);
|
||||||
StopMovement();
|
StopMovement();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Location newLocation = CalculateNextPosition(currentLocation.Value, destination.Value, 0.1f);
|
Location newLocation = CalculateNextPosition(currentLocation.Value, destination.Value, 0.1f);
|
||||||
HiveInMemoryState.CurrentLocation = newLocation;
|
HiveInMemoryState.CurrentLocation = newLocation;
|
||||||
|
|
||||||
_logger.LogInformation($"Moved closer: {newLocation}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +78,6 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
_movementTimer = null;
|
_movementTimer = null;
|
||||||
HiveInMemoryState.IsMoving = false;
|
HiveInMemoryState.IsMoving = false;
|
||||||
HiveInMemoryState.Destination = null;
|
HiveInMemoryState.Destination = null;
|
||||||
_logger.LogInformation("Movement stopped: Reached destination.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool AreLocationsEqual(Location loc1, Location loc2)
|
private static bool AreLocationsEqual(Location loc1, Location loc2)
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
{
|
{
|
||||||
var request = new HiveConnectRequest
|
var request = new HiveConnectRequest
|
||||||
{
|
{
|
||||||
|
HiveSchema = _communicationConfigurationOptions.RequestSchema,
|
||||||
HiveIP = _communicationConfigurationOptions.HiveIP,
|
HiveIP = _communicationConfigurationOptions.HiveIP,
|
||||||
HivePort = _communicationConfigurationOptions.HivePort,
|
HivePort = _communicationConfigurationOptions.HivePort,
|
||||||
HiveID = _communicationConfigurationOptions.HiveID
|
HiveID = _communicationConfigurationOptions.HiveID
|
||||||
|
@ -47,6 +48,8 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
};
|
};
|
||||||
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
|
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
_logger.LogInformation("Attempting to connect Hive. Request: {@request}, URI: {uri}", request, uriBuilder.Uri);
|
||||||
|
|
||||||
var retryPolicy = Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
|
var retryPolicy = Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
|
||||||
.WaitAndRetryAsync(
|
.WaitAndRetryAsync(
|
||||||
10,
|
10,
|
||||||
|
@ -68,7 +71,6 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
HiveInMemoryState.OperationalArea = hiveConnectResponse.OperationalArea;
|
HiveInMemoryState.OperationalArea = hiveConnectResponse.OperationalArea;
|
||||||
HiveInMemoryState.CurrentLocation = _communicationConfigurationOptions.InitialLocation;
|
HiveInMemoryState.CurrentLocation = _communicationConfigurationOptions.InitialLocation;
|
||||||
|
|
||||||
// HERE - we are starting to send telemetry
|
|
||||||
StartTelemetry();
|
StartTelemetry();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -80,7 +82,7 @@ namespace DevOpsProject.HiveMind.Logic.Services
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError($"Failed to connect hive, terminating process");
|
_logger.LogError($"Failed to connect hive, terminating process");
|
||||||
System.Diagnostics.Process.GetCurrentProcess().Kill();
|
Environment.Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
{
|
{
|
||||||
public class ComControlCommunicationConfiguration
|
public class ComControlCommunicationConfiguration
|
||||||
{
|
{
|
||||||
public string RequestScheme { get; set; }
|
|
||||||
public string HiveMindPath { get; set; }
|
public string HiveMindPath { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
namespace DevOpsProject.Shared.Exceptions
|
|
||||||
{
|
|
||||||
public class HiveNotFoundException : Exception
|
|
||||||
{
|
|
||||||
public HiveNotFoundException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HiveNotFoundException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HiveNotFoundException(string message, Exception inner)
|
|
||||||
: base(message, inner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,8 @@
|
||||||
using System;
|
namespace DevOpsProject.Shared.Models
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace DevOpsProject.Shared.Models
|
|
||||||
{
|
{
|
||||||
public class HiveConnectRequest
|
public class HiveConnectRequest
|
||||||
{
|
{
|
||||||
|
public string HiveSchema { get; set; }
|
||||||
public string HiveIP { get; set; }
|
public string HiveIP { get; set; }
|
||||||
public int HivePort { get; set; }
|
public int HivePort { get; set; }
|
||||||
public string HiveID { get; set; }
|
public string HiveID { get; set; }
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
public string HiveID { get; set; }
|
public string HiveID { get; set; }
|
||||||
public string HiveIP { get; set; }
|
public string HiveIP { get; set; }
|
||||||
public int HivePort { get; set; }
|
public int HivePort { get; set; }
|
||||||
|
public string HiveSchema { get; set; }
|
||||||
public HiveTelemetryModel Telemetry { get; set; }
|
public HiveTelemetryModel Telemetry { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,17 +32,21 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
[HttpGet("hive/{hiveId}")]
|
[HttpGet("hive/{hiveId}")]
|
||||||
public async Task<IActionResult> GetHive(string hiveId)
|
public async Task<IActionResult> GetHive(string hiveId)
|
||||||
{
|
{
|
||||||
var hiveModel = await _communicationControlService.GetHiveModel(hiveId);
|
var hiveExists = await _communicationControlService.IsHiveConnected(hiveId);
|
||||||
|
if (!hiveExists)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to get Hive for HiveID: {hiveId}", hiveId);
|
||||||
|
return NotFound($"Hive with HiveID: {hiveId} is not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
var hiveModel = await _communicationControlService.GetHiveModel(hiveId);
|
||||||
return Ok(hiveModel);
|
return Ok(hiveModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("hive")]
|
[HttpGet("hive")]
|
||||||
public async Task<IActionResult> GetHives()
|
public async Task<IActionResult> GetHives()
|
||||||
{
|
{
|
||||||
|
|
||||||
var hives = await _communicationControlService.GetAllHives();
|
var hives = await _communicationControlService.GetAllHives();
|
||||||
|
|
||||||
return Ok(hives);
|
return Ok(hives);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +63,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
if (request?.Hives == null || !request.Hives.Any())
|
if (request?.Hives == null || !request.Hives.Any())
|
||||||
return BadRequest("No hive IDs provided.");
|
return BadRequest("No hive IDs provided.");
|
||||||
|
|
||||||
|
_logger.LogInformation("Hive moving request accepted by enpdoint. Request: {@request}", request);
|
||||||
foreach (var id in request.Hives)
|
foreach (var id in request.Hives)
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
|
@ -69,7 +74,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, $"Failed to send control signal for HiveID: {id}");
|
_logger.LogError(ex, "Failed to send control signal for HiveID: {id} \n Request: {@request}", id, request);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,12 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
public class HiveController : Controller
|
public class HiveController : Controller
|
||||||
{
|
{
|
||||||
private readonly ICommunicationControlService _communicationControlService;
|
private readonly ICommunicationControlService _communicationControlService;
|
||||||
|
private readonly ILogger<HiveController> _logger;
|
||||||
|
|
||||||
public HiveController(ICommunicationControlService communicationControlService)
|
public HiveController(ICommunicationControlService communicationControlService, ILogger<HiveController> logger)
|
||||||
{
|
{
|
||||||
_communicationControlService = communicationControlService;
|
_communicationControlService = communicationControlService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("connect")]
|
[HttpPost("connect")]
|
||||||
|
@ -25,8 +27,16 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
HiveID = request.HiveID,
|
HiveID = request.HiveID,
|
||||||
HiveIP = request.HiveIP,
|
HiveIP = request.HiveIP,
|
||||||
HivePort = request.HivePort,
|
HivePort = request.HivePort,
|
||||||
|
HiveSchema = request.HiveSchema
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool isConnected = await _communicationControlService.IsHiveConnected(request.HiveID);
|
||||||
|
if (isConnected)
|
||||||
|
{
|
||||||
|
_logger.LogError("Hive with HiveID: {hiveId} already connected. Request: {@request}", request.HiveID, request);
|
||||||
|
return BadRequest($"Hive with HiveID: {request.HiveID} already connected");
|
||||||
|
}
|
||||||
|
|
||||||
var hiveOperationalArea = await _communicationControlService.ConnectHive(hiveModel);
|
var hiveOperationalArea = await _communicationControlService.ConnectHive(hiveModel);
|
||||||
var connectResponse = new HiveConnectResponse
|
var connectResponse = new HiveConnectResponse
|
||||||
{
|
{
|
||||||
|
@ -35,6 +45,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(connectResponse);
|
return Ok(connectResponse);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("telemetry")]
|
[HttpPost("telemetry")]
|
||||||
|
@ -50,6 +61,9 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
Timestamp = DateTime.Now
|
Timestamp = DateTime.Now
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool isHiveConnected = await _communicationControlService.IsHiveConnected(request.HiveID);
|
||||||
|
if (isHiveConnected)
|
||||||
|
{
|
||||||
var telemetryUpdateTimestamp = await _communicationControlService.AddTelemetry(hiveTelemetryModel);
|
var telemetryUpdateTimestamp = await _communicationControlService.AddTelemetry(hiveTelemetryModel);
|
||||||
var telemetryResponse = new HiveTelemetryResponse
|
var telemetryResponse = new HiveTelemetryResponse
|
||||||
{
|
{
|
||||||
|
@ -58,6 +72,12 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
|
||||||
|
|
||||||
return Ok(telemetryResponse);
|
return Ok(telemetryResponse);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to write telemetry. Hive with HiveID: {hiveId} is not connected. Request: {@request}", request.HiveID, request);
|
||||||
|
return NotFound($"Failed to write telemetry. Hive with HiveID: {request.HiveID} is not connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Asp.Versioning;
|
||||||
|
|
||||||
|
namespace DevOpsProject.CommunicationControl.API.DI
|
||||||
|
{
|
||||||
|
public static class ApiVersioningConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddApiVersioningConfiguration(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
serviceCollection.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;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace DevOpsProject.CommunicationControl.API.DI
|
||||||
|
{
|
||||||
|
public static class CorsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddCorsConfiguration(this IServiceCollection serviceCollection, string corsPolicyName)
|
||||||
|
{
|
||||||
|
serviceCollection.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy(name: corsPolicyName,
|
||||||
|
policy =>
|
||||||
|
{
|
||||||
|
policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
using DevOpsProject.Shared.Clients;
|
||||||
|
using Polly;
|
||||||
|
using Polly.Extensions.Http;
|
||||||
|
|
||||||
|
namespace DevOpsProject.CommunicationControl.API.DI
|
||||||
|
{
|
||||||
|
public static class HttpClientsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddHttpClientsConfiguration(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
var hiveRetryPolicy = HttpPolicyExtensions
|
||||||
|
.HandleTransientHttpError()
|
||||||
|
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
||||||
|
serviceCollection.AddHttpClient<CommunicationControlHttpClient>()
|
||||||
|
.AddPolicyHandler(hiveRetryPolicy);
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace DevOpsProject.CommunicationControl.API.DI
|
||||||
|
{
|
||||||
|
public static class JsonControllerOptionsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddJsonControllerOptionsConfiguration(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
serviceCollection.AddControllers().AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.JsonSerializerOptions.PropertyNamingPolicy = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,5 @@ namespace DevOpsProject.CommunicationControl.API.DI
|
||||||
|
|
||||||
return serviceCollection;
|
return serviceCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
using DevOpsProject.Shared.Configuration;
|
||||||
|
using DevOpsProject.Shared.Models;
|
||||||
|
|
||||||
|
namespace DevOpsProject.CommunicationControl.API.DI
|
||||||
|
{
|
||||||
|
public static class OptionsConfiguration
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddOptionsConfiguration(this IServiceCollection serviceCollection, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
serviceCollection.Configure<OperationalAreaConfig>(configuration.GetSection("OperationalArea"));
|
||||||
|
serviceCollection.Configure<ComControlCommunicationConfiguration>(configuration.GetSection("CommunicationConfiguration"));
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,6 @@
|
||||||
using Asp.Versioning;
|
|
||||||
using DevOpsProject.CommunicationControl.API.DI;
|
using DevOpsProject.CommunicationControl.API.DI;
|
||||||
using DevOpsProject.CommunicationControl.API.Middleware;
|
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 Microsoft.OpenApi.Models;
|
||||||
using Polly;
|
|
||||||
using Polly.Extensions.Http;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
|
@ -21,26 +14,11 @@ internal class Program
|
||||||
.ReadFrom.Services(services)
|
.ReadFrom.Services(services)
|
||||||
.Enrich.FromLogContext());
|
.Enrich.FromLogContext());
|
||||||
|
|
||||||
builder.Services.AddApiVersioning(options =>
|
builder.Services.AddApiVersioningConfiguration();
|
||||||
{
|
|
||||||
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
|
// TODO: consider this approach
|
||||||
builder.Services.AddControllers().AddJsonOptions(options =>
|
builder.Services.AddJsonControllerOptionsConfiguration();
|
||||||
{
|
|
||||||
options.JsonSerializerOptions.PropertyNamingPolicy = null;
|
|
||||||
}); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen(c =>
|
builder.Services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
|
@ -52,36 +30,12 @@ internal class Program
|
||||||
builder.Services.AddRedis(builder.Configuration);
|
builder.Services.AddRedis(builder.Configuration);
|
||||||
builder.Services.AddCommunicationControlLogic();
|
builder.Services.AddCommunicationControlLogic();
|
||||||
|
|
||||||
builder.Services.Configure<OperationalAreaConfig>(builder.Configuration.GetSection("OperationalArea"));
|
builder.Services.AddOptionsConfiguration(builder.Configuration);
|
||||||
builder.Services.Configure<ComControlCommunicationConfiguration>(builder.Configuration.GetSection("CommunicationConfiguration"));
|
|
||||||
|
|
||||||
var hiveRetryPolicy = HttpPolicyExtensions
|
|
||||||
.HandleTransientHttpError()
|
|
||||||
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
|
||||||
builder.Services.AddHttpClient<CommunicationControlHttpClient>()
|
|
||||||
.AddPolicyHandler(hiveRetryPolicy);
|
|
||||||
|
|
||||||
|
builder.Services.AddHttpClientsConfiguration();
|
||||||
|
|
||||||
var corsPolicyName = "AllowReactApp";
|
var corsPolicyName = "AllowReactApp";
|
||||||
var localCorsPolicyName = "AllowLocalHtml";
|
builder.Services.AddCorsConfiguration(corsPolicyName);
|
||||||
builder.Services.AddCors(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy(name: corsPolicyName,
|
|
||||||
policy =>
|
|
||||||
{
|
|
||||||
policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader();
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddPolicy(name: localCorsPolicyName,
|
|
||||||
policy =>
|
|
||||||
{
|
|
||||||
policy.AllowAnyOrigin() //SECURITY WARNING ! Never allow all origins
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddExceptionHandler<ExceptionHandlingMiddleware>();
|
builder.Services.AddExceptionHandler<ExceptionHandlingMiddleware>();
|
||||||
builder.Services.AddProblemDetails();
|
builder.Services.AddProblemDetails();
|
||||||
|
@ -97,7 +51,6 @@ internal class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseCors(corsPolicyName);
|
app.UseCors(corsPolicyName);
|
||||||
//app.UseCors(localCorsPolicyName);
|
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,8 @@
|
||||||
"InitialSpeed_KM": 5,
|
"InitialSpeed_KM": 5,
|
||||||
"TelemetryInterval_MS": 30000,
|
"TelemetryInterval_MS": 30000,
|
||||||
"PingInterval_MS": 15000
|
"PingInterval_MS": 15000
|
||||||
|
|
||||||
},
|
},
|
||||||
"CommunicationConfiguration": {
|
"CommunicationConfiguration": {
|
||||||
"RequestScheme": "http",
|
|
||||||
"HiveMindPath": "api/v1"
|
"HiveMindPath": "api/v1"
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
@ -45,7 +43,7 @@
|
||||||
"rollingInterval": "Day",
|
"rollingInterval": "Day",
|
||||||
"rollOnFileSizeLimit": true,
|
"rollOnFileSizeLimit": true,
|
||||||
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
|
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
|
||||||
"restrictedToMinimumLevel": "Warning"
|
"restrictedToMinimumLevel": "Information"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue