ajustes ao salvar logs e calibragem

This commit is contained in:
Diego Freitas 2026-07-02 15:42:14 -03:00
parent b953ff524e
commit c4862a7ce4
3 changed files with 351 additions and 144 deletions

View File

@ -1940,23 +1940,25 @@ namespace AgroBase.Models.Modules
{
var op = Variaveis.OperacaoEmAndamento;
if (!op.Sensoriamento.Operacao.ModsCalibragem.ContainsKey(T_Code.Atu))
op.Sensoriamento.Operacao.ModsCalibragem.Add(T_Code.Atu, new Dictionary<string, bool>());
if (!op.Sensoriamento.Operacao.ModsCalibragem.ContainsKey(Dispositivo))
op.Sensoriamento.Operacao.ModsCalibragem.Add(Dispositivo, new Dictionary<string, bool>());
var calAtu = op.Sensoriamento.Operacao.ModsCalibragem[Dispositivo];
// Ligar bomba
foreach (var Bomba in BombasPressurizadoras.Where(x => x.Comandar))
{
calAtu.TryGetValue(Bomba.ID, out var cal);
if (!forcar && cal && calAtu[Bomba.ID]) continue;
if (!forcar && op.ModuloEstaCalibrado(Dispositivo, Bomba.ID))
continue;
calAtu[Bomba.ID] = false;
op.MarcarModuloCalibrado(Dispositivo, Bomba.ID, false);
// Sem herbicida no reservatorio, bomba nao precisa ligar
if (!Bomba.Inicializado || ContadorCarga.PercentualAtual < VariaveisEquipamento.PercentualReservatorioMin)
{
Variaveis.MostrarLog($"[AtuadorModel] [RealizarTestesIniciais] Bomba não iniciada ou nível do reservatório baixo, não irá ligar a bomba {Bomba.ID}");
op.Sensoriamento.InserirLog(Dispositivo, StatusModulo.Alerta, 50, $"Bomba não iniciada ou nível do reservatório baixo, não irá ligar a bomba {Bomba.ID}", id: Bomba.ID);
op.MarcarModuloCalibrado(Dispositivo, Bomba.ID, false);
continue;
}
@ -1997,7 +1999,7 @@ namespace AgroBase.Models.Modules
{
Variaveis.MostrarLog($"[AtuadorModel] [RealizarTestesIniciais] Bomba {Bomba.ID} não possui um sensor de pressão, será controlada mecanicamente pelo pressostato");
op.Sensoriamento.InserirLog(Dispositivo, StatusModulo.Alerta, 50, $"Bomba {Bomba.ID} não possui um sensor de pressão, será controlada mecanicamente pelo pressostato", id: Bomba.ID);
calAtu[Bomba.ID] = true;
op.MarcarModuloCalibrado(Dispositivo, Bomba.ID, true);
continue;
}
@ -2006,7 +2008,7 @@ namespace AgroBase.Models.Modules
bool BombaOk = Bomba.AtingiuPressao();
if (BombaOk)
{
calAtu[Bomba.ID] = true;
op.MarcarModuloCalibrado(Dispositivo, Bomba.ID, true);
}
else
{
@ -2033,7 +2035,7 @@ namespace AgroBase.Models.Modules
await FuncoesGlobais.AguardarCondicaoAsync(Bomba.AtingiuPressao, 4000, 200);
BombaOk = Bomba.AtingiuPressao();
calAtu[Bomba.ID] = BombaOk;
op.MarcarModuloCalibrado(Dispositivo, Bomba.ID, BombaOk);
}
}
@ -2043,7 +2045,7 @@ namespace AgroBase.Models.Modules
calAtu.TryGetValue(Bico.ID, out var cal);
if (!forcar && cal && calAtu[Bico.ID]) continue;
calAtu[Bico.ID] = false;
op.MarcarModuloCalibrado(Dispositivo, Bico.ID, false);
if (Bico.Inicializado)
{
@ -2063,13 +2065,21 @@ namespace AgroBase.Models.Modules
await Task.Delay(200);
calAtu[Bico.ID] = true;
op.MarcarModuloCalibrado(Dispositivo, Bico.ID, true);
}
}
await Task.Delay(1000);
bool sucesso = calAtu.All(x => x.Value);
var idsEsperados = BombasPressurizadoras
.Where(x => x.Comandar)
.Select(x => x.ID)
.Concat(BicosPulverizadores
.Where(x => x.Comandar)
.Select(x => x.ID))
.ToList();
bool sucesso = idsEsperados.All(id => op.ModuloEstaCalibrado(Dispositivo, id));
return sucesso;
}

View File

@ -531,11 +531,20 @@ namespace AgroBase.Models.Modules
if (!mc.ContainsKey(T_Code.Mov))
mc.Add(T_Code.Mov, new Dictionary<string, bool>());
var calMov = mc[T_Code.Mov];
foreach (var mod in DispMvd.Dados.Modulos)
foreach (var Modulo in DispMvd.Dados.Modulos)
{
calMov.TryGetValue(mod.Modulo_ID, out var cal);
if (!forcar && cal && calMov[mod.Modulo_ID]) continue;
calMov[mod.Modulo_ID] = mod.MovMotor.Inicializado;
calMov.TryGetValue(Modulo.Modulo_ID, out var cal);
if (!forcar && cal && calMov[Modulo.Modulo_ID]) continue;
bool ok = Modulo.MovMotor?.Inicializado == true;
op.MarcarModuloCalibrado(T_Code.Mov, Modulo.Modulo_ID, ok);
if (!ok)
{
Variaveis.MostrarLog($"[MovimentacaoUnificadaModel] [RealizarCalibragemMovAsync] MOV {Modulo.Modulo_ID} não inicializado.");
op.Sensoriamento.InserirLog(T_Code.Mov, StatusModulo.Alerta, 50, $"MOV {Modulo.Modulo_ID} não inicializado.", id: Modulo.Modulo_ID);
}
}
DispMvd.Dados.MovPronto = DispMvd.Dados.Modulos.All(x => x.MovMotor.Inicializado);
@ -548,45 +557,54 @@ namespace AgroBase.Models.Modules
var op = Variaveis.OperacaoEmAndamento;
List<Task> tasks = new List<Task>();
var DispMvd = op.DispMvd;
if (DispMvd != null)
var dispMvd = op?.DispMvd;
if (dispMvd?.Dados?.Modulos == null)
return tasks;
dispMvd.Dados.DirPronto = dispMvd.Dados.Modulos.All(x => x.DirMotor.Inicializado);
var modsRef = dispMvd.Dados.Modulos
.ToList();
foreach (var modulo in modsRef)
{
var mc = op.Sensoriamento.Operacao.ModsCalibragem;
if (!mc.ContainsKey(T_Code.Dir))
mc.Add(T_Code.Dir, new Dictionary<string, bool>());
var calDir = mc[T_Code.Dir];
foreach (var mod in DispMvd.Dados.Modulos)
if (!forcar && op.ModuloEstaCalibrado(T_Code.Dir, modulo.Modulo_ID))
continue;
op.MarcarModuloCalibrado(T_Code.Dir, modulo.Modulo_ID, false);
if (!modulo.DirMotor.Inicializado)
{
calDir.TryGetValue(mod.Modulo_ID, out var cal);
if (!forcar && cal && calDir[mod.Modulo_ID]) continue;
calDir[mod.Modulo_ID] = false;
Variaveis.MostrarLog($"[MovimentacaoUnificadaModel] [RealizarCalibragemDirAsync] DIR {modulo.Modulo_ID} não inicializado.");
op.Sensoriamento.InserirLog(T_Code.Dir, StatusModulo.Alerta, 50, $"DIR {modulo.Modulo_ID} não inicializado.", id: modulo.Modulo_ID);
continue;
}
DispMvd.Dados.DirPronto = DispMvd.Dados.Modulos.All(x => x.DirMotor.Inicializado);
op.DefinirTipoMovimentoControle(TipoMovimentoDirecional.Diagnostico);
if (DispMvd.Dados.Modulos.Any(x => x.DirMotor.Inicializado))
tasks.Add(Task.Run(async () =>
{
op.DefinirTipoMovimentoControle(TipoMovimentoDirecional.Diagnostico);
var ModsRef = Modulos.Where(x => x.DirMotor.Comandar).ToList(); // && x.DirMotor.Inicializado
foreach (var Modulo in ModsRef)
try
{
if (calDir[Modulo.Modulo_ID]) continue;
tasks.Add(Modulo.DirMotor.ReferenciaMotor().ContinueWith(t => {
if (t.IsFaulted)
{
Variaveis.MostrarLog($"[MovimentacaoUnificadaModel] [RealizarCalibragemDirAsync] Erro no referenciamento do módulo {Modulo.Modulo_ID}: {t.Exception?.InnerException?.Message}");
op.Sensoriamento.InserirLog(Dispositivo, StatusModulo.Falha, 0, $"Erro no referenciamento do módulo {Modulo.Modulo_ID}: {t.Exception?.InnerException?.Message}");
}
if (t.IsCompleted)
{
Variaveis.MostrarLog($"[MovimentacaoUnificadaModel] [RealizarCalibragemDirAsync] Referenciamento do módulo {Modulo.Modulo_ID} concluído com sucesso!");
op.Sensoriamento.InserirLog(Dispositivo, StatusModulo.Operante, 100, $"Referenciamento do módulo {Modulo.Modulo_ID} concluído com sucesso!");
calDir[Modulo.Modulo_ID] = true;
}
}));
await modulo.DirMotor.ReferenciaMotor();
op.MarcarModuloCalibrado(T_Code.Dir, modulo.Modulo_ID, true);
Variaveis.MostrarLog($"[MovimentacaoUnificadaModel] [RealizarCalibragemDirAsync] Referenciamento do módulo {modulo.Modulo_ID} concluído com sucesso!");
op.Sensoriamento.InserirLog(T_Code.Dir, StatusModulo.Operante, 100, $"Referenciamento do módulo {modulo.Modulo_ID} concluído com sucesso!", id: modulo.Modulo_ID);
}
}
catch (Exception ex)
{
op.MarcarModuloCalibrado(T_Code.Dir, modulo.Modulo_ID, false);
Variaveis.MostrarLog($"[MovimentacaoUnificadaModel] [RealizarCalibragemDirAsync] Erro no referenciamento do módulo {modulo.Modulo_ID}: {ex.Message}");
op.Sensoriamento.InserirLog(T_Code.Dir, StatusModulo.Falha, 0, $"Erro no referenciamento do módulo {modulo.Modulo_ID}: {ex.Message}", id: modulo.Modulo_ID);
}
}));
}
return tasks;
}

View File

@ -148,6 +148,152 @@ namespace AgroBase.Models
public List<GPSModel> GPSTrajetoria { get; set; } = new List<GPSModel>();
public OperacaoParametrosModel Parametros { get; set; } = new OperacaoParametrosModel();
public OperacaoSensoriamentoConjuntoModel Sensoriamento { get; set; }
private readonly object _sensoriamentoStateLock = new object();
internal void DefinirEstadoCalibragemRuntime(bool calibrando, bool pausa)
{
lock (_sensoriamentoStateLock)
{
if (Sensoriamento?.Operacao != null)
{
Sensoriamento.Operacao.Calibrando = calibrando;
Sensoriamento.Operacao.Pausa = pausa;
}
}
RedisService.AtualizarCampos(CtxKey.DadosOperacao, ("calibrando", calibrando));
}
internal void SubstituirSensoriamentoSnapshot(OperacaoSensoriamentoConjuntoModel dadosAtualizados)
{
if (dadosAtualizados == null)
return;
lock (_sensoriamentoStateLock)
{
var runtime = Sensoriamento?.Operacao;
if (runtime != null && dadosAtualizados.Operacao != null)
{
dadosAtualizados.Operacao.CopiarEstadoRuntimeDe(runtime);
}
Sensoriamento = dadosAtualizados;
}
}
internal void MarcarModuloCalibrado(T_Code modulo, string id, bool ok)
{
lock (_sensoriamentoStateLock)
{
if (Sensoriamento?.Operacao?.ModsCalibragem == null)
{
Sensoriamento.Operacao.ModsCalibragem =
new Dictionary<T_Code, Dictionary<string, bool>>();
}
if (!Sensoriamento.Operacao.ModsCalibragem.ContainsKey(modulo) ||
Sensoriamento.Operacao.ModsCalibragem[modulo] == null)
{
Sensoriamento.Operacao.ModsCalibragem[modulo] =
new Dictionary<string, bool>();
}
Sensoriamento.Operacao.ModsCalibragem[modulo][id] = ok;
}
}
internal bool ModuloEstaCalibrado(T_Code modulo, string id)
{
lock (_sensoriamentoStateLock)
{
var mods = Sensoriamento?.Operacao?.ModsCalibragem;
if (mods == null)
return false;
if (!mods.TryGetValue(modulo, out var itens) || itens == null)
return false;
return itens.TryGetValue(id, out bool ok) && ok;
}
}
private readonly object _eventosLock = new object();
private readonly List<OperacaoSensoriamentoLogErrosModel> _eventosPendentes = new List<OperacaoSensoriamentoLogErrosModel>();
private readonly Dictionary<string, DateTime> _dedupeEventosRecentes = new Dictionary<string, DateTime>();
private static readonly TimeSpan JanelaDedupeEvento = TimeSpan.FromSeconds(2);
private static string CriarChaveEvento(T_Code dispositivo, StatusModulo status, double saude, string mensagem, string id, string condicoes)
{
return string.Join(
"|",
dispositivo,
id ?? "",
status,
Math.Round(saude, 3).ToString(CultureInfo.InvariantCulture),
mensagem ?? "",
condicoes ?? ""
);
}
internal void InserirEventoOperacao(T_Code dispositivo, StatusModulo status, double saude, string mensagem, string id = null, string condicoes = null)
{
try
{
DateTime agora = DateTime.Now;
string chave = CriarChaveEvento(
dispositivo,
status,
saude,
mensagem,
id,
condicoes
);
lock (_eventosLock)
{
var expirados = _dedupeEventosRecentes
.Where(x => agora - x.Value > JanelaDedupeEvento)
.Select(x => x.Key)
.ToList();
foreach (var key in expirados)
_dedupeEventosRecentes.Remove(key);
if (_dedupeEventosRecentes.TryGetValue(chave, out DateTime ultimo) && agora - ultimo <= JanelaDedupeEvento)
{
return;
}
_dedupeEventosRecentes[chave] = agora;
_eventosPendentes.Add(new OperacaoSensoriamentoLogErrosModel()
{
Momento = agora,
Dispositivo = dispositivo,
ID = id,
Status = status,
Saude = saude,
Mensagem = mensagem,
Condicoes = condicoes
});
}
}
catch (Exception ex)
{
Variaveis.MostrarLog($"[OperacaoModel.InserirEventoOperacao] Erro ao inserir evento: {ex.Message}");
}
}
private readonly object _atualizarDadosLock = new object();
internal void AtualizarSensoriamentoSeguro()
{
lock (_atualizarDadosLock)
{
Sensoriamento?.AtualizarDados();
}
}
public Form frmOperacao { get; set; }
public bool Treinando { get; set; } = false;
@ -161,6 +307,58 @@ namespace AgroBase.Models
private static string _discoverySessionId = Guid.NewGuid().ToString("N");
private static long _telemetrySequence = 0;
private readonly SemaphoreSlim _calibragemGate = new SemaphoreSlim(1, 1);
public bool CalibrandoRuntime
{
get
{
lock (_sensoriamentoStateLock)
return Sensoriamento?.Operacao?.Calibrando == true;
}
}
private DateTime _ultimaTentativaCalibragem = DateTime.MinValue;
private readonly TimeSpan _cooldownCalibragem = TimeSpan.FromSeconds(30);
private bool PodeTentarCalibragemAutomatica()
{
return DateTime.Now - _ultimaTentativaCalibragem > _cooldownCalibragem;
}
private void RegistrarTentativaCalibragem()
{
_ultimaTentativaCalibragem = DateTime.Now;
}
internal List<OperacaoSensoriamentoLogErrosModel> DrenarEventosPendentes()
{
lock (_eventosLock)
{
var eventos = _eventosPendentes
.Where(x => x != null)
.OrderBy(x => x.Momento)
.ToList();
_eventosPendentes.Clear();
return eventos;
}
}
internal List<OperacaoSensoriamentoLogErrosModel> ListarEventosPendentes()
{
lock (_eventosLock)
{
return _eventosPendentes
.Where(x => x != null)
.OrderBy(x => x.Momento)
.ToList();
}
}
internal void ReenfileirarEventosPendentes(List<OperacaoSensoriamentoLogErrosModel> eventos)
{
if (eventos == null || eventos.Count == 0)
return;
lock (_eventosLock)
{
_eventosPendentes.InsertRange(0, eventos.Where(x => x != null));
}
}
@ -899,7 +1097,7 @@ namespace AgroBase.Models
{
op.DefinirComponentesEmUso();
RedisService.AtualizarCampos(CtxKey.DadosOperacao, ("configurado", false));
op.Sensoriamento.AtualizarDados();
op.AtualizarSensoriamentoSeguro();
}
return op;
@ -1100,7 +1298,7 @@ namespace AgroBase.Models
op.SalvarOperacao();
op.Sensoriamento.AtualizarDados();
op.AtualizarSensoriamentoSeguro();
dispMvd?.Dados?.ReiniciarLeituras(dispMvd?.Dados);
dispAtu?.Dados?.ReiniciarLeituras(dispAtu?.Dados);
@ -1174,7 +1372,7 @@ namespace AgroBase.Models
op.PararTimerLogsOperacao();
op.SalvarLogs(true);
await op.SalvarLogsSeguroAsync(true);
op.Sensoriamento.Operacao.Finalizando = false;
op.Trajetoria?.AutonomiaCorredor?.Parar();
@ -1233,7 +1431,7 @@ namespace AgroBase.Models
op.ID = "";
op.Sensoriamento.AtualizarDados();
op.AtualizarSensoriamentoSeguro();
dispMvd?.Dados?.ReiniciarLeituras(dispMvd.Dados);
dispAtu?.Dados?.ReiniciarLeituras(dispAtu.Dados);
@ -1481,16 +1679,13 @@ namespace AgroBase.Models
entrouEmCalibragem = true;
op.Sensoriamento.Operacao.Calibrando = true;
op.Sensoriamento.Operacao.Pausa = true;
RedisService.AtualizarCampos(CtxKey.DadosOperacao, ("calibrando", true));
op.DefinirEstadoCalibragemRuntime(true, true);
Variaveis.MostrarLog("[OperacaoModel] [RealizarCalibragemInicialAsync] Pausa para calibragem inicial");
op.Sensoriamento.InserirLog(T_Code.Mod, StatusModulo.Operante, 100, "Pausa para calibragem inicial");
await Task.Delay(3000);
await Task.Delay(5000);
if (!ReferenceEquals(Variaveis.OperacaoEmAndamento, this))
return;
@ -1554,10 +1749,7 @@ namespace AgroBase.Models
if (entrouEmCalibragem)
{
op.Sensoriamento.Operacao.Calibrando = false;
op.Sensoriamento.Operacao.Pausa = false;
RedisService.AtualizarCampos(CtxKey.DadosOperacao, ("calibrando", false));
op.DefinirEstadoCalibragemRuntime(false, false);
}
_calibragemGate.Release();
@ -1830,7 +2022,7 @@ namespace AgroBase.Models
var op = this;
op.Sensoriamento?.AtualizarDados();
op.AtualizarSensoriamentoSeguro();
//Variaveis.LoraService?.EnviarDadosParaBase();
if (op.Parametros?.Modo == ModoOperacao.Manual)
@ -2837,7 +3029,6 @@ namespace AgroBase.Models
if (!ReferenceEquals(Variaveis.OperacaoEmAndamento, op))
{
Variaveis.MostrarLog($"[OperacaoModel.tmrLogs_Tick] Timer órfão parado. TimerHash={GetHashCode()}, OperacaoTimer={ID}, OperacaoAtual={Variaveis.OperacaoEmAndamento?.ID}");
_tmrLogsOperacao?.Dispose();
return;
}
@ -2845,25 +3036,39 @@ namespace AgroBase.Models
if (op.Sensoriamento?.Operacao?.OperacaoIniciada != true)
return;
bool salvou = await op.SalvarLogsSeguroAsync(false);
if (!salvou)
return;
if (op.idxLog % 10 == 0)
{
AudioAlertaService.AlertaSonoroPorStatus(op.Sensoriamento.Operacao.StatusOperacaoAtual);
}
if (op.idxLog % 5 == 0 && !op.CalibrandoRuntime && op.PodeTentarCalibragemAutomatica())
{
op.RegistrarTentativaCalibragem();
_ = Task.Run(async () => await op.RealizarCalibragemInicialAsync(false));
}
}
private async Task<bool> SalvarLogsSeguroAsync(bool fim)
{
if (!await _salvarLogsGate.WaitAsync(0))
{
Variaveis.MostrarLog($"[OperacaoModel.tmrLogs_Tick] Tick ignorado porque SalvarLogs ainda está em execução. Operacao={ID}, idxLog={idxLog}");
return;
if (!fim)
{
Variaveis.MostrarLog($"[OperacaoModel.SalvarLogsSeguroAsync] Ignorado: já existe salvamento em execução. Operacao={ID}, idxLog={idxLog}");
return false;
}
await _salvarLogsGate.WaitAsync();
}
try
{
op.SalvarLogs(false);
if (op.idxLog % 10 == 0)
{
AudioAlertaService.AlertaSonoroPorStatus(op.Sensoriamento.Operacao.StatusOperacaoAtual);
}
if (op.idxLog % 5 == 0 && !op.Sensoriamento.Operacao.Calibrando)
{
_ = Task.Run(async () => await op.RealizarCalibragemInicialAsync(false));
}
return SalvarLogs(fim);
}
finally
{
@ -2871,23 +3076,23 @@ namespace AgroBase.Models
}
}
public void SalvarLogs(bool Fim)
public bool SalvarLogs(bool Fim)
{
var op = this;
if (op == null)
return;
return false;
bool salvouComSucesso = false;
bool eventosGravadosComSucesso = false;
DateTime agora = DateTime.Now;
DateTime novoCursorEventos = DateTime.Now;
List<OperacaoSensoriamentoLogErrosModel> eventosSalvos = new List<OperacaoSensoriamentoLogErrosModel>();
try
{
if (string.IsNullOrWhiteSpace(op.ID))
return;
return false;
// Garante pasta da operação
string pastaOperacao = Path.Combine(Variaveis.CaminhoOperacoes, op.ID);
@ -2898,14 +3103,14 @@ namespace AgroBase.Models
//
// Se você já chama op.Sensoriamento.AtualizarDados()
// imediatamente antes de SalvarLogs(), pode remover esse bloco.
op.Sensoriamento?.AtualizarDados();
op.AtualizarSensoriamentoSeguro();
// AtualizarDados() substitui op.Sensoriamento por um novo snapshot,
// então pegamos a referência novamente.
var sen = op.Sensoriamento;
if (sen == null)
return;
return false;
// -----------------------------
// Logs principais
@ -2948,17 +3153,15 @@ namespace AgroBase.Models
// Eventos discretos de saúde/erro
// Melhor salvar um evento por linha.
// -----------------------------
eventosSalvos =
(sen.Logs ?? new List<OperacaoSensoriamentoLogErrosModel>())
.Where(x => x != null)
.OrderBy(x => x.Momento)
.ToList();
eventosSalvos = op.DrenarEventosPendentes();
foreach (var evento in eventosSalvos)
{
SalvarLogLine("eventos", evento);
}
eventosGravadosComSucesso = true;
// -----------------------------
// Câmeras
// -----------------------------
@ -2988,28 +3191,16 @@ namespace AgroBase.Models
op.idxLog++;
salvouComSucesso = true;
if (eventosSalvos.Any())
{
novoCursorEventos = eventosSalvos.Max(x => x.Momento);
}
else
{
novoCursorEventos = DateTime.Now;
}
return true;
}
catch (Exception ex)
{
Variaveis.MostrarLog("[OperacaoModel.SalvarLogs] Erro ao salvar log: " + ex.Message);
return false;
}
finally
{
// Só avança o cursor de eventos se salvou de verdade.
// Assim, se der erro no disco/JSON, você não perde eventos.
if (salvouComSucesso && op?.Sensoriamento != null)
{
op.Sensoriamento.UltimoRegistroLog = novoCursorEventos;
}
try
{
op?.Controle?.TiposControle?.ForEach(x => x.Comandos?.Clear());
@ -3018,6 +3209,11 @@ namespace AgroBase.Models
{
// Não deixa limpeza de comandos derrubar o log.
}
if (!eventosGravadosComSucesso && eventosSalvos.Any())
{
op.ReenfileirarEventosPendentes(eventosSalvos);
}
}
}
@ -4686,48 +4882,16 @@ namespace AgroBase.Models
public DateTime UltimoRegistroLog { get; set; } = DateTime.MinValue;
private static readonly TimeSpan JanelaDedupeEvento = TimeSpan.FromSeconds(2);
public void InserirLog(T_Code dispositivo, StatusModulo status, double saude, string mensagem, string id = null, string condicoes = null)
{
if (op == null)
return;
try
{
if (Logs == null)
Logs = new List<OperacaoSensoriamentoLogErrosModel>();
DateTime agora = DateTime.Now;
bool duplicadoRecente = Logs.Any(x =>
x != null &&
(agora - x.Momento).Duration() <= JanelaDedupeEvento &&
x.Dispositivo == dispositivo &&
x.ID == id &&
x.Status == status &&
Math.Abs(x.Saude - saude) < 0.001 &&
x.Mensagem == mensagem &&
x.Condicoes == condicoes
);
if (duplicadoRecente)
return;
Logs.Add(new OperacaoSensoriamentoLogErrosModel()
{
Momento = agora,
Dispositivo = dispositivo,
ID = id,
Status = status,
Saude = saude,
Mensagem = mensagem,
Condicoes = condicoes
});
}
catch (Exception ex)
{
Variaveis.MostrarLog($"[OperacaoModel.InserirLog] Erro ao inserir log: {ex.Message}");
}
op?.InserirEventoOperacao(
dispositivo,
status,
saude,
mensagem,
id,
condicoes
);
}
@ -4736,7 +4900,6 @@ namespace AgroBase.Models
if (op == null) return;
DateTime agora = DateTime.Now;
DateTime ultimoRegistroLog = UltimoRegistroLog;
// Contexto
HealthWorkerService.AtualizarDadosContexto();
@ -4825,11 +4988,11 @@ namespace AgroBase.Models
DadosPerformance = dadosPerformance,
LivoxLidar = dadosLivox,
CoolerControl = dadosCooler,
Logs = Logs != null ? Logs.Where(x => x != null && x.Momento > ultimoRegistroLog).ToList() : new List<OperacaoSensoriamentoLogErrosModel>(),
UltimoRegistroLog = ultimoRegistroLog,
Logs = op.ListarEventosPendentes(),
UltimoRegistroLog = UltimoRegistroLog,
};
op.Sensoriamento = dadosAtualizados;
op.SubstituirSensoriamentoSnapshot(dadosAtualizados);
}
public OperacaoSensoriamentoConjuntoModel Clone()
@ -4896,7 +5059,23 @@ namespace AgroBase.Models
public List<DispositivoDetalhesModel> DispositivosMapeados { get; set; }
public Dictionary<T_Code, Dictionary<string, bool>> ModsCalibragem { get; set; }
public Dictionary<string, Dictionary<string, bool>> ModsCalibragemView { get; set; }
public void CopiarEstadoRuntimeDe(OperacaoSensoriamentoLogModel origem)
{
if (origem == null)
return;
OperacaoIniciada = origem.OperacaoIniciada;
Emergencia = origem.Emergencia;
Pausa = origem.Pausa;
Calibrando = origem.Calibrando;
Finalizando = origem.Finalizando;
DataInicio = origem.DataInicio;
DataFim = origem.DataFim;
StatusOperacaoAnterior = origem.StatusOperacaoAnterior;
}
public OperacaoSensoriamentoLogModel Clone()
{
return new OperacaoSensoriamentoLogModel()