using FluentValidation; using InfluxDB.Client.Api.Domain; using InfluxDB.Client.Writes; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PARR.API.Contracts.V1; using PARR.API.Contracts.V1.Requests; using PARR.API.Contracts.V1.Requests.Queries; using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Contracts.V1.Responses.Statistics; using PARR.API.Controllers.V1.Base; using PARR.API.Services.Interfaces; using PARR.API.Settings; using PARR.Constants; using PARR.DAL.InfluxDbServices; using PARR.DAL.Settings; namespace PARR.API.Controllers.V1.Statistics { /// /// Статистика доступности роботов /// [Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)] public class StatRobotStateController : BaseApiController { private readonly IInfluxDbService influxDbService; private readonly IValidator validator; private readonly InfluxDbSettings influxDbSettings; private readonly MonitoringSettings monitoringSettings; private readonly IClientService clientService; public StatRobotStateController( IInfluxDbService influxDbService, IValidator validator, InfluxDbSettings influxDbSettings, MonitoringSettings monitoringSettings, IClientService clientService ) { this.influxDbService = influxDbService; this.validator = validator; this.influxDbSettings = influxDbSettings; this.monitoringSettings = monitoringSettings; this.clientService = clientService; } /// /// Доступность робота /// /// [HttpGet(ApiRoutes.StatRobotState.Get)] public async Task Get([FromRoute] RobotsAllEnum robot, [FromQuery] RobotStateGetQuery query) { var startAt = string.IsNullOrEmpty(query.StartAt) ? "-2h" : query.StartAt; var robotSettings = monitoringSettings.Robots.FirstOrDefault(t => t.Name == robot.ToString()); if (robotSettings == null) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при получении статистики. Неверно указаны настройки мониторинга." } })); // var inaccessibilityTime = TimeSpan.Parse("00:03:00"); var inaccessibilityTime = robotSettings.InaccessibilityTime; var result = await influxDbService.QueryAsync(async handler => { //var flux = "from(bucket:\"robots\") " + // "|> range(start: 0) " + // "|> filter(fn: (r) => " + // "r._measurement == \"altitude\" and " + // "r._value > 3500)"; var query = $"from(bucket:\"{influxDbSettings.Robots!.Bucket}\") " + //$"|> range(start: -3h) " + $"|> range(start: {startAt}) " + $"|> filter(fn: (r) => " + $"r._measurement == \"robots\" " + $"and r.robot == \"{robot.ToString()}\"" + $")"; var tables = await handler.QueryAsync(query, influxDbSettings.Organization); return tables.SelectMany(table => table.Records.Select(record => new StatRobotStateResponse { Date = (DateTimeOffset)record.GetTimeInDateTime(), IsUp = int.Parse(record.GetValue()?.ToString() ?? "0"), Ip = record.GetValueByKey("ip")?.ToString() }) ); }, influxDbSettings.Robots!.Token); if (result == null) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при получении статистики" } })); var listWithInaccessTime = CalcInaccessTime(result.ToList(), inaccessibilityTime); return Ok(new Response>(listWithInaccessTime, true)); } /// /// Статистика доступности роботов. Передать инф-у о доступности /// /// /// [HttpPost(ApiRoutes.StatRobotState.Send)] public async Task Send([FromBody] RobotStateRequest request) { var resultValidate = await validator.ValidateAsync(request); if (!resultValidate.IsValid) return BadRequest(new Response(resultValidate.Errors)); var result = influxDbService.Write(write => { var point = PointData.Measurement("robots") .Tag("robot", request.Robot.ToString()) .Tag("ip", clientService.GetClientIp()?.ToString()) //.Field("robot", (int)request.Robot) .Field("isUp", 1) .Timestamp(DateTime.UtcNow, WritePrecision.Ns); write.WritePoint(point, bucket: influxDbSettings.Robots!.Bucket, org: influxDbSettings.Organization); }, influxDbSettings.Robots!.Token); if (!result) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при отправке данных" } })); return Ok(new Response("", true)); } /// /// Рассчитывает время когда робот был недоступен /// /// /// /// private List CalcInaccessTime(List data, TimeSpan inaccessibilityTime) { var inaccessTimeList = new List(); DateTimeOffset? lastTime = null; foreach (var item in data.OrderBy(t => t.Date)) { if (!lastTime.HasValue) { lastTime = item.Date; continue; } lastTime = lastTime.Value.Add(inaccessibilityTime); while (lastTime < item.Date) { //есть время когда робот был недоступен, добаляем в список недоступности inaccessTimeList.Add(new StatRobotStateResponse { Date = lastTime.Value, IsUp = 0 }); lastTime = lastTime.Value.Add(inaccessibilityTime); } // робот был доступен, все ок lastTime = item.Date; } //if (lastTime == null) //{ // // не было вообще никакой статистики, считаем что он недоступен // inaccessTimeList.Add(new StatRobotStateResponse { Date = DateTimeOffset.UtcNow, IsUp = 0 }); //} // Проверяем, вдруг с последней даты активности до сейчас прошло уже много времени и он не доступен if (lastTime.HasValue) { lastTime = lastTime.Value.Add(inaccessibilityTime); while (lastTime.Value < DateTimeOffset.UtcNow) { inaccessTimeList.Add(new StatRobotStateResponse { Date = lastTime.Value, IsUp = 0 }); lastTime = lastTime.Value.Add(inaccessibilityTime); } } data.AddRange(inaccessTimeList); return data.OrderBy(t => t.Date).ToList(); } } }