Compare commits

..

No commits in common. "d4b830096f8c013d1bf4df810abcc1e40225570a" and "21a6a22a9e3d8f0731be468d3b6ac59c9536b9a0" have entirely different histories.

21 changed files with 95 additions and 378 deletions

View File

@ -1,69 +0,0 @@
# dotnet build environment
FROM alpine:latest as dotnet
WORKDIR /
RUN apk add dotnet8-sdk git
RUN git clone https://gitlab.com/kzotkin/hiveemulator
WORKDIR /hiveemulator/src/CommunicationControl/
RUN dotnet build DevOpsProject/DevOpsProject.CommunicationControl.API.csproj
RUN dotnet build DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj
# nodejs build environment
#FROM alpine:latest as nodejs
#WORKDIR /
#RUN apk add git npm
#RUN git clone https://gitlab.com/kzotkin/hiveemulator
#WORKDIR /hiveemulator/src/MapClient/
#RUN npm install
#RUN npm run build
# production environment
FROM alpine:latest
WORKDIR /
#RUN apk add dotnet8-sdk redis openrc lighttpd npm
#RUN apk add git dotnet8-sdk redis openrc npm
#RUN apk add git aspnetcore8-runtime redis openrc npm
RUN apk add aspnetcore8-runtime redis openrc npm
#RUN git clone https://gitlab.com/kzotkin/hiveemulator
#COPY --from=nodejs /hiveemulator/ /hiveemulator/
COPY --from=dotnet /hiveemulator/ /hiveemulator/
#COPY --from=nodejs /hiveemulator/src/MapClient/dist/* /var/www/localhost/htdocs/
#RUN mkdir /hive-cc
#COPY --from=0 /hiveemulator/src/CommunicationControl/DevOpsProject/* /hive-cc/
#COPY --from=dotnet /hiveemulator/src/CommunicationControl/DevOpsProject/* /hiveemulator/src/CommunicationControl/DevOpsProject/
#COPY --from=dotnet /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/* /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/
#RUN mkdir /hive-hm
#COPY --from=0 /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/* /hive-hm/
WORKDIR /hiveemulator/src/MapClient/
RUN npm install
RUN sed -i 's/localhost/10\.1\.1\.2/' public/config.json
WORKDIR /
COPY ./daemon-files/hive-cc /etc/init.d/hive-cc
COPY ./daemon-files/hive-hm /etc/init.d/hive-hm
COPY ./daemon-files/hive-map /etc/init.d/hive-map
RUN chmod u+x /etc/init.d/hive-hm /etc/init.d/hive-cc /etc/init.d/hive-map
#RUN mkdir /etc/rulevels/{stage-redis,stage-cc,stage-hm,stage-map}
#RUN rc-update add redis default
#RUN rc-update add hive-hm default
#RUN rc-update add hive-cc default
#RUN rc-update add hive-map default
CMD sh -c "openrc default ; \
rc-service redis start ; \
rc-service hive-cc start ; \
rc-service hive-hm start ; \
rc-service hive-map start ; \
exec sh"

View File

@ -1,24 +0,0 @@
# dotnet build environment
FROM alpine:latest as dotnet
WORKDIR /
RUN apk add git
RUN git clone https://gitlab.com/kzotkin/hiveemulator
RUN apk add dotnet8-sdk
WORKDIR /hiveemulator/src/CommunicationControl/
RUN dotnet publish DevOpsProject/DevOpsProject.CommunicationControl.API.csproj
# production environment
#FROM mcr.microsoft.com/dotnet/aspnet:8.0 as prod
FROM alpine:latest as prod
#COPY --from=dotnet /hiveemulator/src/CommunicationControl/DevOpsProject/bin/Release/net8.0/ /app/
RUN apk add aspnetcore8-runtime
RUN mkdir -p /hiveemulator/src/CommunicationControl/DevOpsProject/bin/Release/net8.0/
COPY --from=dotnet /hiveemulator/src/CommunicationControl/DevOpsProject/bin/Release/net8.0/ /hiveemulator/src/CommunicationControl/DevOpsProject/bin/Release/net8.0/
EXPOSE 8080
WORKDIR /hiveemulator/src/CommunicationControl/DevOpsProject/bin/Release/net8.0/
ENTRYPOINT ["./DevOpsProject.CommunicationControl.API"]

View File

@ -1,23 +0,0 @@
# dotnet build environment
FROM alpine:latest as dotnet
WORKDIR /
RUN apk add git
RUN git clone https://gitlab.com/kzotkin/hiveemulator
RUN apk add dotnet8-sdk
WORKDIR /hiveemulator/src/CommunicationControl/
RUN dotnet publish DevOpsProject.HiveMind.API/DevOpsProject.HiveMind.API.csproj
# production environment
#FROM mcr.microsoft.com/dotnet/aspnet:8.0 as prod
FROM alpine:latest as prod
RUN apk add aspnetcore8-runtime
RUN mkdir -p /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/bin/Release/net8.0/
#COPY --from=dotnet /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/bin/Release/net8.0/ /app/
COPY --from=dotnet /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/bin/Release/net8.0/ /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/bin/Release/net8.0/
WORKDIR /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/bin/Release/net8.0/
ENTRYPOINT ["./DevOpsProject.HiveMind.API"]

View File

@ -1,22 +0,0 @@
# nodejs build environment
FROM alpine:latest as nodejs
WORKDIR /
RUN apk add git
RUN git clone https://gitlab.com/kzotkin/hiveemulator
RUN apk add npm
WORKDIR /hiveemulator/src/MapClient/
RUN npm install
RUN npm run build
RUN rm public/config.json
# production environment
FROM nginx:alpine
COPY --from=nodejs /hiveemulator/src/MapClient/dist/ /usr/share/nginx/html/
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]

View File

@ -1,14 +0,0 @@
#!/sbin/openrc-run
command="./DevOpsProject.CommunicationControl.API"
command_args=""
pidfile="/run/hive-cc.pid"
supervisor="supervise-daemon"
start_pre() {
cd /hiveemulator/src/CommunicationControl/DevOpsProject/bin/Debug/net8.0/
}
depend() {
need redis
}

View File

@ -1,14 +0,0 @@
#!/sbin/openrc-run
command="./DevOpsProject.HiveMind.API"
command_args=""
pidfile="/run/hive-hm.pid"
supervisor="supervise-daemon"
start_pre() {
cd /hiveemulator/src/CommunicationControl/DevOpsProject.HiveMind.API/bin/Debug/net8.0/
}
depend() {
need hive-cc
}

View File

@ -1,14 +0,0 @@
#!/sbin/openrc-run
command="npm"
command_args="run dev"
pidfile="/run/hive-map.pid"
supervisor="supervise-daemon"
depend() {
need hive-cc
}
start_pre() {
cd /hiveemulator/src/MapClient/
}

View File

@ -1,50 +0,0 @@
services:
redis:
image: redis:7
restart: always
networks:
- sys
cc:
build:
context: .
dockerfile: Dockerfile-cc
#image: registry.digitalocean.com/duke-listings/cc
restart: always
environment:
Redis__ConnectionString: "redis:6379"
ports:
- 8080:8080
networks:
- sys
depends_on:
- redis
hm:
build:
context: .
dockerfile: Dockerfile-hm
#image: registry.digitalocean.com/duke-listings/hm
restart: always
environment:
CommunicationConfiguration__CommunicationControlIP: "cc"
networks:
- sys
depends_on:
- cc
map:
build:
context: .
dockerfile: Dockerfile-map
#image: registry.digitalocean.com/duke-listings/map
restart: always
volumes:
- ./map/config.json:/usr/share/nginx/html/config.json:ro
ports:
- 80:80
networks:
- sys
networks:
sys:

View File

@ -1,3 +0,0 @@
{
"API": "http://10.1.1.2:8080/api/v1/client"
}

View File

@ -1,4 +1,4 @@
using DevOpsProject.CommunicationControl.Logic.Services.Interfaces;
using DevOpsProject.CommunicationControl.Logic.Services.Interfaces;
using DevOpsProject.Shared.Clients;
using DevOpsProject.Shared.Configuration;
using DevOpsProject.Shared.Enums;
@ -37,7 +37,7 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
bool isSuccessfullyDisconnected = false;
try
{
var result = await _redisService.DeleteAsync(GetHiveKey(hiveId));
var result = await _redisService.DeleteAsync(hiveId);
isSuccessfullyDisconnected = result;
return result;
}
@ -65,32 +65,10 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
public async Task<HiveOperationalArea> ConnectHive(HiveModel model)
{
bool isHiveAlreadyConnected = await IsHiveConnected(model.HiveID);
if (isHiveAlreadyConnected)
{
_logger.LogWarning("Reconnect Hive request: {@model}", model);
}
else
{
_logger.LogInformation("Trying to connect Hive: {@model}", model);
}
bool result = await _redisService.SetAsync(GetHiveKey(model.HiveID), model);
if (result)
{
_logger.LogInformation("Successfully connected Hive: {@model}", model);
var operationalArea = _spatialService.GetHiveOperationalArea(model);
if (isHiveAlreadyConnected)
{
await _messageBus.Publish(new HiveReconnectedMessage
{
HiveID = model.HiveID,
Hive = model,
InitialOperationalArea = operationalArea,
IsSuccessfullyReconnected = result
});
}
else
{
await _messageBus.Publish(new HiveConnectedMessage
{
HiveID = model.HiveID,
@ -98,39 +76,31 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
InitialOperationalArea = operationalArea,
IsSuccessfullyConnected = result
});
}
return operationalArea;
}
else
{
_logger.LogError("Failed to connect Hive: {@model}", model);
await _messageBus.Publish(new HiveConnectedMessage
{
HiveID = model.HiveID,
Hive = model,
IsSuccessfullyConnected = result
});
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)
{
string hiveKey = GetHiveKey(model.HiveID);
bool hiveExists = await _redisService.CheckIfKeyExists(hiveKey);
if (hiveExists)
{
bool result = await _redisService.UpdateAsync(hiveKey, (HiveModel hive) =>
{
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
{
HiveID = model.HiveID,
@ -139,33 +109,44 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
});
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)
{
var hive = await GetHiveModel(hiveId);
if (hive == null)
{
_logger.LogError("Sending Hive Control signal: Hive not found for HiveID: {hiveId}", hiveId);
return null;
throw new Exception($"Hive control signal error: cannot find hive with id: {hiveId}");
}
bool isSuccessfullySent = false;
string hiveMindPath = _communicationControlConfiguration.CurrentValue.HiveMindPath;
try
{
var command = new MoveHiveMindCommand
{
CommandType = State.Move,
Location = destination,
Timestamp = DateTime.Now
};
try
{
var result = await _hiveHttpClient.SendHiveControlCommandAsync(hive.HiveSchema, hive.HiveIP, hive.HivePort, hiveMindPath, command);
var result = await _hiveHttpClient.SendHiveControlCommandAsync(hive.HiveSchema, hive.HiveIP, hive.HivePort,
_communicationControlConfiguration.CurrentValue.HiveMindPath, command);
isSuccessfullySent = true;
return result;
}
finally
{
if (isSuccessfullySent)
{
await _messageBus.Publish(new MoveHiveMessage
{
@ -174,12 +155,6 @@ namespace DevOpsProject.CommunicationControl.Logic.Services
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)

View File

@ -8,7 +8,6 @@ namespace DevOpsProject.CommunicationControl.Logic.Services.Interfaces
Task<HiveModel> GetHiveModel(string hiveId);
Task<List<HiveModel>> GetAllHives();
Task<HiveOperationalArea> ConnectHive(HiveModel model);
Task<bool> IsHiveConnected(string hiveId);
Task<DateTime> AddTelemetry(HiveTelemetryModel model);
Task<string> SendHiveControlSignal(string hiveId, Location destination);
}

View File

@ -27,7 +27,6 @@ namespace DevOpsProject.HiveMind.Logic.Services
// If already moving - stop movement
if (HiveInMemoryState.IsMoving)
{
_logger.LogWarning("Previous movement command terminated. Previous destination: {@destination}, Current Location: {@current}, new destination: {@destination}", HiveInMemoryState.Destination, HiveInMemoryState.CurrentLocation, destination);
StopMovement();
}
@ -40,9 +39,8 @@ namespace DevOpsProject.HiveMind.Logic.Services
if (_movementTimer == null)
{
// TODO: Recalculating position each N seconds
int intervalFromSeconds = 3;
_movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(intervalFromSeconds));
_logger.LogInformation("Movement timer started. Destination: {@destination}, recalculation interval: {interval}", destination, intervalFromSeconds);
_movementTimer = new Timer(UpdateMovement, null, TimeSpan.Zero, TimeSpan.FromSeconds(3));
_logger.LogInformation("Movement timer started.");
}
}
}
@ -62,13 +60,14 @@ namespace DevOpsProject.HiveMind.Logic.Services
if (AreLocationsEqual(currentLocation.Value, destination.Value))
{
_logger.LogInformation("Reached destination. Current location: {@currentLocation}, Destination: {@destination}", currentLocation, destination);
StopMovement();
return;
}
Location newLocation = CalculateNextPosition(currentLocation.Value, destination.Value, 0.1f);
HiveInMemoryState.CurrentLocation = newLocation;
_logger.LogInformation($"Moved closer: {newLocation}");
}
}
@ -78,6 +77,7 @@ namespace DevOpsProject.HiveMind.Logic.Services
_movementTimer = null;
HiveInMemoryState.IsMoving = false;
HiveInMemoryState.Destination = null;
_logger.LogInformation("Movement stopped: Reached destination.");
}
private static bool AreLocationsEqual(Location loc1, Location loc2)

View File

@ -48,8 +48,6 @@ namespace DevOpsProject.HiveMind.Logic.Services
};
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)
.WaitAndRetryAsync(
10,

View File

@ -0,0 +1,19 @@
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)
{
}
}
}

View File

@ -1,16 +0,0 @@
using DevOpsProject.Shared.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DevOpsProject.Shared.Messages
{
public class HiveReconnectedMessage : BaseMessage
{
public bool IsSuccessfullyReconnected { get; set; }
public HiveModel Hive { get; set; }
public HiveOperationalArea InitialOperationalArea { get; set; }
}
}

View File

@ -32,21 +32,17 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
[HttpGet("hive/{hiveId}")]
public async Task<IActionResult> GetHive(string 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);
}
[HttpGet("hive")]
public async Task<IActionResult> GetHives()
{
var hives = await _communicationControlService.GetAllHives();
return Ok(hives);
}
@ -63,7 +59,6 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
if (request?.Hives == null || !request.Hives.Any())
return BadRequest("No hive IDs provided.");
_logger.LogInformation("Hive moving request accepted by enpdoint. Request: {@request}", request);
foreach (var id in request.Hives)
{
_ = Task.Run(async () =>
@ -74,7 +69,7 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send control signal for HiveID: {id} \n Request: {@request}", id, request);
_logger.LogError(ex, $"Failed to send control signal for HiveID: {id}");
}
});
}

View File

@ -11,12 +11,10 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
public class HiveController : Controller
{
private readonly ICommunicationControlService _communicationControlService;
private readonly ILogger<HiveController> _logger;
public HiveController(ICommunicationControlService communicationControlService, ILogger<HiveController> logger)
public HiveController(ICommunicationControlService communicationControlService)
{
_communicationControlService = communicationControlService;
_logger = logger;
}
[HttpPost("connect")]
@ -38,7 +36,6 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
};
return Ok(connectResponse);
}
[HttpPost("telemetry")]
@ -54,9 +51,6 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
Timestamp = DateTime.Now
};
bool isHiveConnected = await _communicationControlService.IsHiveConnected(request.HiveID);
if (isHiveConnected)
{
var telemetryUpdateTimestamp = await _communicationControlService.AddTelemetry(hiveTelemetryModel);
var telemetryResponse = new HiveTelemetryResponse
{
@ -65,12 +59,6 @@ namespace DevOpsProject.CommunicationControl.API.Controllers
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");
}
}
}
}

View File

@ -12,5 +12,7 @@ namespace DevOpsProject.CommunicationControl.API.DI
return serviceCollection;
}
}
}

View File

@ -1,3 +0,0 @@
{
"API": "http://localhost:8080/api/v1/client"
}

View File

@ -3,9 +3,9 @@ import axios from "axios";
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
// Fetch the center coordinates for the initial map load
export const fetchCenterCoordinates = async (apiUrl) => {
export const fetchCenterCoordinates = async () => {
try {
const response = await axios.get(`${apiUrl}/area`);
const response = await axios.get(`${API_BASE_URL}/area`);
return response.data;
} catch (error) {
console.error("Error fetching center coordinates:", error);
@ -14,9 +14,9 @@ export const fetchCenterCoordinates = async (apiUrl) => {
};
// Fetch all hives and extract their latitude/longitude
export const fetchHives = async (apiUrl) => {
export const fetchHives = async () => {
try {
const response = await axios.get(`${apiUrl}/hive`);
const response = await axios.get(`${API_BASE_URL}/hive`);
return response.data.map(hive => ({
id: hive.HiveID,
@ -31,9 +31,9 @@ export const fetchHives = async (apiUrl) => {
};
// Move all hives to a new location
export const moveHives = async (apiUrl, lat, lon, ids) => {
export const moveHives = async (lat, lon, ids) => {
try {
await axios.patch(`${apiUrl}/hive`, {
await axios.patch(`${API_BASE_URL}/hive`, {
Hives: ids,
Destination: {
Latitude: lat,

View File

@ -21,7 +21,6 @@ const MapView = () => {
const mapRef = useRef(null);
const vectorLayerRef = useRef(null);
const initialized = useRef(false);
const apiUrl = useRef(null)
const [hives, setHives] = useState([]);
const [popup, setPopup] = useState({ visible: false, coords: null });
const [mouseCoords, setMouseCoords] = useState({ lat: "", lon: "" });
@ -29,17 +28,11 @@ const MapView = () => {
useEffect(() => {
const initializeMap = async () => {
const res = await fetch('/config.json')
const data = await res.json()
apiUrl.current = data.API
if (initialized.current) return;
initialized.current = true;
try {
const center = await fetchCenterCoordinates(apiUrl.current);
const center = await fetchCenterCoordinates();
if (center) {
initMap(center.Latitude, center.Longitude);
await fetchAndDrawHives();
@ -73,7 +66,7 @@ const MapView = () => {
// Fetch hives and draw them on the map
const fetchAndDrawHives = async () => {
try {
const data = await fetchHives(apiUrl.current);
const data = await fetchHives();
setHives(data);
drawHives(data);
} catch (error) {
@ -181,7 +174,7 @@ const MapView = () => {
<Popup
isVisible={popup.visible}
coords={popup.coords}
onConfirm={() => moveHives(apiUrl.current, popup.coords.lat, popup.coords.lon, hives.map(h => h.id))}
onConfirm={() => moveHives(popup.coords.lat, popup.coords.lon, hives.map(h => h.id))}
onCancel={() => setPopup({ visible: false })}
/>
</div>