ajustes ao salvar logs e calibragem
This commit is contained in:
parent
b953ff524e
commit
c4862a7ce4
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue