405 lines
12 KiB
C#
405 lines
12 KiB
C#
using AgroBase.Models;
|
|
using AgroBase.Models.Operadores;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using static AgroBase.Models.Enums;
|
|
using static AgroBase.Models.Operadores.OperadoresModels;
|
|
|
|
namespace AgroBase.Services.Operadores
|
|
{
|
|
public class CameraWorkerService : IWorkerModel
|
|
{
|
|
public CameraWorkerModel DadosLeitura = new CameraWorkerModel();
|
|
public SaudeWorkerModel Saude { get; set; } = new SaudeWorkerModel();
|
|
|
|
private static readonly object _cameraLock = new object();
|
|
private static readonly object _refreshLock = new object();
|
|
|
|
private static readonly JsonSerializerSettings _jsonSettings =
|
|
new JsonSerializerSettings
|
|
{
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
MissingMemberHandling = MissingMemberHandling.Ignore,
|
|
Error = (sender, args) =>
|
|
{
|
|
args.ErrorContext.Handled = true;
|
|
}
|
|
};
|
|
|
|
private static List<CameraWorkerItemModel> CamerasConectadas = CriarListaVaziaComPlaceholder();
|
|
|
|
private static DateTime _ultimoRefreshUtc = DateTime.MinValue;
|
|
private static bool _refreshEmAndamento;
|
|
|
|
/*
|
|
* Pode chamar AtualizarDadosCameras() em tmrLeitura_Tick sem medo.
|
|
* Se o tick estiver em 100 ms, este throttle evita martelar Redis.
|
|
*/
|
|
private static readonly TimeSpan IntervaloMinimoRefresh = TimeSpan.FromMilliseconds(500);
|
|
|
|
private bool DebugMode = false;
|
|
|
|
public static List<CameraWorkerItemModel> ListaCameras
|
|
{
|
|
get
|
|
{
|
|
lock (_cameraLock)
|
|
{
|
|
return CamerasConectadas.Skip(1).ToList();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static OAKCameraModel CameraCaminho
|
|
{
|
|
get
|
|
{
|
|
CameraWorkerItemModel item = ListaCameras.FirstOrDefault(x => x.dispositivo == T_Code.Snr);
|
|
|
|
return item?.ToOAK();
|
|
}
|
|
}
|
|
|
|
public static List<OAKCameraModel> CamerasSolo
|
|
{
|
|
get
|
|
{
|
|
return ListaCameras
|
|
.Where(x => x.dispositivo == T_Code.Cam)
|
|
.Select(x => x.ToOAK())
|
|
.Where(x => x != null)
|
|
.ToList();
|
|
}
|
|
}
|
|
|
|
public void MostrarLog(string message, int limite = 500)
|
|
{
|
|
if (!DebugMode)
|
|
return;
|
|
|
|
message = message ?? string.Empty;
|
|
|
|
Console.WriteLine(
|
|
"[CameraWorker] " +
|
|
message.Substring(
|
|
0,
|
|
Math.Min(limite, message.Length)
|
|
) +
|
|
(message.Length > limite ? "..." : "")
|
|
);
|
|
}
|
|
|
|
public async Task<bool> IniciarProcessamento()
|
|
{
|
|
RedisService.Subscribe(CmdKey.CameraWorkerTx, RedisCallback);
|
|
|
|
DadosLeitura.IniciadoEm = DateTime.Now;
|
|
DadosLeitura.Iniciado = true;
|
|
|
|
bool pronto() => DadosLeitura.Pronto;
|
|
|
|
await FuncoesGlobais.AguardarCondicaoAsync(pronto, 5000);
|
|
|
|
if (pronto())
|
|
{
|
|
MostrarLog("Processamento iniciado com sucesso");
|
|
return true;
|
|
}
|
|
|
|
MostrarLog("Timeout ao iniciar script");
|
|
DadosLeitura.ReiniciarEstado();
|
|
return false;
|
|
}
|
|
|
|
public void RedisCallback(string mensagem)
|
|
{
|
|
try
|
|
{
|
|
if (string.IsNullOrWhiteSpace(mensagem))
|
|
return;
|
|
|
|
var comando = JsonConvert.DeserializeObject<ComandoRedis>(mensagem, _jsonSettings);
|
|
|
|
if (comando == null)
|
|
return;
|
|
|
|
var cmd = (CameraWorkerCommandType)comando.cmd;
|
|
|
|
switch (cmd)
|
|
{
|
|
case CameraWorkerCommandType.ScriptCarregado:
|
|
DadosLeitura.Pronto = true;
|
|
DadosLeitura.ProntoEm = DateTime.Now;
|
|
MostrarLog("Comando recebido: cmd = " + cmd);
|
|
break;
|
|
|
|
default:
|
|
MostrarLog("Comando não reconhecido: " + comando.cmd);
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MostrarLog("Erro ao deserializar resposta: " + ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
DadosLeitura.UltimaMensagem = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
public void AtualizarSaude()
|
|
{
|
|
Saude = OperadoresModels.AtualizarSaude(CtxKey.DadosCameraWorker);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lê câmeras diretamente do Redis e retorna uma lista nova.
|
|
/// Não atualiza o snapshot global. Para runtime, use AtualizarDadosCameras().
|
|
/// </summary>
|
|
public static List<CameraWorkerItemModel> GetListaCameras()
|
|
{
|
|
return LerCamerasDoRedis();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Atualiza o snapshot global das câmeras.
|
|
/// Pode ser chamado no tmrLeitura_Tick. Internamente possui throttle.
|
|
/// </summary>
|
|
public static void AtualizarDadosCameras()
|
|
{
|
|
AtualizarDadosCameras(false);
|
|
}
|
|
|
|
public static void AtualizarDadosCameras(bool forcar)
|
|
{
|
|
DateTime agoraUtc = DateTime.UtcNow;
|
|
|
|
if (!forcar && agoraUtc - _ultimoRefreshUtc < IntervaloMinimoRefresh)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (_refreshLock)
|
|
{
|
|
if (_refreshEmAndamento)
|
|
return;
|
|
|
|
if (!forcar && DateTime.UtcNow - _ultimoRefreshUtc < IntervaloMinimoRefresh)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_refreshEmAndamento = true;
|
|
}
|
|
|
|
try
|
|
{
|
|
List<CameraWorkerItemModel> novaLista = LerCamerasDoRedis();
|
|
|
|
lock (_cameraLock)
|
|
{
|
|
CamerasConectadas = novaLista;
|
|
_ultimoRefreshUtc = DateTime.UtcNow;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[CameraWorkerService.AtualizarDadosCameras] Erro ao atualizar câmeras: " + ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
lock (_refreshLock)
|
|
{
|
|
_refreshEmAndamento = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static OAKCameraModel AtualizarDadosCamera(string mx_id)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(mx_id))
|
|
return null;
|
|
|
|
CameraWorkerItemModel camera = LerCameraDoRedis(mx_id);
|
|
|
|
if (camera == null ||
|
|
string.IsNullOrWhiteSpace(camera.mx_id))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
lock (_cameraLock)
|
|
{
|
|
int index =
|
|
CamerasConectadas.FindIndex(
|
|
x => x.mx_id == mx_id
|
|
);
|
|
|
|
if (index >= 0)
|
|
CamerasConectadas[index] = camera;
|
|
else
|
|
CamerasConectadas.Add(camera);
|
|
|
|
OrdenarCamerasMantendoPlaceholder(CamerasConectadas);
|
|
}
|
|
|
|
return camera.ToOAK();
|
|
}
|
|
|
|
private static List<CameraWorkerItemModel> LerCamerasDoRedis()
|
|
{
|
|
var resultado = CriarListaVaziaComPlaceholder();
|
|
|
|
string camerasStr = RedisService.Get(CtxKey.DadosCameras);
|
|
|
|
if (string.IsNullOrWhiteSpace(camerasStr))
|
|
return resultado;
|
|
|
|
Dictionary<string, CameraWorkerItemModel> cameras;
|
|
|
|
try
|
|
{
|
|
cameras =
|
|
JsonConvert.DeserializeObject<
|
|
Dictionary<string, CameraWorkerItemModel>
|
|
>(
|
|
camerasStr,
|
|
_jsonSettings
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(
|
|
"[CameraWorkerService] Falha ao ler índice de câmeras: " +
|
|
ex.Message
|
|
);
|
|
|
|
return resultado;
|
|
}
|
|
|
|
if (cameras == null || cameras.Count == 0)
|
|
return resultado;
|
|
|
|
foreach (string key in cameras.Keys.ToList())
|
|
{
|
|
CameraWorkerItemModel camera = LerCameraDoRedis(key);
|
|
|
|
if (camera == null)
|
|
continue;
|
|
|
|
if (string.IsNullOrWhiteSpace(camera.mx_id))
|
|
continue;
|
|
|
|
resultado.Add(camera);
|
|
}
|
|
|
|
OrdenarCamerasMantendoPlaceholder(resultado);
|
|
|
|
return resultado;
|
|
}
|
|
|
|
private static CameraWorkerItemModel LerCameraDoRedis(string redisIdOuMxId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(redisIdOuMxId))
|
|
return null;
|
|
|
|
string cameraStr = RedisService.Get(RedisService.CamKey(redisIdOuMxId));
|
|
|
|
if (string.IsNullOrWhiteSpace(cameraStr))
|
|
return null;
|
|
|
|
CameraWorkerItemModel camera;
|
|
|
|
try
|
|
{
|
|
camera = JsonConvert.DeserializeObject<CameraWorkerItemModel>(cameraStr, _jsonSettings);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[CameraWorkerService.LerCameraDoRedis] Falha ao ler câmera " + redisIdOuMxId + ": " + ex.Message);
|
|
|
|
Variaveis.MostrarLog("[CameraWorkerService.LerCameraDoRedis] JSON câmera: " + cameraStr);
|
|
|
|
return null;
|
|
}
|
|
|
|
if (camera == null || string.IsNullOrWhiteSpace(camera.mx_id))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string imuStr = RedisService.Get(RedisService.CamImuKey(camera.mx_id));
|
|
|
|
if (!string.IsNullOrWhiteSpace(imuStr))
|
|
{
|
|
try
|
|
{
|
|
var imu = JsonConvert.DeserializeObject<OAKImuModel>(imuStr, _jsonSettings);
|
|
|
|
camera.imu = imu?.dados;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[CameraWorkerService.LerCameraDoRedis] Falha ao ler IMU da câmera " + camera.mx_id + ": " + ex.Message);
|
|
}
|
|
}
|
|
|
|
return camera;
|
|
}
|
|
|
|
private static List<CameraWorkerItemModel> CriarListaVaziaComPlaceholder()
|
|
{
|
|
return new List<CameraWorkerItemModel>
|
|
{
|
|
new CameraWorkerItemModel
|
|
{
|
|
dispositivo = T_Code.Vzo,
|
|
modelo = "Sem Video"
|
|
}
|
|
};
|
|
}
|
|
|
|
private static void OrdenarCamerasMantendoPlaceholder(List<CameraWorkerItemModel> cameras)
|
|
{
|
|
if (cameras == null || cameras.Count <= 1)
|
|
return;
|
|
|
|
CameraWorkerItemModel placeholder = cameras.FirstOrDefault(x => x.dispositivo == T_Code.Vzo && x.modelo == "Sem Video");
|
|
|
|
var reais =
|
|
cameras
|
|
.Where(x => !(x.dispositivo == T_Code.Vzo && x.modelo == "Sem Video"))
|
|
.OrderBy(PrioridadeCamera)
|
|
.ThenBy(x => x.mx_id)
|
|
.ToList();
|
|
|
|
cameras.Clear();
|
|
|
|
if (placeholder != null)
|
|
cameras.Add(placeholder);
|
|
else
|
|
cameras.Add(CriarListaVaziaComPlaceholder()[0]);
|
|
|
|
cameras.AddRange(reais);
|
|
}
|
|
|
|
private static int PrioridadeCamera(CameraWorkerItemModel camera)
|
|
{
|
|
if (camera == null)
|
|
return 99;
|
|
|
|
if (camera.dispositivo == T_Code.Snr)
|
|
return 0;
|
|
|
|
if (camera.dispositivo == T_Code.Cam)
|
|
return 1;
|
|
|
|
return 2;
|
|
}
|
|
}
|
|
}
|