Files
parr_api/PARR.API/Controllers/V1/Statistics/StatRobotStateController.cs

230 lines
9.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AutoMapper;
using FluentValidation;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Writes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1;
using PARR.API.Contracts.V1.Requests;
using PARR.API.Contracts.V1.Requests.Queries;
using PARR.API.Contracts.V1.Responses;
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.Core.Common.Interfaces;
using PARR.Core.Repositories.Interfaces;
using PARR.Domain.Common.Roles;
using PARR.Domain.Enums;
using PARR.Domain.Settings;
namespace PARR.API.Controllers.V1.Statistics
{
/// <summary>
/// Статистика доступности роботов
/// </summary>
[Authorize(Roles = ParrRoles.EsppRobot.RoleOrAdmin)]
public class StatRobotStateController : BaseApiController
{
private readonly IInfluxDbService influxDbService;
//private readonly IValidator<RobotStateRequest> validator;
private readonly InfluxDbSettings influxDbSettings;
private readonly MonitoringSettings monitoringSettings;
private readonly IClientService clientService;
private readonly IUserRepository userService;
private readonly IMapper mapper;
public StatRobotStateController(
IInfluxDbService influxDbService,
//IValidator<RobotStateRequest> validator,
InfluxDbSettings influxDbSettings,
MonitoringSettings monitoringSettings,
IClientService clientService,
IUserRepository userService,
IMapper mapper
)
{
this.influxDbService = influxDbService;
//this.validator = validator;
this.influxDbSettings = influxDbSettings;
this.monitoringSettings = monitoringSettings;
this.clientService = clientService;
this.userService = userService;
this.mapper = mapper;
}
/// <summary>
/// Доступность робота
/// </summary>
/// <returns></returns>
[HttpGet(ApiRoutes.StatRobotState.Get)]
public async Task<IActionResult> 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<ErrorModel> { 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 InfuxRowData
{
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<ErrorModel> { new ErrorModel { Message = $"Ошибка при получении статистики" } }));
var groupingByRobotIp = result.GroupBy(t => t.Ip);
var response = new List<StatRobotStateResponse>();
foreach (var robotItem in groupingByRobotIp)
{
var user = await userService.Get().FirstOrDefaultAsync(t => t.Ip == robotItem.Key);
response.Add(new StatRobotStateResponse
{
RobotIp = robotItem.Key,
User = mapper.Map<UserBaseResponse>(user),
Data = CalcInaccessTime(robotItem.Select(t => new StatRobotStateData { Date = t.Date, IsUp = t.IsUp }).ToList(), inaccessibilityTime)
});
}
//var listWithInaccessTime = CalcInaccessTime(result.ToList(), inaccessibilityTime);
return Ok(new Response<List<StatRobotStateResponse>>(response.OrderBy(t => t.User?.Name).ToList(), true));
}
/// <summary>
/// Статистика доступности роботов. Передать инф-у о доступности
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost(ApiRoutes.StatRobotState.Send)]
public async Task<IActionResult> 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<ErrorModel> { new ErrorModel { Message = $"Ошибка при отправке данных" } }));
return Ok(new Response<string>("", true));
}
/// <summary>
/// Рассчитывает время когда робот был недоступен
/// </summary>
/// <param name="data"></param>
/// <param name="inaccessibilityTime"></param>
/// <returns></returns>
private List<StatRobotStateData> CalcInaccessTime(List<StatRobotStateData> data, TimeSpan inaccessibilityTime)
{
var inaccessTimeList = new List<StatRobotStateData>();
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 StatRobotStateData { 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 StatRobotStateData { Date = lastTime.Value, IsUp = 0 });
lastTime = lastTime.Value.Add(inaccessibilityTime);
}
}
data.AddRange(inaccessTimeList);
return data.OrderBy(t => t.Date).ToList();
}
}
class InfuxRowData
{
public DateTimeOffset Date { get; set; }
public int IsUp { get; set; }
public string? Ip { get; set; }
}
}