ajuste no gerenciamento de cameras no lado do c#, versionamento de arquivos e atualizacao das metricas do atuador

This commit is contained in:
Diego Freitas 2026-06-22 13:46:34 -03:00
parent 4de10bdb67
commit 824f2bad58
19 changed files with 1786 additions and 545 deletions

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using AgroBase.Models;
using AgroBase.Services;
using AgroBase.Services.Operadores;
using Microsoft.Web.WebView2.WinForms;
using Newtonsoft.Json;
using static MqttService;
@ -27,7 +28,7 @@ namespace AgroBase.Forms
cameraSolo = new CameraSoloModel()
{
Nome = "T",
Posicao = Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo.Count(),
Posicao = CameraWorkerService.CamerasSolo.Count(),
ArquivoLeitura = "ervas_T.json",
browser = null,
camera = new CameraService(),

View File

@ -36,9 +36,10 @@ namespace AgroBase.Forms.IHM
});
FuncoesGlobais.DefinirEventosPicBtn(picSalvar, "Erro ao salvar parâmetros!", async () =>
{
Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho = CameraCaminho.Clone();
Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo = new List<OAKCameraModel>(CamerasSolo);
VariaveisEquipamento.Parametros.camera_caminhho_id = CameraCaminho.Id;
VariaveisEquipamento.Parametros.camera_ervas_id = CamerasSolo?.Select(x => x.Id)?.ToList();
VariaveisEquipamento.Parametros.SalvarParametros();
CustomDialog.ShowDialog(
"Sucesso",
"Configuração de câmeras salva com sucesso!",
@ -258,7 +259,7 @@ namespace AgroBase.Forms.IHM
private async Task AtualizarCameras()
{
flwCamerasSolo.Controls.Clear();
var cameras = CameraWorkerService.GetListaCameras();
var cameras = CameraWorkerService.ListaCameras;
ListaCameras = cameras.Select(x => new OAKCameraModel()
{
Index = cameras.IndexOf(x),
@ -268,8 +269,8 @@ namespace AgroBase.Forms.IHM
Id = x.mx_id,
}).ToList();
CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.Clone();
CamerasSolo = new List<OAKCameraModel>(Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo);
CameraCaminho = CameraWorkerService.CameraCaminho?.Clone();
CamerasSolo = new List<OAKCameraModel>(CameraWorkerService.CamerasSolo);
for (int i = 0; i < Variaveis.OperacaoEmAndamento.Parametros.QtdCamerasSolo; i++)
{

View File

@ -1467,7 +1467,7 @@ namespace AgroBase.Forms.IHM
}
});
foreach (var camera in Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo)
foreach (var camera in CameraWorkerService.CamerasSolo)
{
Testes.AddRange(new List<ColunasDiagnosticoModel>()
{
@ -1508,7 +1508,7 @@ namespace AgroBase.Forms.IHM
List<string> CamerasIDs = CameraService.ObterListaCameras().Values.ToList();
string IDcamRua = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.Id;//.Replace("\\", "#").ToLower();
string IDcamRua = CameraWorkerService.CameraCaminho.Id;//.Replace("\\", "#").ToLower();
bool RuaConfigurado = !string.IsNullOrEmpty(IDcamRua) && IDcamRua != "Sem video";
idx = PreencheDadosLinha(grid, Testes, idx, RuaConfigurado ? 1 : 0);
@ -1519,9 +1519,9 @@ namespace AgroBase.Forms.IHM
bool RuaVideo = await VerificaImagemCamera(CamerasIDs, IDcamRua, RuaConectado);
idx = PreencheDadosLinha(grid, Testes, idx, RuaVideo ? 1 : 0);
foreach (var camera in Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo)
foreach (var camera in CameraWorkerService.CamerasSolo)
{
string IDcamSolo = (Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo[0].Id);//.Replace("\\", "#").ToLower();
string IDcamSolo = (CameraWorkerService.CamerasSolo[0].Id);//.Replace("\\", "#").ToLower();
bool SoloConfigurado = !string.IsNullOrEmpty(IDcamSolo) && IDcamSolo != "Sem video";
idx = PreencheDadosLinha(grid, Testes, idx, SoloConfigurado ? 1 : 0);

View File

@ -160,7 +160,7 @@ namespace AgroBase.Forms.IHM
private async Task AtualizarControlesUI()
{
var Labels = this.Controls.OfType<Label>().Where(y => y.Name.Contains("lblD_")).ToList();
var listaCameras = CameraWorkerService.GetListaCameras();
var listaCameras = CameraWorkerService.ListaCameras;
foreach (var lbl in Labels)
{
T_Code Dispositivo = (T_Code)Enum.Parse(typeof(T_Code), lbl.Name.Split('_')[1]);
@ -172,7 +172,7 @@ namespace AgroBase.Forms.IHM
bool modDesconectado = Modulo == null || Modulo.Status == StatusModulo.Desconectado;
int idx = Convert.ToInt32(Mod_ID);
Mod_ID = T_Code.Cam.ToString() + "_" + Mod_ID;
var CameraSolo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo.Count() > idx ? Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo[idx] : null;
var CameraSolo = CameraWorkerService.CamerasSolo.Count() > idx ? CameraWorkerService.CamerasSolo[idx] : null;
lbl.ForeColor = modDesconectado || CameraSolo?.Id == null ?
Color.Black
: !listaCameras.Any(x => x.mx_id == CameraSolo.Id) ?
@ -183,7 +183,7 @@ namespace AgroBase.Forms.IHM
{
var Modulo = SerialService.DispositivosMapeados.FirstOrDefault(x => x.Dispositivo == Dispositivo);
bool modDesconectado = Modulo == null || Modulo.Status == StatusModulo.Desconectado;
var CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho;
var CameraCaminho = CameraWorkerService.CameraCaminho;
lbl.ForeColor = modDesconectado || CameraCaminho?.Id == null ?
Color.Black
: !listaCameras.Any(x => x.mx_id == CameraCaminho.Id)
@ -195,7 +195,7 @@ namespace AgroBase.Forms.IHM
var Modulo = SerialService.DispositivosMapeados.FirstOrDefault(x => x.Dispositivo == T_Code.Snr);
bool modDesconectado = Modulo == null || Modulo.Status == StatusModulo.Desconectado;
var saudeImu = HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == Dispositivo);
var CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho;
var CameraCaminho = CameraWorkerService.CameraCaminho;
lbl.ForeColor =
modDesconectado || saudeImu == null ? Color.Black
: saudeImu.status == StatusModulo.Conectado ? Color.Blue

View File

@ -68,11 +68,11 @@ namespace AgroBase.Forms.Operacoes
nudPotenciaAgitador.Value = 100;
flwCamerasSolo.Controls.Clear();
for (int i = 0; i < Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo.Count(); i++)
for (int i = 0; i < CameraWorkerService.CamerasSolo.Count(); i++)
{
Panel pnlCam = new Panel()
{
Name = "pnlCamSolo_" + Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo[i].Name,
Name = "pnlCamSolo_" + CameraWorkerService.CamerasSolo[i].Name,
Width = 290,
Height = 166,
BackgroundImageLayout = ImageLayout.Zoom,
@ -230,7 +230,7 @@ namespace AgroBase.Forms.Operacoes
try
{
var frame = await WeedWorkerService.GetCameraFrame(TipoFrameCamera.Debug);
Panel pnl = FuncoesGlobais.FindControlRecursive<Panel>(flwCamerasSolo, "pnlCamSolo_" + Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo[0].Name);
Panel pnl = FuncoesGlobais.FindControlRecursive<Panel>(flwCamerasSolo, "pnlCamSolo_" + CameraWorkerService.CamerasSolo[0].Name);
AtualizarImagemPainel(pnl, frame?.image());
}
finally

View File

@ -2,6 +2,7 @@
using AgroBase.Models;
using AgroBase.Models.Modules;
using AgroBase.Services;
using AgroBase.Services.Operadores;
using System;
using System.Collections.Generic;
using System.Linq;
@ -69,7 +70,7 @@ namespace AgroBase.Forms.Sensoriamento
Cameras = CameraService.ObterListaCameras();
cmbCaminho.Items.Clear();
cmbCaminho.Items.AddRange(Cameras.Values.ToArray());
string IDrua = ((SensoriamentoModel)Disp.Dados).CameraCaminho.Id;
string IDrua = CameraWorkerService.CameraCaminho.Id;
if (Cameras.Any(x => x.Value == IDrua))
{
cmbCaminho.SelectedIndex = cmbCaminho.Items.IndexOf(IDrua);
@ -79,7 +80,7 @@ namespace AgroBase.Forms.Sensoriamento
cmbCaminho.Text = IDrua;
}
var CamerasSolo = ((SensoriamentoModel)Disp.Dados).CamerasSolo;
var CamerasSolo = CameraWorkerService.CamerasSolo;
cmbSoloEsq.Items.Clear();
cmbSoloEsq.Items.AddRange(Cameras.Values.ToArray());
@ -196,7 +197,7 @@ namespace AgroBase.Forms.Sensoriamento
_Disp.Dados._EnderecoCAN_Rx = FuncoesGlobais.StringToHexByte(txtEnderecoCAN_Rx.Text);
_Disp.Dados.RequisitarDados = chbRequisitarDados.Checked;
_Disp.Dados.Sensores = Sensores.ToList();
_Disp.Dados.CameraCaminho.Id = cmbCaminho.Text;
CameraWorkerService.CameraCaminho.Id = cmbCaminho.Text;
//_Disp.Dados.CamerasSolo.Clear();
//_Disp.Dados.CameraID_Solo.AddRange(new string[] { cmbSoloEsq.Text, cmbSoloDir.Text });
_Disp.SalvarParametros(_Disp.Dados);

View File

@ -141,6 +141,8 @@ namespace AgroBase.Forms
RedisService.Iniciar();
RedisService.LimparDadosIniciais();
GeneralJoystick.IniciarRotinas();
await Variaveis.IniciarMqttAsync(_appCts.Token)
.ConfigureAwait(false);
@ -148,10 +150,9 @@ namespace AgroBase.Forms
* Hoje pode ficar comentado se UDP não for usado no rover,
* mas o método fica aqui no lugar certo do lifecycle.
*/
//Variaveis.IniciarUDP();
Variaveis.IniciarUDP();
APIService.IniciarRotinas();
GeneralJoystick.IniciarRotinas();
VersionamentoService.IniciarRotinas();
LivoxManagerProcess.Start();

View File

@ -41,7 +41,7 @@ namespace AgroBase.Forms
private void AtualizarListaCameras()
{
cmbCameras.Items.Clear();
cmbCameras.Items.AddRange(CameraWorkerService.GetListaCameras().Select(x => x.modelo).ToArray());
cmbCameras.Items.AddRange(CameraWorkerService.ListaCameras.Select(x => x.modelo).ToArray());
cmbCameras.SelectedIndex = -1;
}

View File

@ -84,10 +84,10 @@ namespace AgroBase.Forms
var Camera = ListaCameras[cmbDispositivo.SelectedIndex];
if (Camera.imu == null) return;
if (Camera.mx_id == Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id)
Camera.imu = Variaveis.OperacaoEmAndamento.DispSen.Dados?.CameraCaminho?.cam?.imu;
else if (Variaveis.OperacaoEmAndamento.DispSen.Dados?.CamerasSolo?.Any(x => x?.Id == Camera.mx_id) ?? false)
Camera.imu = Variaveis.OperacaoEmAndamento.DispSen.Dados?.CamerasSolo?.FirstOrDefault(x => x?.Id == Camera.mx_id)?.cam?.imu;
if (Camera.mx_id == CameraWorkerService.CameraCaminho?.Id)
Camera.imu = CameraWorkerService.CameraCaminho?.cam?.imu;
else if (CameraWorkerService.CamerasSolo?.Any(x => x?.Id == Camera.mx_id) ?? false)
Camera.imu = CameraWorkerService.CamerasSolo?.FirstOrDefault(x => x?.Id == Camera.mx_id)?.cam?.imu;
Imu = new OperacaoSensoriamentoLogImuModel()
{
@ -108,7 +108,7 @@ namespace AgroBase.Forms
private void AtualizarListaCameras()
{
ListaCameras = CameraWorkerService.GetListaCameras();
ListaCameras = CameraWorkerService.ListaCameras;
cmbDispositivo.Items.Clear();
cmbDispositivo.Items.AddRange(ListaCameras.Select(x => x.modelo).ToArray());

View File

@ -206,7 +206,7 @@ namespace AgroBase.Models.Modules
private HashSet<int> _bombasAtivasIntervaloAnterior = new HashSet<int>();
private Dictionary<int, double> _potenciaBombasIntervaloAnterior = new Dictionary<int, double>();
private double _vazaoIntervaloAnteriorMLs;
private const double IntervaloMaximoIntegracaoSeg = 0.5;
private const double IntervaloMaximoIntegracaoSeg = 2.0;
private const double IdadeMaximaTelemetriaSeg = 2.5;
private double _tempoMovimentoAnteriorSeg;
@ -382,10 +382,20 @@ namespace AgroBase.Models.Modules
Variaveis.OperacaoEmAndamento.Iniciado &&
!operacaoConcluida;
bool atuadorComTelemetriaRecente =
DadosLeitura != null &&
DadosLeitura.UltimoComandoRespondido != DateTime.MinValue &&
DadosLeitura
.UltimoComandoRespondido
.AddMilliseconds(TempoLimiteConexao) > agora;
bool atuadorTemEstrutura =
(BicosPulverizadores?.Count ?? 0) > 0;
bool atuadorDisponivel =
operacaoAtiva &&
DadosLeitura != null &&
DadosLeitura.Conectado;
atuadorTemEstrutura &&
atuadorComTelemetriaRecente;
bool pulverizadorAutomatico =
Variaveis.OperacaoEmAndamento
@ -470,8 +480,7 @@ namespace AgroBase.Models.Modules
_ultimoTickMetricas) /
(double)Stopwatch.Frequency;
if (dt > 0 &&
dt <= IntervaloMaximoIntegracaoSeg)
if (dt > 0 && dt <= IntervaloMaximoIntegracaoSeg)
{
double deltaMovimentoSeg = 0;
@ -506,6 +515,12 @@ namespace AgroBase.Models.Modules
* Não inventa tempo nem volume.
*/
ZerarVazoesInstantaneas();
if (dt > IntervaloMaximoIntegracaoSeg)
{
Variaveis.MostrarLog("[ATU/METRICAS] Integração ignorada por dt alto: " + dt.ToString("0.000") + "s");
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(Dispositivo, StatusModulo.Alerta, 50, "[ATU/METRICAS] Integração ignorada por dt alto: " + dt.ToString("0.000") + "s");
}
}
}
@ -1320,7 +1335,7 @@ namespace AgroBase.Models.Modules
Dados.BombasPressurizadoras.ForEach(x => x.Funcoes = x.Funcoes.Distinct().ToList());
ReiniciarLeituras(Dados);
PrepararAtuadorParaOperacao(Dados);
PrepararAtuadorParaOperacao(Dados, resetarComandos: true);
}
public void ReiniciarLeituras(AtuadorModel Dados)
@ -1337,13 +1352,18 @@ namespace AgroBase.Models.Modules
{
x.ComandoAtuar = false;
x.AnguloControle = 0;
x.Atuacoes = 0;
x.VazaoInstantaneaMLs = 0;
x.VolumeVazadoML = 0;
x.VazaoMediaMLs = 0;
x.TempoAtuado = 0;
x.TempoAtuadoEmMovimento = 0;
x.TempoComFluxo = 0;
x.ReiniciarHistoricoAtuacao(true);
x.ValoresLeituras.ForEach(l =>
{
l.atual = new CanCommandFreqModel<dynamic>();
@ -1386,10 +1406,19 @@ namespace AgroBase.Models.Modules
}
}
private void PrepararAtuadorParaOperacao(AtuadorModel Dados)
public void PrepararAtuadorParaOperacao()
{
PrepararAtuadorParaOperacao(this, resetarComandos: false);
}
private void PrepararAtuadorParaOperacao(AtuadorModel Dados, bool resetarComandos)
{
var dadosAtuador = Dados;
var controle = Variaveis.OperacaoEmAndamento.Parametros?.Controle;
var controle =
Variaveis
.OperacaoEmAndamento
.Parametros?
.Controle;
if (dadosAtuador == null || controle == null)
return;
@ -1398,7 +1427,13 @@ namespace AgroBase.Models.Modules
if (bomba != null)
{
bomba.ComandoAtuar = false;
if (resetarComandos)
bomba.ComandoAtuar = false;
/*
* Estes setpoints precisam existir antes do primeiro comando
* físico da bomba em operação automática.
*/
bomba.PotenciaSP = 100;
bomba.PressaoSP = controle.AtuPressaoLinha;
bomba.Controle.Modo = controle.AtuModoControle;
@ -1408,12 +1443,113 @@ namespace AgroBase.Models.Modules
if (agitador != null)
{
agitador.ComandoAtuar = false;
if (resetarComandos)
agitador.ComandoAtuar = false;
/*
* O agitador tem a própria fonte de verdade em
* HerbicideCounterService.AtualizarAgitadorCalda().
*/
agitador.PotenciaSP = 0;
agitador.PressaoSP = -1;
}
}
public void SincronizarBombaPressurizadoraAutomatica(bool forcar = false)
{
var operacao = Variaveis.OperacaoEmAndamento;
var controle =
operacao?
.Parametros?
.Controle;
var bomba = BombaPressurizadora;
if (operacao == null || controle == null || bomba == null || !bomba.Inicializado)
return;
bool pulverizadorAutomatico = controle.PulverizadorAutomatico;
if (!pulverizadorAutomatico)
return;
bool operacaoConcluida = operacao.Sensoriamento?.StatusOperacao == StatusOperacao.Concluido;
bool atuacaoPermitida =
operacao.Iniciado &&
!operacao.Finalizando &&
!operacao.Emergencia &&
!operacaoConcluida &&
!operacao.Pausa;
/*
* Reaplica setpoints sempre antes de sincronizar.
* Assim, se ReiniciarLeituras() zerou a bomba no início da operação,
* ela não fica presa em potência/pressão zero.
*/
PrepararAtuadorParaOperacao();
bool statusDesejado =
bomba.Comandar &&
atuacaoPermitida &&
ContadorCarga.PulverizadorLiberado;
/*
* ComandoAtuar passa a representar também o comando lógico automático.
* Isso deixa o log BombaLigada coerente com o que o software deseja.
*/
bomba.ComandoAtuar = statusDesejado;
double potenciaEfetiva =
statusDesejado
? bomba.PotenciaSP
: 0;
double pressaoEfetiva =
bomba.Sensor_ID_Num > -1
? (
statusDesejado
? bomba.PressaoSP
: 0
)
: bomba.PressaoSP;
Estado estadoDesejado =
statusDesejado
? Estado.Ligado
: Estado.Desligado;
bool estadoDivergente =
estadoDesejado != bomba._EstadoLeitura;
bool potenciaDivergente =
Math.Abs(
bomba._PotenciaSP -
potenciaEfetiva
) > 1.0;
bool pressaoDivergente =
bomba.Sensor_ID_Num > -1 &&
Math.Abs(
bomba._PressaoSP -
pressaoEfetiva
) > 0.05;
if (forcar ||
estadoDivergente ||
potenciaDivergente ||
pressaoDivergente)
{
EnviarComandoFisicoBomba(
bomba,
statusDesejado,
potenciaEfetiva,
pressaoEfetiva
);
}
}
public async Task<bool> VerificaDispositivoConectado()
{
if (!CanManager.CanService.IsConnected) return false;
@ -1797,15 +1933,19 @@ namespace AgroBase.Models.Modules
*
* Ela não depende de um ComandoAtuar antigo.
*/
else if (
ehBombaPrincipal &&
pulverizadorAutomatico
)
else if (ehBombaPrincipal && pulverizadorAutomatico)
{
statusDesejado =
bomba.Comandar &&
atuacaoPermitida &&
ContadorCarga.PulverizadorLiberado;
/*
* Mantém o modelo coerente com o comando automático.
* Sem isso, o log BombaLigada pode ficar false mesmo quando
* o controle automático deseja a bomba ligada.
*/
bomba.ComandoAtuar = statusDesejado;
}
/*
* Agitador e comandos manuais.

View File

@ -56,8 +56,6 @@ namespace AgroBase.Models.Modules
public List<SensorSinaleiroModel> Sinaleiros { get; set; }
public List<SensorReleModel> Reles { get; set; }
public List<SensorServoModel> Servos { get; set; }
public OAKCameraModel CameraCaminho { get; set; } = new OAKCameraModel();
public List<OAKCameraModel> CamerasSolo { get; set; } = new List<OAKCameraModel>();
public UltrasonicA05Configuracoes ConfiguracaoSensorUltrassom { get; set; }
private SenHandler _SenHandler = new SenHandler();
public LoRaEspService loRaService { get; set; } = new LoRaEspService();
@ -86,8 +84,6 @@ namespace AgroBase.Models.Modules
_EnderecoCAN_Rx = 0x11,
_TaxaAmostragem = 1000,
RequisitarDados = true,
CameraCaminho = new OAKCameraModel(),
CamerasSolo = new List<OAKCameraModel>(Variaveis.OperacaoEmAndamento.Parametros.QtdCamerasSolo ?? VariaveisEquipamento.QuantidadeCamerasSolo),
// 21 ~ 23
Sinaleiros = new List<SensorSinaleiroModel>()
{
@ -1169,7 +1165,7 @@ namespace AgroBase.Models.Modules
Descricao = "Luminosidade Ambiente",
ID_Num = 31,
Componente = S_Code.sLUZ,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1221,7 +1217,7 @@ namespace AgroBase.Models.Modules
Descricao = "Indice de radiacao UV Ambiente",
ID_Num = 32,
Componente = S_Code.sIUV,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1271,7 +1267,7 @@ namespace AgroBase.Models.Modules
Descricao = "Gas e Fumaca",
ID_Num = 33,
Componente = S_Code.sGAS,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1329,7 +1325,7 @@ namespace AgroBase.Models.Modules
Descricao = "Monoxido de carbono",
ID_Num = 34,
Componente = S_Code.sGAS,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1387,7 +1383,7 @@ namespace AgroBase.Models.Modules
Descricao = "Qualidade do ar",
ID_Num = 35,
Componente = S_Code.sQAR,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1475,7 +1471,7 @@ namespace AgroBase.Models.Modules
Descricao = "Temperatura e Umidade Ambiente",
ID_Num = 36,
Componente = S_Code.sSHT,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1538,7 +1534,7 @@ namespace AgroBase.Models.Modules
Descricao = "Chuva",
ID_Num = 37,
Componente = S_Code.sCHV,
Aferir = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
@ -1589,8 +1585,8 @@ namespace AgroBase.Models.Modules
Descricao = "Angulo Direcional ET",
ID_Num = 91,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
@ -1644,8 +1640,8 @@ namespace AgroBase.Models.Modules
Descricao = "Angulo Direcional DT",
ID_Num = 92,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
@ -1699,8 +1695,8 @@ namespace AgroBase.Models.Modules
Descricao = "Angulo Direcional EF",
ID_Num = 93,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
@ -1754,8 +1750,8 @@ namespace AgroBase.Models.Modules
Descricao = "Angulo Direcional DF",
ID_Num = 94,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Aferir = false,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
@ -2409,24 +2405,6 @@ namespace AgroBase.Models.Modules
Sinaleiros.First(x => x.ID == "LEDOP").Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(StatusLED.Piscando, status_operacao: _statusOperacao);
}
public void AtualizarDadosCameras()
{
if (CameraCaminho?.Id != null)
{
var att = CameraWorkerService.AtualizarDadosCamera(CameraCaminho.Id);
if (att != null) CameraCaminho = att;
}
for (int i = 0; i < CamerasSolo?.Count; i++)
{
if (CamerasSolo[i]?.Id != null)
{
var att = CameraWorkerService.AtualizarDadosCamera(CamerasSolo[i].Id);
if (att != null) CamerasSolo[i] = att;
}
}
}
}
public class SensorModel

View File

@ -1188,6 +1188,10 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.Sensoriamento.UltimoRegistroLog = DateTime.Now;
tmrLogs?.Start();
var dispSen = Variaveis.OperacaoEmAndamento.DispSen;
var dispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
var dispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
Variaveis.OperacaoEmAndamento.Iniciado = true;
Variaveis.OperacaoEmAndamento.Simulando = false;
Variaveis.OperacaoEmAndamento.Treinando = false;
@ -1204,9 +1208,10 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.Sensoriamento.AtualizarDados();
Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.ReiniciarLeituras(DispMvd?.Dados);
Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.ReiniciarLeituras(DispAtu?.Dados);
Variaveis.OperacaoEmAndamento.DispSen?.Dados?.ReiniciarLeituras(DispSen?.Dados);
dispMvd?.Dados?.ReiniciarLeituras(dispMvd?.Dados);
dispAtu?.Dados?.ReiniciarLeituras(dispAtu?.Dados);
dispSen?.Dados?.ReiniciarLeituras(dispSen?.Dados);
dispAtu?.Dados?.PrepararAtuadorParaOperacao();
Variaveis.OperacaoEmAndamento.ModsCalibragem = new Dictionary<T_Code, Dictionary<string, bool>>()
{
@ -1222,19 +1227,36 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.Controle.Reiniciar();
Variaveis.OperacaoEmAndamento.ControleAnterior.Reiniciar();
double distanciaPercorrida = DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0;
double tempoAtuado = DispAtu?.Dados?.TempoAtuado ?? 0;
Variaveis.OperacaoEmAndamento.DispSen?.Dados?.ContadorCarga?.AtualizarConsumo(distanciaPercorrida);
Variaveis.OperacaoEmAndamento.DispSen?.Dados?.ContadorCarga?.IniciarOperacao(distanciaPercorrida);
Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.ContadorCarga?.IniciarOperacao(distanciaPercorrida, tempoAtuado);
double distanciaPercorrida = dispMvd?.Dados?.DistanciaPercorridaTotal ?? 0;
double tempoAtuado = dispAtu?.Dados?.TempoAtuado ?? 0;
dispSen?.Dados?.ContadorCarga?.AtualizarConsumo(distanciaPercorrida);
dispSen?.Dados?.ContadorCarga?.IniciarOperacao(distanciaPercorrida);
dispAtu?.Dados?.ContadorCarga?.IniciarOperacao(distanciaPercorrida, tempoAtuado);
Variaveis.OperacaoEmAndamento.GPSTrajetoria = new List<GPSModel>();
Variaveis.OperacaoEmAndamento.Trajetoria?.ProjetarTrajetoriaFixa();
Variaveis.OperacaoEmAndamento.Trajetoria.VerificacaoInicialMeioRuaConcluida = false;
RedisService.Publish(CmdKey.ManagerWorkerRx, JsonConvert.SerializeObject(new { cmd = ManagerWorkerCommandType.IniciarMPC }));
Trajetoria?.ProjetarTrajetoriaFixa();
Trajetoria.VerificacaoInicialMeioRuaConcluida = false;
/*
* Mantém a operação bloqueada enquanto calibra/prepara.
* O Manager/MPC deve começar depois da calibração.
*/
Pausa = true;
await RealizarCalibragemInicialAsync(true);
Pausa = false;
RedisService.Publish(
CmdKey.ManagerWorkerRx,
JsonConvert.SerializeObject(
new
{
cmd = ManagerWorkerCommandType.IniciarMPC
}
)
);
}
public async Task FinalizarOperacao()
@ -1291,8 +1313,14 @@ namespace AgroBase.Models
{
if (Variaveis.OperacaoEmAndamento.Calibrando) return;
bool modsCalibrados = Variaveis.OperacaoEmAndamento.ModsCalibragem.All(x => x.Value.Values.Count > 0 && x.Value.Values.All(y => y == true));
if (!forcar && modsCalibrados) return;
var modsCalibragem = Variaveis.OperacaoEmAndamento.ModsCalibragem;
bool modsCalibrados = modsCalibragem.All(x => x.Value.Values.Count > 0 && x.Value.Values.All(y => y == true));
if (!forcar && modsCalibrados)
{
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(T_Code.Mod, StatusModulo.Operante, 100, "Todos os módulos já foram calibrados anteriormente, não irá calibrar novamente.");
return;
}
var DispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
var DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
@ -1301,9 +1329,9 @@ namespace AgroBase.Models
var pControle = Variaveis.OperacaoEmAndamento.Parametros.Controle;
var _Controle = Variaveis.OperacaoEmAndamento.Controle;
bool calibragemAtuNecessaria = pControle.PulverizadorAutomatico && (DispAtu?.Dados != null && sv.Contains(s.FirstOrDefault(x => x.modulo == T_Code.Atu)?.status ?? StatusModulo.Desconectado) && (forcar || Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Atu].Any(x => !x.Value) || !Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Atu].Any()));
bool calibragemDirNecessaria = (DispMvd?.Dados != null && sv.Contains(s.FirstOrDefault(x => x.modulo == T_Code.Dir)?.status ?? StatusModulo.Desconectado) && (forcar || Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Dir].Any(x => !x.Value) || !Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Dir].Any()));
bool calibragemMovNecessaria = (DispMvd?.Dados != null && sv.Contains(s.FirstOrDefault(x => x.modulo == T_Code.Mov)?.status ?? StatusModulo.Desconectado) && (forcar || Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Mov].Any(x => !x.Value) || !Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Mov].Any()));
bool calibragemAtuNecessaria = pControle.PulverizadorAutomatico && (DispAtu?.Dados != null && sv.Contains(s.FirstOrDefault(x => x.modulo == T_Code.Atu)?.status ?? StatusModulo.Desconectado) && (forcar || modsCalibragem[T_Code.Atu].Any(x => !x.Value) || !modsCalibragem[T_Code.Atu].Any()));
bool calibragemDirNecessaria = (DispMvd?.Dados != null && sv.Contains(s.FirstOrDefault(x => x.modulo == T_Code.Dir)?.status ?? StatusModulo.Desconectado) && (forcar || modsCalibragem[T_Code.Dir].Any(x => !x.Value) || !modsCalibragem[T_Code.Dir].Any()));
bool calibragemMovNecessaria = (DispMvd?.Dados != null && sv.Contains(s.FirstOrDefault(x => x.modulo == T_Code.Mov)?.status ?? StatusModulo.Desconectado) && (forcar || modsCalibragem[T_Code.Mov].Any(x => !x.Value) || !modsCalibragem[T_Code.Mov].Any()));
if (!calibragemAtuNecessaria && !calibragemDirNecessaria && !calibragemMovNecessaria) return;
@ -1633,6 +1661,8 @@ namespace AgroBase.Models
}
case T_Code.Atu:
{
Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.SincronizarBombaPressurizadoraAutomatica();
for (int i = 0; i < _Controle.Bicos.Count; i++)
{
Estado comandoEstado = _Controle.Bicos[i].ComandoAtuar ? Estado.Ligado : Estado.Desligado;
@ -2971,6 +3001,27 @@ namespace AgroBase.Models
SalvarLogLine(T_Code.Dir.ToString(), logDir);
});
try
{
var dispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
var dispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
dispAtu?.Dados?.AtualizarMetricasOperacao();
dispAtu?.Dados?.ContadorCarga?.AtualizarConsumo(
dispMvd?.Dados?.DistanciaPercorridaTotal ?? 0,
Variaveis.OperacaoEmAndamento.TempoDecorridoSegs,
dispMvd?.Dados?.VelocidadeMedia ?? 0
);
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[SalvarLogs/ATU] Erro ao atualizar métricas antes do log: " +
ex.Message
);
}
AtuSensoriamentoLogModel logAtu = new AtuSensoriamentoLogModel()
{
Momento = Agora,
@ -3041,7 +3092,7 @@ namespace AgroBase.Models
atu = T_Code.Atu.ToString(),
sen = T_Code.Sen.ToString(),
cam_caminho = T_Code.Snr.ToString(),
cam_solo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo.Select((x, i) => x.Dispositivo.ToString() + i).ToList(),
cam_solo = CameraWorkerService.CamerasSolo.Select((x, i) => x.Dispositivo.ToString() + i).ToList(),
};
File.WriteAllText(ArquivoOp, JsonConvert.SerializeObject(data));
}
@ -3078,9 +3129,9 @@ namespace AgroBase.Models
private void SalvarLogCameraSolo()
{
foreach (var Camera in Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo)
foreach (var Camera in CameraWorkerService.CamerasSolo)
{
string camera_name = Camera.Dispositivo.ToString() + Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo.IndexOf(Camera);
string camera_name = Camera.Dispositivo.ToString() + CameraWorkerService.CamerasSolo.IndexOf(Camera);
string Caminho = Variaveis.CaminhoOperacoes + Variaveis.OperacaoEmAndamento.ID + "/" + camera_name;
if (!Directory.Exists(Caminho))
{
@ -3104,7 +3155,9 @@ namespace AgroBase.Models
private void SalvarLogCameraCaminho()
{
var Camera = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho;
var Camera = CameraWorkerService.CameraCaminho;
if (Camera == null)
return;
string Caminho = Variaveis.CaminhoOperacoes + Variaveis.OperacaoEmAndamento.ID + "/" + Camera.Dispositivo;
if (!Directory.Exists(Caminho))
@ -3602,6 +3655,7 @@ namespace AgroBase.Models
public List<ManagerWorkerMessageResponseModulosPendentesModel> ModulosSaude { get; set; }
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 CanServiceTipo TipoCanAtivo { get; set; }
public List<OperacaoSensoriamentoLogErrosModel> Logs { get; set; }
public DateTime UltimoRegistroLog { get; set; } = DateTime.MinValue;
@ -3631,6 +3685,7 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.DadosPerformance.AtualizarDadosPerformance();
VisualWorkerService.AtualizarAnalise();
WeedWorkerService.AtualizarAnalise();
CameraWorkerService.AtualizarDadosCameras();
GPSService.AtualizaDadosRedis();
LivoxManagerProcess.AtualizarLeitura();
MovimentacaoMovimentoCompensadoModel.AtualizarDados();
@ -3649,7 +3704,6 @@ namespace AgroBase.Models
double tmp_max = Math.Max(Variaveis.OperacaoEmAndamento.DadosPerformance?.GPU_temp ?? 0, Variaveis.OperacaoEmAndamento.DadosPerformance?.CPU_temp ?? 0);
_DispMvd?.Dados?.AtualizarDadosFreio();
_DispSen?.Dados?.AtualizarStatusSinaleiros(Variaveis.OperacaoEmAndamento.Parametros?.Modo ?? ModoOperacao.NaoDefinido, Variaveis.OperacaoEmAndamento.StatusAtual);
_DispSen?.Dados?.AtualizarDadosCameras();
_DispAtu?.Dados?.AtualizarMetricasOperacao();
_Cooler?.Atualizar(tmp_max, DateTime.Now);
_Bateria?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0);
@ -3665,7 +3719,7 @@ namespace AgroBase.Models
var bombaLinha = _DispAtu?.Dados?.BombaPressurizadora;
var dadosAtuador = _DispAtu != null ? new OperacaoSensoriamentoLogAtuModel()
{
QtdCamerasSolo = _DispSen.Dados.CamerasSolo.Count,
QtdCamerasSolo = CameraWorkerService.CamerasSolo.Count,
CapacidadeReservatorio = VariaveisEquipamento.CapacidadeReservatorio,
HerbicidaConsumido = _Reservatorio.VolumeConsumidoTotal_L,
HerbicidaConsumidoOperacao = _Reservatorio.VolumeConsumidoOperacao_L,
@ -3885,6 +3939,7 @@ namespace AgroBase.Models
ModulosSaude = HealthWorkerService.ModulosSaude.ToList(),
DispositivosMapeados = new List<DispositivoDetalhesModel>(SerialService.DispositivosMapeados),
ModsCalibragem = new Dictionary<T_Code, Dictionary<string, bool>>(Variaveis.OperacaoEmAndamento.ModsCalibragem),
ModsCalibragemView = Variaveis.OperacaoEmAndamento.ModsCalibragem?.ToDictionary(x => x.Key.ToString(), x => new Dictionary<string, bool>(x.Value ?? new Dictionary<string, bool>())) ?? new Dictionary<string, Dictionary<string, bool>>(),
Logs = Logs != null ? Logs.Where(x => x?.Momento >= UltimoRegistroLog).ToList() : new List<OperacaoSensoriamentoLogErrosModel>(),
CoolerControl = dadosCooler
};
@ -3936,6 +3991,7 @@ namespace AgroBase.Models
ModulosSaude = new List<ManagerWorkerMessageResponseModulosPendentesModel>(ModulosSaude),
DispositivosMapeados = new List<DispositivoDetalhesModel>(DispositivosMapeados),
ModsCalibragem = new Dictionary<T_Code, Dictionary<string, bool>>(ModsCalibragem),
ModsCalibragemView = new Dictionary<string, Dictionary<string, bool>>(ModsCalibragemView),
Logs = new List<OperacaoSensoriamentoLogErrosModel>(Logs ?? new List<OperacaoSensoriamentoLogErrosModel>()),
UltimoRegistroLog = UltimoRegistroLog,
CoolerControl = CoolerControl?.Clone(),

View File

@ -1149,12 +1149,8 @@ namespace AgroBase.Models.Operacoes
private void AtualizarCameras()
{
CameraWorkerService.GetListaCameras();
Cameras = CameraWorkerService.ListaCameras != null
? new List<CameraWorkerItemModel>(
CameraWorkerService.ListaCameras
)
? new List<CameraWorkerItemModel>(CameraWorkerService.ListaCameras)
: new List<CameraWorkerItemModel>();
}

View File

@ -34,22 +34,30 @@ namespace AgroBase.Models.Operadores
public class CameraWorkerItemModel
{
public T_Code dispositivo { get; set; }
// Novos campos identificados no seu JSON
public double timestamp { get; set; }
public bool presente_scan { get; set; }
public string status_scan { get; set; }
public double manager_reset_ts { get; set; }
public bool iniciando { get; set; } // Presente no JSON
// Campos que já existiam na sua classe
public string mx_id { get; set; }
public string versao { get; set; }
public string modelo { get; set; }
public bool iniciado { get; set; }
public bool rodando { get; set; }
// Campos opcionais (coloque '?' para evitar erros se forem nulos ou ausentes)
public T_Code dispositivo { get; set; }
public string versao { get; set; }
public string modelo { get; set; }
public bool tem_depth { get; set; }
public bool tem_imu { get; set; }
public bool streaming { get; set; }
public double fps { get; set; }
public int stream_w { get; set; }
public int stream_h { get; set; }
public double stream_kbps { get; set; }
public double stream_last_frame { get; set; }
public SaudeWorkerModel saude { get; set; }
public CameraWorkerItemPerformanceModel performance { get; set; }
public OAKImuDataModel imu { get; set; }
@ -60,8 +68,8 @@ namespace AgroBase.Models.Operadores
{
Id = mx_id,
Dispositivo = dispositivo,
Version = versao,
Name = modelo,
Version = versao ?? "Desconhecida",
Name = modelo ?? "Desconhecido",
cam = this
};
}

View File

@ -136,31 +136,59 @@ namespace AgroBase.Services
private double _distanciaTotalAnterior_m;
private double _tempoTotalAnterior_s;
private bool _inicioOperacaoPendente;
private double _distanciaInicioOperacaoPendente_m;
private double _tempoInicioOperacaoPendente_s;
private bool _inicializado;
public void IniciarOperacao(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual)
{
/*
* Antes, se o contador ainda não tivesse recebido uma leitura
* válida do reservatório, o início era ignorado.
*
* Isso fazia VolumeConsumidoOperacao_L ficar zero durante toda
* a operação quando a primeira leitura válida chegava depois
* do comando de iniciar.
*/
if (!_inicializado)
{
OperacaoAtiva = true;
OperacaoFinalizada = false;
_inicioOperacaoPendente = true;
_distanciaInicioOperacaoPendente_m =
distanciaTotalMetrosAtual;
_tempoInicioOperacaoPendente_s =
tempoTotalSegundosAtual;
ResetarJanelas();
return;
}
_volumeInicialOperacao_L = VolumeAtual_L;
_distanciaTotalAnterior_m = distanciaTotalMetrosAtual;
_tempoTotalAnterior_s = tempoTotalSegundosAtual;
_distanciaTotalAnterior_m =
distanciaTotalMetrosAtual;
_tempoTotalAnterior_s =
tempoTotalSegundosAtual;
OperacaoAtiva = true;
OperacaoFinalizada = false;
_inicioOperacaoPendente = false;
ResetarJanelas();
}
public void FinalizarOperacao()
{
if (!OperacaoAtiva)
if (!OperacaoAtiva && !_inicioOperacaoPendente)
return;
OperacaoAtiva = false;
OperacaoFinalizada = true;
_inicioOperacaoPendente = false;
}
public void AtualizarConsumo(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual, double velocidadeAtual_m_s)
@ -202,6 +230,26 @@ namespace AgroBase.Services
if (!_inicializado)
{
Inicializar(distanciaTotalMetrosAtual, tempoTotalSegundosAtual);
if (OperacaoAtiva || _inicioOperacaoPendente)
{
/*
* Primeira leitura válida depois do comando de iniciar.
* A operação passa a usar o volume atual como referência
* inicial, sem perder o estado de OperacaoAtiva.
*/
_volumeInicialOperacao_L = VolumeAtual_L;
_distanciaTotalAnterior_m = distanciaTotalMetrosAtual;
_tempoTotalAnterior_s = tempoTotalSegundosAtual;
OperacaoAtiva = true;
OperacaoFinalizada = false;
_inicioOperacaoPendente = false;
ResetarJanelas();
}
AtualizarAutonomia(velocidadeAtual_m_s);
return;
}
@ -341,10 +389,21 @@ namespace AgroBase.Services
private void AtualizarHerbicidaPorAtuacao()
{
int qtdAtuacoes = Variaveis.OperacaoEmAndamento.Controle?.Bicos?.Sum(x => x.Atuacoes) ?? 0;
int qtdAtuacoes =
Variaveis
.OperacaoEmAndamento
.DispAtu?
.Dados?
.BicosPulverizadores?
.Sum(x => x?.Atuacoes ?? 0)
?? 0;
if (qtdAtuacoes > 0)
HerbicidaPorAtuacao_L = VolumeConsumidoOperacao_L / qtdAtuacoes;
HerbicidaPorAtuacao_L =
VolumeConsumidoOperacao_L /
qtdAtuacoes;
else
HerbicidaPorAtuacao_L = 0;
}
private void AtualizarAutonomia(double velocidadeAtual_m_s)
@ -399,9 +458,9 @@ namespace AgroBase.Services
velocidadeReferencia_m_s =
FuncoesMatematicas.CalculaVelocidadeMsPercentual(
Variaveis.OperacaoEmAndamento
.Parametros
.Controle
.MovVelocidadeCErvasPercent
.Parametros?
.Controle?
.MovVelocidadeCErvasPercent ?? 30
);
}
@ -476,6 +535,10 @@ namespace AgroBase.Services
_distanciaTotalAnterior_m = 0;
_tempoTotalAnterior_s = 0;
_inicioOperacaoPendente = false;
_distanciaInicioOperacaoPendente_m = 0;
_tempoInicioOperacaoPendente_s = 0;
ResetarJanelas();
}

View File

@ -1,12 +1,12 @@
using AgroBase.Models.Operadores;
using AgroBase.Models;
using AgroBase.Models.Operadores;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
using static AgroBase.Models.Operadores.OperadoresModels;
using System.Collections.Generic;
using static AgroBase.Models.Enums;
using System.Linq;
using System.Threading.Tasks;
using static AgroBase.Models.Enums;
using static AgroBase.Models.Operadores.OperadoresModels;
namespace AgroBase.Services.Operadores
{
@ -14,17 +14,87 @@ namespace AgroBase.Services.Operadores
{
public CameraWorkerModel DadosLeitura = new CameraWorkerModel();
public SaudeWorkerModel Saude { get; set; } = new SaudeWorkerModel();
private static List<CameraWorkerItemModel> CamerasConectadas = new List<CameraWorkerItemModel>();
public static List<CameraWorkerItemModel> ListaCameras => CamerasConectadas.Skip(1).ToList();
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 = true;
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)
{
Console.WriteLine($"[CameraWorker] {message.Substring(0, Math.Min(limite, message.Length))}{(message.Length > limite ? "..." : "")}");
}
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()
@ -35,7 +105,9 @@ namespace AgroBase.Services.Operadores
DadosLeitura.Iniciado = true;
bool pronto() => DadosLeitura.Pronto;
await FuncoesGlobais.AguardarCondicaoAsync(pronto, 5000);
if (pronto())
{
MostrarLog("Processamento iniciado com sucesso");
@ -51,28 +123,39 @@ namespace AgroBase.Services.Operadores
{
try
{
var comando = JsonConvert.DeserializeObject<ComandoRedis>(mensagem);
if (string.IsNullOrWhiteSpace(mensagem))
return;
var comando =
JsonConvert.DeserializeObject<ComandoRedis>(
mensagem,
_jsonSettings
);
if (comando == null)
return;
var cmd = (CameraWorkerCommandType)comando.cmd;
//MostrarLog($"Comando recebido: cmd = {cmd.ToString()}");
switch (cmd)
{
case CameraWorkerCommandType.ScriptCarregado:
DadosLeitura.Pronto = true;
DadosLeitura.ProntoEm = DateTime.Now;
MostrarLog($"Comando recebido: cmd = {cmd.ToString()}");
MostrarLog("Comando recebido: cmd = " + cmd);
break;
default:
MostrarLog($"Comando não reconhecido: {comando.cmd}");
MostrarLog("Comando não reconhecido: " + comando.cmd);
break;
}
}
catch (Exception ex)
{
MostrarLog($"Erro ao deserializar resposta: {ex.Message}");
MostrarLog(
"Erro ao deserializar resposta: " +
ex.Message
);
}
finally
{
@ -82,51 +165,295 @@ namespace AgroBase.Services.Operadores
public void AtualizarSaude()
{
Saude = OperadoresModels.AtualizarSaude(CtxKey.DadosCameraWorker);
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()
{
CamerasConectadas = new List<CameraWorkerItemModel>()
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)
{
new CameraWorkerItemModel()
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)
{
Console.WriteLine(
"[CameraWorkerService] 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)
{
Console.WriteLine(
"[CameraWorkerService] Falha ao ler câmera " +
redisIdOuMxId + ": " + ex.Message
);
Console.WriteLine(
"[CameraWorkerService] 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)
{
Console.WriteLine(
"[CameraWorkerService] 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"
}
};
var cameras_str = RedisService.Get(CtxKey.DadosCameras);
var cameras = JsonConvert.DeserializeObject<Dictionary<string, CameraWorkerItemModel>>(cameras_str);
if (cameras.Any())
{
foreach (var cam in cameras)
{
var camera_str = RedisService.Get(RedisService.CamKey(cam.Key)) ?? "";
var camera = JsonConvert.DeserializeObject<CameraWorkerItemModel>(camera_str);
if (camera?.mx_id == null) continue;
CamerasConectadas.Add(camera);
}
}
return CamerasConectadas;
}
public static OAKCameraModel AtualizarDadosCamera(string mx_id)
private static void OrdenarCamerasMantendoPlaceholder(
List<CameraWorkerItemModel> cameras)
{
var cam = CamerasConectadas.FirstOrDefault(x => x.mx_id == mx_id);
if (cam == null) return null;
var camera_str = RedisService.Get(RedisService.CamKey(cam.mx_id)) ?? "";
var camera = JsonConvert.DeserializeObject<CameraWorkerItemModel>(camera_str);
if (camera?.mx_id == null) return null;
var imu_str = RedisService.Get(RedisService.CamImuKey(cam.mx_id)) ?? "";
var imu = JsonConvert.DeserializeObject<OAKImuModel>(imu_str);
camera.imu = imu?.dados;
cam = camera;
return camera.ToOAK();
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;
}
}
}

View File

@ -216,8 +216,8 @@ namespace AgroBase.Services.Operadores
("qtd_bicos", VariaveisEquipamento.QuantidadeBicosPulverizadores),
("percentual_reservatorio_min", VariaveisEquipamento.PercentualReservatorioMin),
("percentual_tensao_bateria_min", VariaveisEquipamento.PercentualTensaoBateriaMin),
("camera_caminho_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id ?? ""),
("camera_solo_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CamerasSolo?.FirstOrDefault()?.Id ?? ""),
("camera_caminho_id", VariaveisEquipamento.Parametros.camera_caminhho_id ?? ""),
("camera_solo_id", VariaveisEquipamento.Parametros.camera_ervas_id?.FirstOrDefault() ?? ""),
("path_ia_model_ruas_seg", VersionamentoService.ArquivoModeloSegStreetDetector.CaminhoCompleto),
("path_ia_labelmap_ruas_seg", VersionamentoService.ArquivoLabelmapSegStreetDetector.CaminhoCompleto),
("path_ia_norm_stats_ruas_seg", VersionamentoService.ArquivoModeloNormstatsStreetDetector.CaminhoCompleto),
@ -320,9 +320,9 @@ namespace AgroBase.Services.Operadores
),
("Snr", new
{
percentual_altura_solo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.PercentualAlturaSolo ?? 0.1,
distancia_maxima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.DistanciaMaximaVisao ?? 5000,
distancia_minima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.DistanciaMinimaVisao ?? 300
percentual_altura_solo = CameraWorkerService.CameraCaminho?.PercentualAlturaSolo ?? 0.1,
distancia_maxima = CameraWorkerService.CameraCaminho?.DistanciaMaximaVisao ?? 5000,
distancia_minima = CameraWorkerService.CameraCaminho?.DistanciaMinimaVisao ?? 300
}
)
);

File diff suppressed because it is too large Load Diff

View File

@ -121,7 +121,7 @@ class CameraManager:
dispositivos_serializados = list(set(dispositivos_serializados))
#self.mostrar_log("Dispositivos Serializados", dispositivos_serializados)
#print("Dispositivos Serializados", dispositivos_serializados)
cameras_mapeadas = ContextoGlobalRedis.get_cameras() or {}
@ -138,6 +138,7 @@ class CameraManager:
}
else:
#cam_existente["timestamp"] = agora
#cam_existente["mx_id"] = mx_id
cam_existente["presente_scan"] = True
cam_existente["status_scan"] = "detectada"
@ -191,15 +192,23 @@ class CameraManager:
ContextoGlobalRedis.set(CtxKey.DadosCameras, cameras_mapeadas)
#self.mostrar_log("Cameras Mapeadas", cameras_mapeadas)
#print("Cameras Mapeadas", cameras_mapeadas)
equipamento = ContextoGlobalRedis.get_equipamento() or {}
visual_worker_camera_id = equipamento.get("camera_caminho_id")
weed_worker_camera_id = equipamento.get("camera_solo_id")
#self.mostrar_log(
# "[CameraManager] IDs configurados | "
# f"visual_worker_camera_id={visual_worker_camera_id!r} | "
# f"weed_worker_camera_id={weed_worker_camera_id!r} | "
# f"detectadas={list(cameras_mapeadas.keys())}"
#)
# 3) Só tenta iniciar se a câmera estiver realmente válida
if self._camera_pode_iniciar(visual_worker_camera_id, cameras_mapeadas, agora):
#self.mostrar_log(f"[CameraManager] Enviando iniciar VisualWorker para câmera {visual_worker_camera_id}")
ContextoGlobalRedis.publicar_comando(
CmdKey.VisualWorkerRx,
{
@ -209,6 +218,7 @@ class CameraManager:
)
if self._camera_pode_iniciar(weed_worker_camera_id, cameras_mapeadas, agora):
#self.mostrar_log(f"[CameraManager] Enviando iniciar WeedWorker para câmera {weed_worker_camera_id}")
ContextoGlobalRedis.publicar_comando(
CmdKey.WeedWorkerRx,
{