ajuste no canservice da firmware

This commit is contained in:
Diego Freitas 2025-08-22 15:36:19 -03:00
parent cad0bfaabd
commit bde1ec5064
77 changed files with 1780 additions and 890 deletions

Binary file not shown.

View File

@ -768,16 +768,18 @@ namespace AgroBase.Forms.IHM
idx = PreencheDadosLinha(grid, Testes, idx, CorrenteMotor); idx = PreencheDadosLinha(grid, Testes, idx, CorrenteMotor);
var ServoFreio = Modulo_Sen.Servos.FirstOrDefault(x => x.ID.Contains(Modulo.Modulo_ID) && x.Inicializado); var ServoFreio = Modulo_Sen.Servos.FirstOrDefault(x => x.ID.Contains(Modulo.Modulo_ID) && x.Inicializado);
(bool atuar, int angAtivar) = Modulo.MovMotor.ConfigFreio.AtualizarDadosFreio(true, 0, 15); Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov).UltimaDirecao = Direcao.EmFreio;
bool AtivarFreio = SenIniciado && ServoFreio != null ? await EnviarComandoMovFreio(Variaveis.OperacaoEmAndamento.DispSen, ServoFreio, angAtivar, Testes[idx], 2000) : false; Variaveis.OperacaoEmAndamento.DispMvd.Dados.AtualizarDadosFreio();
bool AtivarFreio = SenIniciado && ServoFreio != null ? await EnviarComandoMovFreio(Variaveis.OperacaoEmAndamento.DispSen, ServoFreio, Modulo.MovMotor.ConfigFreio.AnguloControle, Testes[idx], 2000) : false;
idx = PreencheDadosLinha(grid, Testes, idx, ServoFreio != null ? Testes[idx].ValorAferido : -1); idx = PreencheDadosLinha(grid, Testes, idx, ServoFreio != null ? Testes[idx].ValorAferido : -1);
var SensorAB7V2 = Modulo_Sen.Sensores.FirstOrDefault(x => x.ID == "AB7V2"); var SensorAB7V2 = Modulo_Sen.Sensores.FirstOrDefault(x => x.ID == "AB7V2");
double CorrenteFreio = SensorAB7V2 != null ? SensorAB7V2.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Corrente).atual.valor : -1; double CorrenteFreio = SensorAB7V2 != null ? SensorAB7V2.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Corrente).atual.valor : -1;
double TensaoFreio = SensorAB7V2 != null ? SensorAB7V2.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Tensao).atual.valor : -1; double TensaoFreio = SensorAB7V2 != null ? SensorAB7V2.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Tensao).atual.valor : -1;
(bool desatuar, int angDesativar) = Modulo.MovMotor.ConfigFreio.AtualizarDadosFreio(false); Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov).UltimaDirecao = Direcao.Parado;
bool DesativarFreio = SenIniciado && ServoFreio != null ? await EnviarComandoMovFreio(Variaveis.OperacaoEmAndamento.DispSen, ServoFreio, angDesativar, Testes[idx], 2000) : false; Variaveis.OperacaoEmAndamento.DispMvd.Dados.AtualizarDadosFreio();
bool DesativarFreio = SenIniciado && ServoFreio != null ? await EnviarComandoMovFreio(Variaveis.OperacaoEmAndamento.DispSen, ServoFreio, Modulo.MovMotor.ConfigFreio.AnguloControle, Testes[idx], 2000) : false;
idx = PreencheDadosLinha(grid, Testes, idx, ServoFreio != null ? Testes[idx].ValorAferido : -1); idx = PreencheDadosLinha(grid, Testes, idx, ServoFreio != null ? Testes[idx].ValorAferido : -1);
idx = PreencheDadosLinha(grid, Testes, idx, TensaoFreio); idx = PreencheDadosLinha(grid, Testes, idx, TensaoFreio);

View File

@ -168,7 +168,7 @@ namespace AgroBase.Forms.IHM
txtConcluidoEm.Text = _Sensoriamento.DataFim == DateTime.MinValue ? "-" : _Sensoriamento.DataFim.ToString("dd/MM HH:mm:ss"); txtConcluidoEm.Text = _Sensoriamento.DataFim == DateTime.MinValue ? "-" : _Sensoriamento.DataFim.ToString("dd/MM HH:mm:ss");
txtDistanciaPercorrida.Text = _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m"; txtDistanciaPercorrida.Text = _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m";
txtTempoOperacao.Text = _Sensoriamento.TempoDecorrido.ToString("HH:mm:ss"); txtTempoOperacao.Text = _Sensoriamento.TempoDecorrido.ToString("HH:mm:ss");
txtErvasIdentificadas.Text = _Sensoriamento.Atuador.QtdErvasIdentificadas.ToString(); //txtErvasIdentificadas.Text = _Sensoriamento.Atuador.QtdErvasIdentificadas.ToString();
txtHerbicidaAplicado.Text = _Sensoriamento.Atuador.HerbicidaConsumido.ToString("0.000") + " L"; txtHerbicidaAplicado.Text = _Sensoriamento.Atuador.HerbicidaConsumido.ToString("0.000") + " L";
txtBateriaConsumida.Text = _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + " %"; txtBateriaConsumida.Text = _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + " %";

View File

@ -135,6 +135,8 @@ namespace AgroBase.Forms.Operacoes
private async Task tmrLeitura_Tick() private async Task tmrLeitura_Tick()
{
try
{ {
this.Text = Variaveis.OperacaoEmAndamento.TextoFormOperacao("Manual"); this.Text = Variaveis.OperacaoEmAndamento.TextoFormOperacao("Manual");
@ -176,6 +178,12 @@ namespace AgroBase.Forms.Operacoes
if (!ReqFrameSnr) if (!ReqFrameSnr)
_ = AtualizarCameraCaminho(); _ = AtualizarCameraCaminho();
} }
catch
{
}
}
private async Task AtualizarCameraSolo() private async Task AtualizarCameraSolo()
{ {
@ -432,6 +440,8 @@ namespace AgroBase.Forms.Operacoes
#region Informacoes gerais #region Informacoes gerais
private void PopularInformacoesGerais() private void PopularInformacoesGerais()
{
try
{ {
gridDados.Rows.Clear(); gridDados.Rows.Clear();
gridDados.Columns.Clear(); gridDados.Columns.Clear();
@ -480,6 +490,9 @@ namespace AgroBase.Forms.Operacoes
AtualizarInformacoesGerais(); AtualizarInformacoesGerais();
} }
catch { }
}
private void AtualizarInformacoesGerais() private void AtualizarInformacoesGerais()
{ {

View File

@ -88,7 +88,7 @@ namespace AgroBase.Forms.Operacoes
lblDistanciaPercorrida.Text = "Distancia Percorrida: " + _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m"; lblDistanciaPercorrida.Text = "Distancia Percorrida: " + _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m";
lblTempoOperacao.Text = "Tempo de Operação: " + _Sensoriamento.TempoDecorrido.ToString("HH:mm:ss"); lblTempoOperacao.Text = "Tempo de Operação: " + _Sensoriamento.TempoDecorrido.ToString("HH:mm:ss");
lblVelocidadeMedia.Text = "Velocidade Média: " + _Sensoriamento.Movimentacao.VelocidadeMedia.ToString("0.00") + " km/h"; lblVelocidadeMedia.Text = "Velocidade Média: " + _Sensoriamento.Movimentacao.VelocidadeMedia.ToString("0.00") + " km/h";
lblErvasIdentificadas.Text = "Ervas Identificadas: " + _Sensoriamento.Atuador.QtdErvasIdentificadas.ToString("000"); //lblErvasIdentificadas.Text = "Ervas Identificadas: " + _Sensoriamento.Atuador.QtdErvasIdentificadas.ToString("000");
lblHerbicidaAplicado.Text = "Herbicida Aplicado: " + _Sensoriamento.Atuador.HerbicidaConsumido.ToString("0.000") + " L"; lblHerbicidaAplicado.Text = "Herbicida Aplicado: " + _Sensoriamento.Atuador.HerbicidaConsumido.ToString("0.000") + " L";
lblHerbicidaPorErva.Text = "Herbicida por Erva: " + _Sensoriamento.Atuador.HerbicidaPorErva.ToString("0.000") + " mL"; lblHerbicidaPorErva.Text = "Herbicida por Erva: " + _Sensoriamento.Atuador.HerbicidaPorErva.ToString("0.000") + " mL";
lblBateriaConsumida.Text = "Bateria Consumida: " + _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + "%"; lblBateriaConsumida.Text = "Bateria Consumida: " + _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + "%";

View File

@ -741,7 +741,7 @@ namespace AgroBase.Forms.Operacoes
}); });
// Obtemos todos os tipos de ervas identificadas ao longo dos logs // Obtemos todos os tipos de ervas identificadas ao longo dos logs
var todasErvas = LogsOperacao /*var todasErvas = LogsOperacao
.SelectMany(log => log.Atuador.ErvasIdentificadas) .SelectMany(log => log.Atuador.ErvasIdentificadas)
.SelectMany(bico => bico.Keys) .SelectMany(bico => bico.Keys)
.Distinct() .Distinct()
@ -795,7 +795,7 @@ namespace AgroBase.Forms.Operacoes
) )
{ {
Titulo = "Ervas Identificadas" Titulo = "Ervas Identificadas"
}; };*/
List<GraficoInterativoSerieModel> seriesCamerasSolo = new List<GraficoInterativoSerieModel>(); List<GraficoInterativoSerieModel> seriesCamerasSolo = new List<GraficoInterativoSerieModel>();
foreach (var camSolo in LogsCam_Solo) foreach (var camSolo in LogsCam_Solo)
@ -1013,7 +1013,7 @@ namespace AgroBase.Forms.Operacoes
double scaleY = (double)bitmapComObjetos.Height / frame.height; double scaleY = (double)bitmapComObjetos.Height / frame.height;
// Iterar sobre os objetos detectados // Iterar sobre os objetos detectados
foreach (var objeto in frame.deteccoes) /*foreach (var objeto in frame.deteccoes)
{ {
// Ajustar coordenadas e tamanho de acordo com a escala // Ajustar coordenadas e tamanho de acordo com a escala
int xAtual = (int)(objeto.bbox[0] * scaleX); int xAtual = (int)(objeto.bbox[0] * scaleX);
@ -1027,7 +1027,7 @@ namespace AgroBase.Forms.Operacoes
// Desenhar a descrição do objeto (id: descricao) // Desenhar a descrição do objeto (id: descricao)
string descricao = $"{objeto.descricao} | {objeto.confianca.ToString("0.00")} | ({objeto.id})"; string descricao = $"{objeto.descricao} | {objeto.confianca.ToString("0.00")} | ({objeto.id})";
g.DrawString(descricao, new Font("Arial", 20), Brushes.White, new PointF(xAtual, yAtual - 15)); g.DrawString(descricao, new Font("Arial", 20), Brushes.White, new PointF(xAtual, yAtual - 15));
} }*/
} }
// Retornar o bitmap modificado // Retornar o bitmap modificado
@ -1069,7 +1069,7 @@ namespace AgroBase.Forms.Operacoes
double herbicidaConsumido = Log.Atuador.HerbicidaConsumido != 0 ? Log.Atuador.HerbicidaConsumido : LogAtu.VolumeVazaoML; double herbicidaConsumido = Log.Atuador.HerbicidaConsumido != 0 ? Log.Atuador.HerbicidaConsumido : LogAtu.VolumeVazaoML;
double percentualHerbicidaConsumido = FuncoesMatematicas.Clamp(((herbicidaConsumido / 1000.0) / LogsAtuador.FirstOrDefault().VolumeReservatorio) * 100.0, 0, 100); double percentualHerbicidaConsumido = FuncoesMatematicas.Clamp(((herbicidaConsumido / 1000.0) / LogsAtuador.FirstOrDefault().VolumeReservatorio) * 100.0, 0, 100);
int totalAtuacoes = Log.Atuador.ErvasIdentificadas.Sum(dict => dict.Values.Sum()); int totalAtuacoes = 0; // Log.Atuador.ErvasIdentificadas.Sum(dict => dict.Values.Sum());
double herbicidaPorErva = Log.Atuador.HerbicidaPorErva != 0 ? Log.Atuador.HerbicidaPorErva : (herbicidaConsumido / (totalAtuacoes > 0 ? totalAtuacoes : 1)); double herbicidaPorErva = Log.Atuador.HerbicidaPorErva != 0 ? Log.Atuador.HerbicidaPorErva : (herbicidaConsumido / (totalAtuacoes > 0 ? totalAtuacoes : 1));
lblPercentualBateria.Text = $"Bateria {Log.Bateria.PorcentagemBateria.ToString("0.00")}%"; lblPercentualBateria.Text = $"Bateria {Log.Bateria.PorcentagemBateria.ToString("0.00")}%";
@ -1444,9 +1444,9 @@ namespace AgroBase.Forms.Operacoes
private void AtualizarInformacoesSonar() private void AtualizarInformacoesSonar()
{ {
var Log = LogsOperacao[idxMomentoAtual].OperadorVisual.Analises; var Log = LogsOperacao[idxMomentoAtual].OperadorVisual.Analises;
txtSonarDistanciaObstaculo.Text = Log.matriz_confianca.block.d_obs_true_min_m.ToString("0.00"); txtSonarDistanciaObstaculo.Text = (Log.matriz_confianca?.block?.d_obs_true_min_m ?? 0).ToString("0.00");
txtSonarDistanciaParada.Text = Log.matriz_confianca.block.decision.dist_necessaria.ToString("0.00"); txtSonarDistanciaParada.Text = (Log.matriz_confianca?.block?.decision?.dist_necessaria ?? 0).ToString("0.00");
txtSonarDecisao.Text = Log.matriz_confianca.block.reason_detail; txtSonarDecisao.Text = (Log.matriz_confianca?.block?.reason_detail ?? "");
if (gridObstaculos.Columns.Count == 0) if (gridObstaculos.Columns.Count == 0)
{ {
@ -1458,7 +1458,7 @@ namespace AgroBase.Forms.Operacoes
gridObstaculos.Rows.Clear(); gridObstaculos.Rows.Clear();
if (Log != null) if (Log?.deteccao != null)
{ {
foreach (var obstaculo in Log.deteccao) foreach (var obstaculo in Log.deteccao)
{ {

View File

@ -56,7 +56,7 @@ namespace AgroBase.Models.Modules
FuncoesPinout.SCL, FuncoesPinout.SCL,
}; };
public int QuantidadeBicos { get; set; } = 4; public int QuantidadeBicos { get; set; } = 4;
public double DensidadeHerbicida { get; set; } = 1.1; public double DensidadeHerbicida { get; set; } = 1.0;
public List<AtuadorBicoModel> BicosPulverizadores { get; set; } = new List<AtuadorBicoModel>(); public List<AtuadorBicoModel> BicosPulverizadores { get; set; } = new List<AtuadorBicoModel>();
public List<AtuadorBombaModel> BombasPressurizadoras { get; set; } = new List<AtuadorBombaModel>(); public List<AtuadorBombaModel> BombasPressurizadoras { get; set; } = new List<AtuadorBombaModel>();
public List<SensorModel> Sensores { get; set; } = new List<SensorModel>(); public List<SensorModel> Sensores { get; set; } = new List<SensorModel>();
@ -1330,7 +1330,7 @@ namespace AgroBase.Models.Modules
bool conectado = data[3] == 1; bool conectado = data[3] == 1;
int versao = data[4]; int versao = data[4];
Console.WriteLine($"[ATU] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}"); //Console.WriteLine($"[ATU] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}");
Dados.DadosLeitura.Conectado = conectado; Dados.DadosLeitura.Conectado = conectado;
Dados.DadosLeitura.Dispositivo = tipoModulo; Dados.DadosLeitura.Dispositivo = tipoModulo;
@ -1348,7 +1348,7 @@ namespace AgroBase.Models.Modules
{ {
double latenciaLoop = ConverterByteParaDouble(data, 2) * 100; double latenciaLoop = ConverterByteParaDouble(data, 2) * 100;
Dados.DadosLeitura.LatenciaLoop = Convert.ToInt32(latenciaLoop); Dados.DadosLeitura.LatenciaLoop = Convert.ToInt32(latenciaLoop);
Console.WriteLine($"[ATU] Ciclo concluido. Latencia de loop: {latenciaLoop} ms"); //Console.WriteLine($"[ATU] Ciclo concluido. Latencia de loop: {latenciaLoop} ms");
break; break;
} }
} }

View File

@ -261,7 +261,7 @@ namespace AgroBase.Models.Modules
public List<MvdMotorMOVSensorimanetoGrafico> LogGrafico { get; set; } = new List<MvdMotorMOVSensorimanetoGrafico>(); public List<MvdMotorMOVSensorimanetoGrafico> LogGrafico { get; set; } = new List<MvdMotorMOVSensorimanetoGrafico>();
public List<AlarmeModel> Alarmes { get; set; } = new List<AlarmeModel>(); public List<AlarmeModel> Alarmes { get; set; } = new List<AlarmeModel>();
public MvdServoFreioModel ConfigFreio { get; set; } = new MvdServoFreioModel(); public MvdServoFreioModel ConfigFreio { get; set; }
public static MovimentacaoModel CarregarParametrosIniciais(string Mod_ID, byte _EnderecoTx, byte _EnderecoRx) public static MovimentacaoModel CarregarParametrosIniciais(string Mod_ID, byte _EnderecoTx, byte _EnderecoRx)
@ -279,9 +279,9 @@ namespace AgroBase.Models.Modules
FuncoesPinout.PWM, FuncoesPinout.PWM,
FuncoesPinout.BRK, FuncoesPinout.BRK,
}, },
ConfigFreio = new MvdServoFreioModel() ConfigFreio = new MvdServoFreioModel(Mod_ID)
{ {
AnguloInicial = 10, AnguloInicial = Mod_ID.Contains("E") ? 10 : 170,
Incremental = Mod_ID.Contains("E"), Incremental = Mod_ID.Contains("E"),
Leitura = false, Leitura = false,
}, },
@ -291,70 +291,77 @@ namespace AgroBase.Models.Modules
TipoMovimentoDirecional.RodasDianteiras, TipoMovimentoDirecional.RodasDianteiras,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario, Parado = Sentido.Parado,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
} }
}, },
{ {
TipoMovimentoDirecional.RodasTraseiras, TipoMovimentoDirecional.RodasTraseiras,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario, Parado = Sentido.Parado,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
} }
}, },
{ {
TipoMovimentoDirecional.MovimentoDiagonal, TipoMovimentoDirecional.MovimentoDiagonal,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario, Parado = Sentido.Parado,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
} }
}, },
{ {
TipoMovimentoDirecional.MovimentoArco, TipoMovimentoDirecional.MovimentoArco,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario, Parado = Sentido.Parado,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
} }
}, },
{ {
TipoMovimentoDirecional.RotacionarNoEixo, TipoMovimentoDirecional.RotacionarNoEixo,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Sentido.Horario,
Tras = Sentido.Antihorario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Sentido.Horario, Parado = Sentido.Parado,
Tras = Sentido.Antihorario
} }
}, },
{ {
TipoMovimentoDirecional.MovimentoLateral, TipoMovimentoDirecional.MovimentoLateral,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Mod_ID.Contains("T") ? Sentido.Horario : Sentido.Antihorario,
Tras = Mod_ID.Contains("T") ? Sentido.Antihorario : Sentido.Horario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Mod_ID.Contains("T") ? Sentido.Horario : Sentido.Antihorario, Parado = Sentido.Parado,
Tras = Mod_ID.Contains("T") ? Sentido.Antihorario : Sentido.Horario
} }
}, },
{ {
TipoMovimentoDirecional.Diagnostico, TipoMovimentoDirecional.Diagnostico,
new ConfiguracaoSentidoMotor() new ConfiguracaoSentidoMotor()
{ {
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
Direita = Sentido.Parado, Direita = Sentido.Parado,
Esquerda = Sentido.Parado, Esquerda = Sentido.Parado,
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario, Parado = Sentido.Parado,
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
} }
}, },
}, },
@ -367,7 +374,6 @@ namespace AgroBase.Models.Modules
var simulando = Variaveis.OperacaoEmAndamento.Simulando || Variaveis.OperacaoEmAndamento.Treinando; var simulando = Variaveis.OperacaoEmAndamento.Simulando || Variaveis.OperacaoEmAndamento.Treinando;
bool Freado = DirecaoAtual == Direcao.EmFreio; bool Freado = DirecaoAtual == Direcao.EmFreio;
(bool AtuarServo, int AnguloFreio) = ConfigFreio.AtualizarDadosFreio(Freado, 0, -1, 5);
if ((Comandar && Inicializado) || simulando) if ((Comandar && Inicializado) || simulando)
{ {
@ -377,7 +383,6 @@ namespace AgroBase.Models.Modules
{ {
DirecaoAtual = Direcao.Parado; DirecaoAtual = Direcao.Parado;
Freado = false; Freado = false;
(AtuarServo, AnguloFreio) = ConfigFreio.AtualizarDadosFreio(Freado, 0, -1, 5);
} }
RPM_SP_Controle = _Controle.RPM_Motor; RPM_SP_Controle = _Controle.RPM_Motor;
@ -437,16 +442,6 @@ namespace AgroBase.Models.Modules
LeituraRPM = RPM_SP; LeituraRPM = RPM_SP;
} }
} }
if (AtuarServo)
{
string Mod_ID = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => x.MovMotor._EnderecoCAN_Tx == _EnderecoCAN_Tx)?.Modulo_ID ?? "";
var ServoFreio = Variaveis.OperacaoEmAndamento.DispSen.Dados.Servos.FirstOrDefault(x => x.ID.Contains(Mod_ID) && x.Inicializado && x.Controlar);
if (ServoFreio != null && ServoFreio.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.ServoAnguloLeitura).atual.valor != AnguloFreio)
{
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sFRO, ServoFreio.ID, AnguloFreio);
}
}
} }
@ -458,109 +453,170 @@ namespace AgroBase.Models.Modules
public class MvdServoFreioModel public class MvdServoFreioModel
{ {
public MvdServoFreioModel(string mod_id) { Mod_ID = mod_id; }
public string Mod_ID { get; set; }
// ---- Tunáveis ----
private const int TolAng = 2; // tolerância angular (graus)
private const int TolVelIn = 2; // histerese interna de velocidade (%)
private const int TolVelOut = 4; // histerese externa de velocidade (%)
private const int FatorEscala = 10; // proporcional p/ erro de velocidade
private const int AnguloMin = 0;
private const int AnguloMax = 180;
private const int AnguloControleMaxDefault = 20;
// ---- Estado ----
public bool Incremental { get; set; } public bool Incremental { get; set; }
public int AnguloInicial { get; set; } public int AnguloInicial { get; set; }
private int AnguloControle { get; set; } = -1;
private int AnguloControleMin
{
get
{
if (Incremental)
{
return AnguloMin;
}
else
{
return AnguloMax;
}
}
}
private int AnguloControleMax { get; set; } = 20;
public bool Leitura { get; set; } public bool Leitura { get; set; }
private bool _controle { get; set; } public int AnguloControle { get; set; } = -1;
public bool Controle private int AnguloControleMax { get; set; } = AnguloControleMaxDefault;
private int VelocidadePercentual_SP { get; set; }
private bool VelOkMemo { get; set; } = true; // memorização p/ histerese
private bool UltimoControle { get; set; } = false; // último EmFreio?
public DateTime UltimoEnvio { get; set; }
public int DelayEnvio { get; set; } = 500;
public SensorServoModel Servo
{ {
get get
{ {
return _controle; return Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Servos?
.FirstOrDefault(x => x.ID.Contains(Mod_ID));
} }
} }
private int RPM_SP { get; set; }
private int MargemRPM { get; set; } = 2; // Margem de tolerância no RPM
private int AnguloMin { get; set; } = 0; // Ângulo mínimo permitido pelo servo
private int AnguloMax { get; set; } = 180; // Ângulo máximo permitido pelo servo
private int FatorEscala { get; set; } = 10; // Fator de ajuste do controle proporcional
public (bool, int) AtualizarDadosFreio(bool status, int rpm_sp = 0, int anguloInicial = -1, int anguloSP = -1) private double VelocidadeAtual =>
{ FuncoesMatematicas.CalculaVelocidadePercentualMs(Variaveis.OperacaoEmAndamento.Sensoriamento.Movimentacao?.VelocidadeMedia ?? 0);
bool mudou = _controle != status;
_controle = status;
RPM_SP = rpm_sp;
if (!_controle) private bool StatusControleEmFreio =>
(Variaveis.OperacaoEmAndamento.Controle?.TiposControle?
.FirstOrDefault(x => x.Tipo == T_Code.Mov)?.UltimaDirecao
?? Direcao.Parado) == Direcao.EmFreio;
// leitura atual do ângulo (ou null)
private int AnguloLido
{ {
AnguloControle = AnguloControleMin; // Solta o freio se não estiver no modo de controle get
return (mudou, AnguloControle); {
var v = Servo?.ValoresLeituras?
.FirstOrDefault(x => x.funcao == FuncoesPinout.ServoAnguloLeitura)?
.atual?.valor ?? 0;
return (int)v;
}
} }
if (anguloInicial == -1) // histerese de velocidade
private bool VelocidadeOkHisterese(int sp, double atual)
{ {
anguloInicial = AnguloInicial; var erro = Math.Abs(sp - atual);
} if (VelOkMemo)
if (AnguloControle == -1)
{ {
AnguloControle = AnguloControleMin; // estava OK → só sai se passar da banda externa
} if (erro > TolVelOut) VelOkMemo = false;
if (AnguloControle == AnguloControleMin)
{
AnguloControle = anguloInicial;
}
int ajuste = anguloSP;
if (anguloSP == -1)
{
double Rpm_Atual = FuncoesMatematicas.CalculaRPMVelocidade(Variaveis.OperacaoEmAndamento.DispMvd.Dados.VelocidadeMedia);
// Garante que os valores de RPM sejam válidos
Rpm_Atual = Math.Max(Rpm_Atual, 0);
RPM_SP = Math.Max(RPM_SP, 0);
// Verifica se o RPM atual está dentro da margem de tolerância
if (Math.Abs(Rpm_Atual - RPM_SP) <= MargemRPM)
{
return (false, AnguloControle); // Se dentro da margem, não ajusta o ângulo
}
// Calcula o erro e ajusta proporcionalmente
int erroRPM = RPM_SP - (int)Rpm_Atual;
ajuste = erroRPM / FatorEscala;
// Se incremental, aplica um ajuste limitado
if (Incremental)
{
ajuste = Math.Abs(ajuste); // Garante que o ajuste seja positivo
} }
else else
{ {
ajuste = -Math.Abs(ajuste); // Garante que o ajuste seja negativo // estava NOK → só entra se ficar dentro da banda interna
if (erro <= TolVelIn) VelOkMemo = true;
} }
return VelOkMemo;
} }
// Atualiza o ângulo de controle, garantindo que fique dentro dos limites // condição “atingiu SP” com tolerância angular
int limiteMax = AnguloInicial + (Incremental ? AnguloControleMax : -AnguloControleMax); private bool AtingiuSPComTol(int? lido, int alvo)
if (Incremental)
{ {
AnguloControle = (int)FuncoesMatematicas.Clamp(AnguloControle + ajuste, AnguloMin, limiteMax); if (!lido.HasValue) return false; // sem leitura → considerar não atingido
return Math.Abs(lido.Value - alvo) <= TolAng;
}
public bool EnviarComando { get; private set; } = false;
public void AtualizarDados(int velocidade_sp = 0, int anguloInicial = -1, int anguloSP = -1)
{
VelocidadePercentual_SP = velocidade_sp;
if (!(Servo?.Controlar ?? false))
{
EnviarComando = false;
return;
}
bool emFreio = StatusControleEmFreio;
bool transicao = (UltimoControle != emFreio);
if (anguloInicial == -1) anguloInicial = AnguloInicial;
if (AnguloControle == -1) AnguloControle = Incremental ? AnguloMin : AnguloMax;
// ---- Calcula alvo de ângulo ----
int alvo;
if (!emFreio)
{
// Soltar freio: vai para o mínimo (liberado) conforme o sentido do seu hardware.
alvo = Incremental ? AnguloMin : AnguloMax;
} }
else else
{ {
AnguloControle = (int)FuncoesMatematicas.Clamp(AnguloControle - ajuste, limiteMax, AnguloMax); // Em freio: definir alvo em torno do AnguloInicial +/- janela
int limiteLo, limiteHi;
if (Incremental)
{
limiteLo = AnguloMin;
limiteHi = Clamp(anguloInicial + AnguloControleMax, AnguloMin, AnguloMax);
}
else
{
limiteLo = Clamp(anguloInicial - AnguloControleMax, AnguloMin, AnguloMax);
limiteHi = AnguloMax;
} }
return (true, AnguloControle); if (anguloSP != -1)
{
// alvo explícito
alvo = Clamp(anguloSP, limiteLo, limiteHi);
} }
else
{
// proporcional ao erro de velocidade (só para DEFINIR alvo, não para “reenviar”)
int erro = VelocidadePercentual_SP - (int)VelocidadeAtual; // pode ser + ou -
int ajuste = erro / FatorEscala;
ajuste = AnguloControleMax;
ajuste = Math.Abs(ajuste);
// direção do ajuste
if (Incremental)
{
alvo = Clamp(AnguloControle + ajuste, limiteLo, limiteHi);
}
else
{
alvo = Clamp(AnguloControle - ajuste, limiteLo, limiteHi);
}
}
}
// aplica alvo
int anguloAntigo = AnguloControle;
AnguloControle = alvo;
// ---- Decidir se deve enviar ----
// Regra: envia quando (a) houve transição de modo, (b) alvo mudou significativamente,
// ou (c) não atingiu o alvo atual (com tolerância).
bool alvoMudou = Math.Abs(AnguloControle - anguloAntigo) > 0;
bool atingiu = AtingiuSPComTol(AnguloLido, AnguloControle);
// histerese de velocidade apenas para DEFINIR o alvo (já considerado acima).
// Não use VelocidadeOk para forçar reenvio — isso causa “chatters” de comando.
bool vel_ok = VelocidadeOkHisterese(VelocidadePercentual_SP, VelocidadeAtual); // atualiza memo, se quiser expor depois
EnviarComando = transicao || alvoMudou || !atingiu;
UltimoControle = emFreio;
}
private static int Clamp(int v, int lo, int hi) => (v < lo) ? lo : (v > hi ? hi : v);
} }
public class MvdMotorMOVSensorimanetoGrafico public class MvdMotorMOVSensorimanetoGrafico
@ -591,7 +647,7 @@ namespace AgroBase.Models.Modules
{ {
var ctrl = Variaveis.OperacaoEmAndamento?.Controle; var ctrl = Variaveis.OperacaoEmAndamento?.Controle;
var disp = Variaveis.OperacaoEmAndamento?.DispMvd?.Dados?.Modulos; var disp = Variaveis.OperacaoEmAndamento?.DispMvd?.Dados?.Modulos;
if (Variaveis.OperacaoEmAndamento.StatusAtual == StatusOperacao.EmAndamento && ctrl == null || disp == null || !MovimentosCompensar.Contains(ctrl.TipoMovimento)) if (Variaveis.OperacaoEmAndamento.StatusAtual != StatusOperacao.EmAndamento || ctrl == null || disp == null || !MovimentosCompensar.Contains(ctrl.TipoMovimento))
{ {
compensacoes = (disp ?? Enumerable.Empty<ModuloMvdModel>()).ToDictionary(x => x.Modulo_ID, x => 1.0); compensacoes = (disp ?? Enumerable.Empty<ModuloMvdModel>()).ToDictionary(x => x.Modulo_ID, x => 1.0);
return; return;
@ -755,13 +811,6 @@ namespace AgroBase.Models.Modules
fRR = rExtRear / rIntRear; fRR = rExtRear / rIntRear;
} }
if (!esquerda)
{
// Inverte se curva é para direita
(fFL, fFR) = (fFR, fFL);
(fRL, fRR) = (fRR, fRL);
}
// Saturação e leve suavização (ex: EMA) podem ser aplicadas aqui // Saturação e leve suavização (ex: EMA) podem ser aplicadas aqui
fFL = Math.Min(fatorMaximo, fFL); fFL = Math.Min(fatorMaximo, fFL);
fFR = Math.Min(fatorMaximo, fFR); fFR = Math.Min(fatorMaximo, fFR);

View File

@ -417,6 +417,19 @@ namespace AgroBase.Models.Modules
return tasks; return tasks;
} }
public void AtualizarDadosFreio()
{
foreach (var Mod in Modulos)
{
var Freio = Mod.MovMotor.ConfigFreio;
Freio.AtualizarDados();
if (Freio.EnviarComando && Freio.UltimoEnvio.AddMilliseconds(Freio.DelayEnvio) < DateTime.Now)
{
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sFRO, Freio.Servo.ID, Freio.AnguloControle);
Freio.UltimoEnvio = DateTime.Now;
}
}
}
} }

View File

@ -2466,7 +2466,7 @@ namespace AgroBase.Models.Modules
// FLOAT com ou sem ponto // FLOAT com ou sem ponto
float f = float.Parse(valor, CultureInfo.InvariantCulture); float f = float.Parse(valor, CultureInfo.InvariantCulture);
ushort final = (ushort)(Escalar ? f * 100 : f); short final = (short)(Escalar ? f * 100 : f);
if (QtdBytes == 1) if (QtdBytes == 1)
return new byte[] { (byte)(final & 0xFF) }; return new byte[] { (byte)(final & 0xFF) };

View File

@ -1019,6 +1019,10 @@ namespace AgroBase.Models
} }
case T_Code.Atu: case T_Code.Atu:
{ {
if (ControleAnterior.BicosAtuados.Count != Controle.BicosAtuados.Count)
{
ControleAnterior.BicosAtuados = Controle.BicosAtuados.Select(x => x.Clone()).ToList();
}
for (int i = 0; i < _Controle.BicosAtuados.Count; i++) for (int i = 0; i < _Controle.BicosAtuados.Count; i++)
{ {
if (ForcarEnvio || _Controle.BicosAtuados[i].ComandoAtuar != _ControleAnterior.BicosAtuados[i].ComandoAtuar) if (ForcarEnvio || _Controle.BicosAtuados[i].ComandoAtuar != _ControleAnterior.BicosAtuados[i].ComandoAtuar)
@ -1677,6 +1681,7 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.DadosPerformance.AtualizarDadosPerformance(); Variaveis.OperacaoEmAndamento.DadosPerformance.AtualizarDadosPerformance();
MovimentacaoMovimentoCompensadoModel.AtualizarDados(); MovimentacaoMovimentoCompensadoModel.AtualizarDados();
Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.AtualizarDadosFreio();
// Soma total de atuações para todas as ervas de todos os bicos // Soma total de atuações para todas as ervas de todos os bicos
int totalAtuacoes = 0; int totalAtuacoes = 0;
@ -1702,8 +1707,7 @@ namespace AgroBase.Models
HerbicidaConsumido = _DispAtu.Dados.VolumeVazaoML, HerbicidaConsumido = _DispAtu.Dados.VolumeVazaoML,
PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno, PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno,
HerbicidaPorErva = HerbicidaPorErva, HerbicidaPorErva = HerbicidaPorErva,
ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.deteccoes?.Count ?? 0, ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.ervas_no_radar ?? false,
ErvasIdentificadas = WeedWorkerService.DadosLeitura?.Analise?.ervas_identificadas ?? new List<Dictionary<string, int>>(),
PercentualReservatorio = FuncoesMatematicas.Clamp(_DispAtu.Dados.PercentualReservatorio, 0, 100), PercentualReservatorio = FuncoesMatematicas.Clamp(_DispAtu.Dados.PercentualReservatorio, 0, 100),
VolumeReservatorio = _DispAtu.Dados.VolumeReservatorio, VolumeReservatorio = _DispAtu.Dados.VolumeReservatorio,
PressaoLinha = _DispAtu.Dados.PressaoLinha, PressaoLinha = _DispAtu.Dados.PressaoLinha,
@ -1880,7 +1884,7 @@ namespace AgroBase.Models
public double HerbicidaConsumido { get; set; } public double HerbicidaConsumido { get; set; }
public double PercentualErvasTerreno { get; set; } public double PercentualErvasTerreno { get; set; }
public double HerbicidaPorErva { get; set; } public double HerbicidaPorErva { get; set; }
public int ErvasNoRadar { get; set; } public bool ErvasNoRadar { get; set; }
public double PercentualReservatorio { get; set; } public double PercentualReservatorio { get; set; }
public double VolumeReservatorio { get; set; } public double VolumeReservatorio { get; set; }
public double PressaoLinha { get; set; } public double PressaoLinha { get; set; }
@ -1888,26 +1892,12 @@ namespace AgroBase.Models
public double VazaoMedia { get; set; } public double VazaoMedia { get; set; }
public double LeituraPotenciaBomba { get; set; } public double LeituraPotenciaBomba { get; set; }
public double VolumeVazaoMl { get; set; } public double VolumeVazaoMl { get; set; }
public List<Dictionary<string, int>> ErvasIdentificadas { get; set; }
public int QtdErvasIdentificadas
{
get
{
int atuacoes = 0;
foreach (var bico in ErvasIdentificadas)
{
atuacoes += bico.Sum(x => x.Value);
}
return atuacoes;
}
}
public OperacaoSensoriamentoLogAtuModel Clone() public OperacaoSensoriamentoLogAtuModel Clone()
{ {
return new OperacaoSensoriamentoLogAtuModel() return new OperacaoSensoriamentoLogAtuModel()
{ {
QtdCamerasSolo = QtdCamerasSolo, QtdCamerasSolo = QtdCamerasSolo,
ErvasIdentificadas = new List<Dictionary<string, int>>(ErvasIdentificadas),
ErvasNoRadar = ErvasNoRadar, ErvasNoRadar = ErvasNoRadar,
HerbicidaConsumido = HerbicidaConsumido, HerbicidaConsumido = HerbicidaConsumido,
HerbicidaPorErva = HerbicidaPorErva, HerbicidaPorErva = HerbicidaPorErva,

View File

@ -26,8 +26,8 @@ namespace AgroBase.Models.Operadores
Pronto = Pronto, Pronto = Pronto,
ProntoEm = ProntoEm, ProntoEm = ProntoEm,
CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>(CameraFrames ?? new Dictionary<CameraFrameType, OAKCameraFrameModel>()), CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>(CameraFrames ?? new Dictionary<CameraFrameType, OAKCameraFrameModel>()),
Analises = Analises.Clone(), Analises = Analises?.Clone(),
Imu = Imu.Clone(), Imu = Imu?.Clone(),
UltimaMensagem = UltimaMensagem, UltimaMensagem = UltimaMensagem,
}; };
} }
@ -522,9 +522,9 @@ namespace AgroBase.Models.Operadores
grid_w = grid_w, grid_w = grid_w,
grid_h = grid_h, grid_h = grid_h,
y_range_m = y_range_m, y_range_m = y_range_m,
row_dist_m = new List<float>(row_dist_m), row_dist_m = new List<float>(row_dist_m ?? new List<float>()),
row_scale_x_m = new List<float>(row_scale_x_m), row_scale_x_m = new List<float>(row_scale_x_m ?? new List<float>()),
block = block.Clone() block = block?.Clone() ?? new VisualWorkerMessageMatrizConfiancaBlockModel()
}; };
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using static OpenTK.Graphics.OpenGL.GL;
namespace AgroBase.Models.Operadores namespace AgroBase.Models.Operadores
{ {
@ -41,23 +42,30 @@ namespace AgroBase.Models.Operadores
public class WeedWorkerAnaliseModel public class WeedWorkerAnaliseModel
{ {
public DateTime timestamp { get; set; } public DateTime Timestamp
public int width { get; set; } {
get
{
return FuncoesGlobais.UnixToDateTime(timestamp.ToString());
}
}
public double timestamp { get; set; }
public int height { get; set; } public int height { get; set; }
public List<WeedWorkerAnaliseDeteccaoModel> deteccoes { get; set; } public int width { get; set; }
public Dictionary<int, bool> controle { get; set; } public Dictionary<int, bool> controle { get; set; }
public List<Dictionary<string, int>> ervas_identificadas { get; set; } public bool ervas_no_radar { get; set; }
public WeedWorkerAnaliseEstatisticasModel estatisticas { get; set; }
public WeedWorkerAnaliseModel Clone() public WeedWorkerAnaliseModel Clone()
{ {
return new WeedWorkerAnaliseModel() return new WeedWorkerAnaliseModel()
{ {
timestamp = timestamp, timestamp = timestamp,
deteccoes = new List<WeedWorkerAnaliseDeteccaoModel>(deteccoes ?? new List<WeedWorkerAnaliseDeteccaoModel>()),
controle = new Dictionary<int, bool>(controle ?? new Dictionary<int, bool>()), controle = new Dictionary<int, bool>(controle ?? new Dictionary<int, bool>()),
ervas_identificadas = ervas_identificadas?.Select(dict => new Dictionary<string, int>(dict)).ToList(),
height = height, height = height,
width = width, width = width,
ervas_no_radar = ervas_no_radar,
estatisticas = estatisticas?.Clone()
}; };
} }
} }
@ -75,6 +83,76 @@ namespace AgroBase.Models.Operadores
public double confianca { get; set; } public double confianca { get; set; }
} }
public class WeedWorkerAnaliseEstatisticasModel
{
public WeedWorkerAnaliseEstatisticasErvasModel erva { get; set; }
public WeedWorkerAnaliseEstatisticasBicosModel bicos { get; set; }
public WeedWorkerAnaliseEstatisticasModel Clone()
{
return new WeedWorkerAnaliseEstatisticasModel()
{
erva = erva?.Clone(),
bicos = bicos?.Clone()
};
}
}
public class WeedWorkerAnaliseEstatisticasErvasModel
{
public double frac_global { get; set; }
public double ema_global { get; set; }
public double thr_on_global { get; set; }
public double thr_off_global { get; set; }
public double vel_adj { get; set; }
public WeedWorkerAnaliseEstatisticasErvasModel Clone()
{
return new WeedWorkerAnaliseEstatisticasErvasModel()
{
ema_global = ema_global,
frac_global = frac_global,
thr_off_global = thr_off_global,
thr_on_global = thr_on_global,
vel_adj = vel_adj,
};
}
}
public class WeedWorkerAnaliseEstatisticasBicosModel
{
public List<double> larguras { get; set; }
public List<double> contagem_cana_px_por_bico { get; set; }
public List<double> contagem_cana_frac_por_bico { get; set; }
public WeedWorkerAnaliseEstatisticasBicosFaixaModel faixa { get; set; }
public WeedWorkerAnaliseEstatisticasBicosModel Clone()
{
return new WeedWorkerAnaliseEstatisticasBicosModel()
{
larguras = new List<double>(larguras),
contagem_cana_frac_por_bico = new List<double>(contagem_cana_frac_por_bico),
contagem_cana_px_por_bico = new List<double>(contagem_cana_px_por_bico),
faixa = faixa?.Clone()
};
}
}
public class WeedWorkerAnaliseEstatisticasBicosFaixaModel
{
public int y_top { get; set; }
public int y_bot { get; set; }
public WeedWorkerAnaliseEstatisticasBicosFaixaModel Clone()
{
return new WeedWorkerAnaliseEstatisticasBicosFaixaModel()
{
y_bot = y_bot,
y_top = y_top,
};
}
}
public enum WeedWorkerCommandType public enum WeedWorkerCommandType
{ {
ScriptCarregado = 1, ScriptCarregado = 1,

View File

@ -1425,6 +1425,12 @@ namespace AgroBase.Models
return Velocidade; return Velocidade;
} }
public static double CalculaVelocidadePercentualMs(double vel_ms)
{
double Velocidade = vel_ms / CalculaVelocidadeMsPercentual(100);
return Velocidade;
}
public static double GrausParaRadianos(double angle) public static double GrausParaRadianos(double angle)
{ {
return Math.PI / 180.0 * angle; return Math.PI / 180.0 * angle;

View File

@ -168,7 +168,7 @@ namespace AgroBase.Services
{ {
get get
{ {
return SerialService.DispositivosCan.Any(d => SerialService.DispositivosMapeados.Any(x => x.Status == StatusModulo.Operante && x.Dispositivo == d)); return SerialService.DispositivosMapeados.Any(x => /*x.Status != StatusModulo.Desconectado &&*/ SerialService.DispositivosCan.Contains(x.Dispositivo));
} }
} }
private AsyncTaskTimerModel tmrEnviarCAN; private AsyncTaskTimerModel tmrEnviarCAN;

View File

@ -379,7 +379,7 @@ namespace AgroBase.Services
int.TryParse(qualidade, out int fix); int.TryParse(qualidade, out int fix);
double.TryParse(idadeCorrecaoRaw, out double idadeCorrecao); double.TryParse(idadeCorrecaoRaw.Replace(".", ","), out double idadeCorrecao);
//Console.WriteLine($"GNGGA: Hora={horaUTC}, Latitude={latitude}, Longitude={longitude}, Qualidade={qualidade}, Satélites={satelitesUsados}, HDOP={hdop}, Altitude={altitude}"); //Console.WriteLine($"GNGGA: Hora={horaUTC}, Latitude={latitude}, Longitude={longitude}, Qualidade={qualidade}, Satélites={satelitesUsados}, HDOP={hdop}, Altitude={altitude}");
@ -793,7 +793,7 @@ namespace AgroBase.Services
private static async Task AplicarCorrecaoRTK_Ntrip() private static async Task AplicarCorrecaoRTK_Ntrip()
{ {
if (LoopRTK_Ntrip || !APIService.HasInternet) if (LoopRTK_Ntrip || !APIService.HasInternet || true)
return; return;
LoopRTK_Ntrip = true; LoopRTK_Ntrip = true;
@ -853,8 +853,9 @@ namespace AgroBase.Services
} }
else else
{ {
Console.WriteLine($"Falha na conexão: {response}"); Console.WriteLine($"Falha na conexão com NTRIP: {response}");
await Task.Delay(5000); // Aguarde antes de tentar novamente await Task.Delay(5000); // Aguarde antes de tentar novamente
break;
} }
} }
} }

View File

@ -170,7 +170,7 @@ namespace AgroBase.Services
if (!EnviarComando(comando)) if (!EnviarComando(comando))
{ {
porta.Close(); _PortaLoRa.Close();
return false; return false;
} }

View File

@ -330,7 +330,8 @@ namespace AgroBase.Services
_pulverizador.AddRange(CanPrefix(DadosLoRaParse.DadosPulverizador)); _pulverizador.AddRange(CanPrefix(DadosLoRaParse.DadosPulverizador));
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PercentualReservatorio)); // 1 byte _pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PercentualReservatorio)); // 1 byte
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PressaoLinha)); // 1 byte _pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PressaoLinha)); // 1 byte
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.ErvasIdentificadas.Sum(x => x.Values.Sum()))); // 1 byte //_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.ErvasIdentificadas.Sum(x => x.Values.Sum()))); // 1 byte
_pulverizador.Add((byte)Convert.ToInt32(0)); // 1 byte
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.HerbicidaConsumido)); // 1 byte _pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.HerbicidaConsumido)); // 1 byte
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.HerbicidaPorErva)); // 1 byte _pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.HerbicidaPorErva)); // 1 byte
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PercentualErvasTerreno)); // 1 byte _pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PercentualErvasTerreno)); // 1 byte

View File

@ -140,7 +140,7 @@ namespace AgroBase.Services.Operadores
("Gerais", new ("Gerais", new
{ {
velocidade_ms = _Sensoriamento.Movimentacao != null ? _Sensoriamento.Movimentacao.VelocidadeMedia : 0, velocidade_ms = _Sensoriamento.Movimentacao != null ? _Sensoriamento.Movimentacao.VelocidadeMedia : 0,
ervas_no_radar = _Sensoriamento.Atuador != null ? _Sensoriamento.Atuador.ErvasNoRadar : 0, ervas_no_radar = _Sensoriamento.Atuador != null ? _Sensoriamento.Atuador.ErvasNoRadar : false,
} }
) )
); );

View File

@ -16,7 +16,6 @@ namespace AgroBase.Services.Operadores
public SaudeWorkerModel Saude { get; set; } = new SaudeWorkerModel(); public SaudeWorkerModel Saude { get; set; } = new SaudeWorkerModel();
private bool DebugMode = true; private bool DebugMode = true;
static long _lastTs = -1;
public void MostrarLog(string message, int limite = 500) public void MostrarLog(string message, int limite = 500)
{ {
@ -172,70 +171,23 @@ namespace AgroBase.Services.Operadores
var analise = root["analise"] as JObject; var analise = root["analise"] as JObject;
if (analise == null) return; if (analise == null) return;
// 1) pega timestamp sem recriar dicionários try
long tsUnix = analise["timestamp"]?.Value<long>() ?? 0;
if (tsUnix != 0)
{ {
var ts = FuncoesGlobais.UnixToDateTime(tsUnix.ToString()); DadosLeitura.Analise = JsonConvert.DeserializeObject<WeedWorkerAnaliseModel>(root["analise"].ToString());
DadosLeitura.Analise.timestamp = ts;
} }
catch (Exception ex)
// 2) se não mudou, pule deserializações pesadas
if (tsUnix == _lastTs) goto AtualizaBicos;
_lastTs = tsUnix;
// 3) deteccoes (se precisar manter o tipo forte)
var detToken = analise["deteccoes"];
if (detToken != null)
{ {
var lista = detToken.ToObject<List<WeedWorkerAnaliseDeteccaoModel>>(); Console.WriteLine($"Erro ao deserializar analise de ervas: {ex.Message}");
DadosLeitura.Analise.deteccoes = lista;
} }
// 4) controle: converte direto p/ int->bool sem ToString/dupla desserialização
var ctrlObj = analise["controle"] as JObject;
if (ctrlObj != null)
{
var dict = new Dictionary<int, bool>(ctrlObj.Count);
foreach (var prop in ctrlObj.Properties())
if (int.TryParse(prop.Name, out int k))
dict[k] = prop.Value.Value<bool>();
DadosLeitura.Analise.controle = dict;
}
// 5) ervas_identificadas: JArray -> List<Dictionary<string,int>>
var ervasTok = analise["ervas_identificadas"] as JArray;
if (ervasTok != null)
{
var ervas = new List<Dictionary<string, int>>(ervasTok.Count);
foreach (var item in ervasTok.OfType<JObject>())
{
var d = new Dictionary<string, int>(item.Count);
foreach (var p in item.Properties())
d[p.Name] = p.Value.Value<int>();
ervas.Add(d);
}
DadosLeitura.Analise.ervas_identificadas = ervas;
}
AtualizaBicos:
Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.BicosPulverizadores?.ForEach(bico =>
{
int atuacoes = 0;
var ervas = DadosLeitura?.Analise?.ervas_identificadas ?? new List<Dictionary<string, int>>();
if (bico.Posicao > 0 && bico.Posicao <= ervas.Count)
atuacoes = ervas[bico.Posicao - 1]?.Sum(x => x.Value) ?? 0;
bico.Atuacoes = atuacoes;
});
} }
public static void ReiniciarLeituraAnalise() public static void ReiniciarLeituraAnalise()
{ {
RedisService.AtualizarCampos( RedisService.AtualizarCampos(
CtxKey.DadosWeedWorker, CtxKey.DadosWeedWorker,
("analise.deteccoes", new List<WeedWorkerAnaliseDeteccaoModel>()), ("analise.ervas_no_radar", false),
("analise.controle", new Dictionary<string, bool>(Variaveis.OperacaoEmAndamento.DispAtu.Dados.BicosPulverizadores.ToDictionary(x => x.Posicao.ToString(), x => x.ComandoAtuar))), ("analise.controle", new Dictionary<string, bool>(Variaveis.OperacaoEmAndamento.DispAtu.Dados.BicosPulverizadores.ToDictionary(x => x.Posicao.ToString(), x => x.ComandoAtuar))),
("analise.ervas_identificadas", new List<Dictionary<string, int>>()) ("analise.estatisticas", new WeedWorkerAnaliseEstatisticasModel())
); );
} }

View File

@ -22,7 +22,7 @@ namespace AgroBase.Services
{ {
get get
{ {
return SerialService.DispositivosCan.Any(d => SerialService.DispositivosMapeados.Any(x => x.Status != StatusModulo.Desconectado && x.Dispositivo == d)); return SerialService.DispositivosMapeados.Any(x => /*x.Status != StatusModulo.Desconectado &&*/ SerialService.DispositivosCan.Contains(x.Dispositivo));
} }
} }
private AsyncTaskTimerModel tmrEnviarCAN; private AsyncTaskTimerModel tmrEnviarCAN;
@ -55,22 +55,14 @@ namespace AgroBase.Services
{ {
try try
{ {
if (Porta == null && (_PortaCAN?.IsOpen ?? false))
{
Porta = _PortaCAN;
}
if (Porta == null && _PortaCAN == null) if (Porta == null && _PortaCAN == null)
{ {
return false; return false;
} }
if (!IsConnected) Porta?.Close();
{
Porta.Close();
}
if (_PortaCAN == null || !IsConnected) if ((Porta != null && _PortaCAN == null) || !IsConnected)
{ {
lock (_lock) lock (_lock)
{ {

View File

@ -208,7 +208,6 @@ namespace AgroBase.Services
bool LoRa = await ProcurarDispositivoLoRa(_Porta); bool LoRa = await ProcurarDispositivoLoRa(_Porta);
if (LoRa) if (LoRa)
{ {
_Porta.Close();
continue; continue;
} }
} }
@ -218,7 +217,6 @@ namespace AgroBase.Services
bool GPSEncontrado = await ProcurarDispositivoGPS(_Porta); bool GPSEncontrado = await ProcurarDispositivoGPS(_Porta);
if (GPSEncontrado) if (GPSEncontrado)
{ {
_Porta.Close();
continue; continue;
} }
} }
@ -229,7 +227,6 @@ namespace AgroBase.Services
bool dispositivoCan = await ProcurarDispositivosCAN(_Porta); bool dispositivoCan = await ProcurarDispositivosCAN(_Porta);
if (dispositivoCan) if (dispositivoCan)
{ {
_Porta.Close();
AtualizarConsole("Barramento CAN encontrado"); AtualizarConsole("Barramento CAN encontrado");
continue; continue;
} }
@ -253,6 +250,10 @@ namespace AgroBase.Services
GPSService.PortaGPS?.Close(); GPSService.PortaGPS?.Close();
GPSService.PortaGPS = null; GPSService.PortaGPS = null;
break; break;
case T_Code.Lra:
LoRaBaseService._PortaLoRa?.Close();
LoRaBaseService._PortaLoRa = null;
break;
} }
} }
@ -396,6 +397,11 @@ namespace AgroBase.Services
if (!CanManager.CanService.Inicializar(Porta)) if (!CanManager.CanService.Inicializar(Porta))
{ {
AtualizarConsole($"Adaptador {CanManager.CanService._portName} não conectado!"); AtualizarConsole($"Adaptador {CanManager.CanService._portName} não conectado!");
var dispositivosRemover = DispositivosMapeados.Where(x => DispositivosCan.Contains(x.Dispositivo)).ToList();
foreach (var d in dispositivosRemover)
{
DispositivosMapeados.Remove(d);
}
return false; return false;
} }
@ -419,7 +425,7 @@ namespace AgroBase.Services
} }
} }
if (true && !MKS057DCanService.Referenciando && (!MKS057DCanService.Iniciado || MKS057DCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4))) if (true && Variaveis.OperacaoEmAndamento.DispMvd != null && !MKS057DCanService.Referenciando && (!MKS057DCanService.Iniciado || MKS057DCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4)))
{ {
AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivos MKS"); AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivos MKS");
bool DispMks = await MKS057DCanService.VerificaDispositivoConectado(); bool DispMks = await MKS057DCanService.VerificaDispositivoConectado();
@ -429,7 +435,7 @@ namespace AgroBase.Services
} }
} }
if (true && (!OIDCanService.Iniciado || OIDCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4))) if (true && Variaveis.OperacaoEmAndamento.DispMvd != null && (!OIDCanService.Iniciado || OIDCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4)))
{ {
AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo OID"); AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo OID");
bool DispOid = await OIDCanService.VerificaDispositivoConectado(); bool DispOid = await OIDCanService.VerificaDispositivoConectado();

View File

@ -1,12 +1,5 @@
{ {
"epochs": [ { "epochs": [ {
"calculation_time": "13397850728648898",
"config_version": 0,
"model_version": "0",
"padded_top_topics_start_index": 0,
"taxonomy_version": 0,
"top_topics_and_observing_domains": [ ]
}, {
"calculation_time": "13398455528662484", "calculation_time": "13398455528662484",
"config_version": 0, "config_version": 0,
"model_version": "0", "model_version": "0",
@ -29,5 +22,5 @@
"top_topics_and_observing_domains": [ ] "top_topics_and_observing_domains": [ ]
} ], } ],
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642", "hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
"next_scheduled_calculation_time": "13400432611771339" "next_scheduled_calculation_time": "13400432611772238"
} }

View File

@ -1,3 +1,3 @@
2025/08/18-17:01:38.585 14e0 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001 2025/08/22-10:57:48.747 3fc4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
2025/08/18-17:01:38.592 14e0 Recovering log #3 2025/08/22-10:57:48.753 3fc4 Recovering log #3
2025/08/18-17:01:38.596 14e0 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log 2025/08/22-10:57:48.756 3fc4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log

View File

@ -1,3 +1,3 @@
2025/08/18-17:00:07.328 1a28 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001 2025/08/22-10:47:37.338 2a88 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
2025/08/18-17:00:07.334 1a28 Recovering log #3 2025/08/22-10:47:37.345 2a88 Recovering log #3
2025/08/18-17:00:07.337 1a28 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log 2025/08/22-10:47:37.349 2a88 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log

View File

@ -1 +1 @@
{"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13400107303917497","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":10944},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:627:be00:b128:5cd2:a1f6:e65c","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}} {"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13400431527833615","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":18302},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:60c:f100:c78:e04e:44f:8fe","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}}

View File

@ -1 +1 @@
{"sts":[{"expiry":1787083300.948773,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1755547300.948778}],"version":2} {"sts":[{"expiry":1787405309.402605,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1755869309.402614}],"version":2}

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
2025/08/18-17:06:16.351 14e0 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001 2025/08/22-13:45:28.240 3fc4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
2025/08/18-17:06:16.352 14e0 Recovering log #3 2025/08/22-13:45:28.242 3fc4 Recovering log #3
2025/08/18-17:06:16.355 14e0 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log 2025/08/22-13:45:28.246 3fc4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log

View File

@ -1,3 +1,3 @@
2025/08/18-17:00:57.069 1a28 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001 2025/08/22-10:49:31.955 2a88 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
2025/08/18-17:00:57.071 1a28 Recovering log #3 2025/08/22-10:49:31.957 2a88 Recovering log #3
2025/08/18-17:00:57.075 1a28 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log 2025/08/22-10:49:31.960 2a88 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log

View File

@ -1,3 +1,3 @@
2025/08/18-17:01:38.507 481c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001 2025/08/22-10:57:48.675 c54 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
2025/08/18-17:01:38.509 481c Recovering log #7 2025/08/22-10:57:48.676 c54 Recovering log #7
2025/08/18-17:01:38.509 481c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log 2025/08/22-10:57:48.677 c54 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log

View File

@ -1,3 +1,3 @@
2025/08/18-17:00:07.253 4f4c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001 2025/08/22-10:47:37.263 3d50 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
2025/08/18-17:00:07.255 4f4c Recovering log #7 2025/08/22-10:47:37.265 3d50 Recovering log #7
2025/08/18-17:00:07.255 4f4c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log 2025/08/22-10:47:37.265 3d50 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Id":"1","Name":"16_08_2025_13_03_09_Manual","Length":0.0,"Dist1":0.0,"Dist2":0.0},"geometry":{"id":null,"type":"LineString","coordinates":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}]} {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Id":"1","Name":"22_08_2025_10_41_55_Manual","Length":29.677008111445875,"Dist1":0.0,"Dist2":0.0},"geometry":{"id":null,"type":"LineString","coordinates":[[-47.395256450666665,-22.172346369833335],[-47.3952565435,-22.172346308833333],[-47.395256576833333,-22.1723463895],[-47.395256607833332,-22.172346390833333],[-47.395256612,-22.172346383666667],[-47.3952566095,-22.172346395833333],[-47.395256603,-22.17234638],[-47.395256633333332,-22.172346400166667],[-47.39525668666667,-22.172346436166666],[-47.395256690333333,-22.172346315],[-47.395256662833333,-22.172346244833335],[-47.3952566795,-22.172346234166668],[-47.395256709166667,-22.172346255666668],[-47.395256872166669,-22.172346330833335],[-47.395256888,-22.172346301],[-47.3952568545,-22.172346285],[-47.395256829,-22.172346216166666],[-47.395256821666663,-22.172346155333333],[-47.395256853333336,-22.172346067333333],[-47.395256828333331,-22.172346081333334],[-47.395256798333335,-22.172346064166668],[-47.39525658783333,-22.1723460885],[-47.395256589166664,-22.172345992],[-47.395256726833331,-22.17234593],[-47.3952567375,-22.1723458955],[-47.395256776833335,-22.172345912166666],[-47.3952566485,-22.172345831166666],[-47.395256515833331,-22.172345763833334],[-47.395256549666669,-22.172345739333334],[-47.395256393,-22.172345694166665],[-47.395256296166664,-22.172345727166668],[-47.395256282166663,-22.172345670166667],[-47.395256309333334,-22.1723456805],[-47.39525658116667,-22.172347672333334],[-47.395256880833337,-22.172348807333332],[-47.395258087333332,-22.172352117833334],[-47.395258773666669,-22.1723537055],[-47.395258515,-22.172356928333333],[-47.395258686666665,-22.172358251333332],[-47.395259433666666,-22.1723619705],[-47.395259733333333,-22.172363576],[-47.395260085,-22.172365142166665],[-47.395261047666665,-22.172367679166666],[-47.395261088833337,-22.172368784333333],[-47.395261036333331,-22.172368743833335],[-47.395260981166665,-22.172368728166667],[-47.395260906333334,-22.172370990833333],[-47.395261183166667,-22.172372657666667],[-47.395261496166668,-22.172374629166665],[-47.395261777166667,-22.1723753845],[-47.395261561333335,-22.1723772325],[-47.3952616635,-22.172379212666666],[-47.395259892666665,-22.1723810495],[-47.395259789333331,-22.172383026333332],[-47.395258998833334,-22.172386347833335],[-47.3952587635,-22.172388393333332],[-47.3952582685,-22.1723898615],[-47.395258494833335,-22.172393639666666],[-47.395250912166667,-22.172404825833333],[-47.395250136666668,-22.172405724833332],[-47.395250509,-22.172405612],[-47.3952558565,-22.172405050666665],[-47.39525849333333,-22.172409069],[-47.395260315166666,-22.172416536166665],[-47.3952563085,-22.172417938],[-47.395253391,-22.172420802166666],[-47.395256822166665,-22.172420572833332],[-47.395256906833332,-22.172424407666668],[-47.3952563335,-22.172425210166665],[-47.395257268833333,-22.172427588],[-47.395257951666665,-22.172428887166667],[-47.395257650833337,-22.172429874833334],[-47.395260252833332,-22.172433116666667],[-47.395263901833331,-22.172437103],[-47.3952669375,-22.172439262833333],[-47.395269575833332,-22.172440730833333],[-47.395276767333335,-22.172442326666665],[-47.395280671833333,-22.1724421815],[-47.395281228166667,-22.172442044666667],[-47.3952777085,-22.172442249166668],[-47.395270072833334,-22.172444610833335],[-47.395266372,-22.172447167833333],[-47.395261905,-22.172453009333335],[-47.395260418,-22.172456977166668],[-47.395258931166666,-22.172460745166667],[-47.395256378333336,-22.172467218166666],[-47.395255179333333,-22.1724699915],[-47.395252460666669,-22.1724764045],[-47.395250798333336,-22.172480014833333],[-47.3952483145,-22.1724866155],[-47.395247748666669,-22.172488646166666],[-47.395247018166664,-22.172494215333334],[-47.395246894333333,-22.172498196],[-47.39524629466667,-22.172500856166668],[-47.395242381833334,-22.172506576833332],[-47.39523919483333,-22.172508991666668],[-47.395232237333332,-22.172512476666668],[-47.3952284705,-22.172514312666667],[-47.395221581833333,-22.172517214333332],[-47.395217751333334,-22.172518779333334],[-47.395214971,-22.172520573666667],[-47.395210067333331,-22.1725241545],[-47.3952079915,-22.172527879833332],[-47.395204200333332,-22.1725354175],[-47.395202777666668,-22.1725386405],[-47.395202796833331,-22.17254124],[-47.39520273116667,-22.172542654166666],[-47.3952026795,-22.172543427166666],[-47.3952027095,-22.17254343],[-47.395202701833334,-22.172543459333333],[-47.395202711,-22.172543445166667],[-47.395202679833332,-22.172543447166667],[-47.395202698333335,-22.172543444],[-47.395202698333335,-22.172543444]]}}]}

View File

@ -17,7 +17,7 @@
<meta name="viewport" content="width=device-width, <meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style> <style>
#map_67e0a7b51ff20c385c068ee0a8d7495e { #map_7d7f70bc75999486401c90b97c0360ad {
position: relative; position: relative;
width: 100.0%; width: 100.0%;
height: 100.0%; height: 100.0%;
@ -54,14 +54,14 @@
<body> <body>
<div class="folium-map" id="map_67e0a7b51ff20c385c068ee0a8d7495e" ></div> <div class="folium-map" id="map_7d7f70bc75999486401c90b97c0360ad" ></div>
</body> </body>
<script> <script>
var map_67e0a7b51ff20c385c068ee0a8d7495e = L.map( var map_7d7f70bc75999486401c90b97c0360ad = L.map(
"map_67e0a7b51ff20c385c068ee0a8d7495e", "map_7d7f70bc75999486401c90b97c0360ad",
{ {
center: [0.0, 0.0], center: [0.0, 0.0],
crs: L.CRS.EPSG3857, crs: L.CRS.EPSG3857,
@ -77,26 +77,6 @@
var tile_layer_bcfeae14139b4f2f33677d1b47c8ee4f = L.tileLayer(
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
{
"minZoom": 0,
"maxZoom": 19,
"maxNativeZoom": 19,
"noWrap": false,
"attribution": "\u0026copy; \u003ca href=\"https://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors",
"subdomains": "abc",
"detectRetina": false,
"tms": false,
"opacity": 1,
}
);
tile_layer_bcfeae14139b4f2f33677d1b47c8ee4f.addTo(map_67e0a7b51ff20c385c068ee0a8d7495e);
</script> </script>
<script> <script>
@ -116,7 +96,7 @@
} }
trajeto_json_add({"features": []}); trajeto_json_add({"features": []});
trajeto_json.addTo(map_67e0a7b51ff20c385c068ee0a8d7495e); trajeto_json.addTo(map_7d7f70bc75999486401c90b97c0360ad);
function adicionarGeometria(novaGeometria) { function adicionarGeometria(novaGeometria) {
trajeto_json.addData(novaGeometria); trajeto_json.addData(novaGeometria);
@ -179,9 +159,9 @@
var marcadorEquipamento = L.marker([0, 0], { var marcadorEquipamento = L.marker([0, 0], {
icon: customIcon icon: customIcon
}).addTo(map_67e0a7b51ff20c385c068ee0a8d7495e); }).addTo(map_7d7f70bc75999486401c90b97c0360ad);
var marcadorBase = L.marker([0, 0], {}).addTo(map_67e0a7b51ff20c385c068ee0a8d7495e); var marcadorBase = L.marker([0, 0], {}).addTo(map_7d7f70bc75999486401c90b97c0360ad);
var icon = L.AwesomeMarkers.icon( var icon = L.AwesomeMarkers.icon(
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"} {"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
); );
@ -246,7 +226,7 @@
} }
if (foco) { if (foco) {
map_67e0a7b51ff20c385c068ee0a8d7495e.setView(novaPosicao, map_67e0a7b51ff20c385c068ee0a8d7495e.getZoom()); map_7d7f70bc75999486401c90b97c0360ad.setView(novaPosicao, map_7d7f70bc75999486401c90b97c0360ad.getZoom());
} }
} }
@ -268,7 +248,7 @@
marcadorDinamico.setRotationAngle(angulo); marcadorDinamico.setRotationAngle(angulo);
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]); adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
map_67e0a7b51ff20c385c068ee0a8d7495e.setView(novaPosicao, map_67e0a7b51ff20c385c068ee0a8d7495e.getZoom());*/ map_7d7f70bc75999486401c90b97c0360ad.setView(novaPosicao, map_7d7f70bc75999486401c90b97c0360ad.getZoom());*/
}); });
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) { function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {

View File

@ -17,7 +17,7 @@
<meta name="viewport" content="width=device-width, <meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style> <style>
#map_fa80b7c1ff2d8431553ba20c387f3aad { #map_353f6ba1c95182a48ae91b9a891f095d {
position: relative; position: relative;
width: 100.0%; width: 100.0%;
height: 100.0%; height: 100.0%;
@ -54,16 +54,16 @@
<body> <body>
<div class="folium-map" id="map_fa80b7c1ff2d8431553ba20c387f3aad" ></div> <div class="folium-map" id="map_353f6ba1c95182a48ae91b9a891f095d" ></div>
</body> </body>
<script> <script>
var map_fa80b7c1ff2d8431553ba20c387f3aad = L.map( var map_353f6ba1c95182a48ae91b9a891f095d = L.map(
"map_fa80b7c1ff2d8431553ba20c387f3aad", "map_353f6ba1c95182a48ae91b9a891f095d",
{ {
center: [0.0, 0.0], center: [-22.172444564750002, -47.395241953833334],
crs: L.CRS.EPSG3857, crs: L.CRS.EPSG3857,
...{ ...{
"zoom": 12, "zoom": 12,
@ -78,7 +78,7 @@
var tile_layer_b446020bdc934e39b8d676cd95436167 = L.tileLayer( var tile_layer_8564e33b7369e4710f4819bd3c55e0bf = L.tileLayer(
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
{ {
"minZoom": 0, "minZoom": 0,
@ -95,7 +95,7 @@
); );
tile_layer_b446020bdc934e39b8d676cd95436167.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad); tile_layer_8564e33b7369e4710f4819bd3c55e0bf.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
@ -111,7 +111,7 @@
}*/ }*/
}); });
} }
function geo_json_09ba5926a79ad74ce059c256c57c60e9_onEachFeature(feature, layer) { function geo_json_b417ccdd5de3860de9f9bb6e9ce890b6_onEachFeature(feature, layer) {
layer.on({ layer.on({
@ -148,23 +148,23 @@
}*/ }*/
}); });
}; };
var geo_json_09ba5926a79ad74ce059c256c57c60e9 = L.geoJson(null, { var geo_json_b417ccdd5de3860de9f9bb6e9ce890b6 = L.geoJson(null, {
onEachFeature: geo_json_09ba5926a79ad74ce059c256c57c60e9_onEachFeature, onEachFeature: geo_json_b417ccdd5de3860de9f9bb6e9ce890b6_onEachFeature,
...{ ...{
} }
}); });
function geo_json_09ba5926a79ad74ce059c256c57c60e9_add (data) { function geo_json_b417ccdd5de3860de9f9bb6e9ce890b6_add (data) {
geo_json_09ba5926a79ad74ce059c256c57c60e9 geo_json_b417ccdd5de3860de9f9bb6e9ce890b6
.addData(data); .addData(data);
} }
geo_json_09ba5926a79ad74ce059c256c57c60e9_add({"features": [{"geometry": {"coordinates": [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 0.0, "Dist2": 0.0, "Id": "1", "Length": 0.0, "Name": "16_08_2025_13_03_09_Manual"}, "type": "Feature"}], "type": "FeatureCollection"}); geo_json_b417ccdd5de3860de9f9bb6e9ce890b6_add({"features": [{"geometry": {"coordinates": [[-47.395256450666665, -22.172346369833335], [-47.3952565435, -22.172346308833333], [-47.39525657683333, -22.1723463895], [-47.39525660783333, -22.172346390833333], [-47.395256612, -22.172346383666667], [-47.3952566095, -22.172346395833333], [-47.395256603, -22.17234638], [-47.39525663333333, -22.172346400166667], [-47.39525668666667, -22.172346436166666], [-47.39525669033333, -22.172346315], [-47.39525666283333, -22.172346244833335], [-47.3952566795, -22.172346234166668], [-47.39525670916667, -22.172346255666668], [-47.39525687216667, -22.172346330833335], [-47.395256888, -22.172346301], [-47.3952568545, -22.172346285], [-47.395256829, -22.172346216166666], [-47.39525682166666, -22.172346155333333], [-47.395256853333336, -22.172346067333333], [-47.39525682833333, -22.172346081333334], [-47.395256798333335, -22.172346064166668], [-47.39525658783333, -22.1723460885], [-47.395256589166664, -22.172345992], [-47.39525672683333, -22.17234593], [-47.3952567375, -22.1723458955], [-47.395256776833335, -22.172345912166666], [-47.3952566485, -22.172345831166666], [-47.39525651583333, -22.172345763833334], [-47.39525654966667, -22.172345739333334], [-47.395256393, -22.172345694166665], [-47.395256296166664, -22.172345727166668], [-47.39525628216666, -22.172345670166667], [-47.395256309333334, -22.1723456805], [-47.39525658116667, -22.172347672333334], [-47.39525688083334, -22.172348807333332], [-47.39525808733333, -22.172352117833334], [-47.39525877366667, -22.1723537055], [-47.395258515, -22.172356928333333], [-47.395258686666665, -22.172358251333332], [-47.395259433666666, -22.1723619705], [-47.39525973333333, -22.172363576], [-47.395260085, -22.172365142166665], [-47.395261047666665, -22.172367679166666], [-47.39526108883334, -22.172368784333333], [-47.39526103633333, -22.172368743833335], [-47.395260981166665, -22.172368728166667], [-47.395260906333334, -22.172370990833333], [-47.39526118316667, -22.172372657666667], [-47.39526149616667, -22.172374629166665], [-47.39526177716667, -22.1723753845], [-47.395261561333335, -22.1723772325], [-47.3952616635, -22.172379212666666], [-47.395259892666665, -22.1723810495], [-47.39525978933333, -22.172383026333332], [-47.395258998833334, -22.172386347833335], [-47.3952587635, -22.172388393333332], [-47.3952582685, -22.1723898615], [-47.395258494833335, -22.172393639666666], [-47.39525091216667, -22.172404825833333], [-47.39525013666667, -22.172405724833332], [-47.395250509, -22.172405612], [-47.3952558565, -22.172405050666665], [-47.39525849333333, -22.172409069], [-47.39526031516667, -22.172416536166665], [-47.3952563085, -22.172417938], [-47.395253391, -22.172420802166666], [-47.395256822166665, -22.17242057283333], [-47.39525690683333, -22.172424407666668], [-47.3952563335, -22.172425210166665], [-47.39525726883333, -22.172427588], [-47.395257951666665, -22.172428887166667], [-47.39525765083334, -22.172429874833334], [-47.39526025283333, -22.172433116666667], [-47.39526390183333, -22.172437103], [-47.3952669375, -22.172439262833333], [-47.39526957583333, -22.172440730833333], [-47.395276767333335, -22.172442326666665], [-47.39528067183333, -22.1724421815], [-47.39528122816667, -22.172442044666667], [-47.3952777085, -22.172442249166668], [-47.395270072833334, -22.172444610833335], [-47.395266372, -22.172447167833333], [-47.395261905, -22.172453009333335], [-47.395260418, -22.172456977166668], [-47.395258931166666, -22.172460745166667], [-47.395256378333336, -22.172467218166666], [-47.39525517933333, -22.1724699915], [-47.39525246066667, -22.1724764045], [-47.395250798333336, -22.172480014833333], [-47.3952483145, -22.1724866155], [-47.39524774866667, -22.172488646166666], [-47.395247018166664, -22.172494215333334], [-47.39524689433333, -22.172498196], [-47.39524629466667, -22.172500856166668], [-47.395242381833334, -22.17250657683333], [-47.39523919483333, -22.172508991666668], [-47.39523223733333, -22.172512476666668], [-47.3952284705, -22.172514312666667], [-47.39522158183333, -22.172517214333332], [-47.395217751333334, -22.172518779333334], [-47.395214971, -22.172520573666667], [-47.39521006733333, -22.1725241545], [-47.3952079915, -22.172527879833332], [-47.39520420033333, -22.1725354175], [-47.39520277766667, -22.1725386405], [-47.39520279683333, -22.17254124], [-47.39520273116667, -22.172542654166666], [-47.3952026795, -22.172543427166666], [-47.3952027095, -22.17254343], [-47.395202701833334, -22.172543459333333], [-47.395202711, -22.172543445166667], [-47.39520267983333, -22.172543447166667], [-47.395202698333335, -22.172543444], [-47.395202698333335, -22.172543444]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 0.0, "Dist2": 0.0, "Id": "1", "Length": 29.677008111445875, "Name": "22_08_2025_10_41_55_Manual"}, "type": "Feature"}], "type": "FeatureCollection"});
geo_json_09ba5926a79ad74ce059c256c57c60e9.setStyle(function(feature) {return feature.properties.style;}); geo_json_b417ccdd5de3860de9f9bb6e9ce890b6.setStyle(function(feature) {return feature.properties.style;});
geo_json_09ba5926a79ad74ce059c256c57c60e9.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad); geo_json_b417ccdd5de3860de9f9bb6e9ce890b6.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
</script> </script>
@ -185,7 +185,7 @@
} }
trajeto_json_add({"features": []}); trajeto_json_add({"features": []});
trajeto_json.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad); trajeto_json.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
function adicionarGeometria(novaGeometria) { function adicionarGeometria(novaGeometria) {
trajeto_json.addData(novaGeometria); trajeto_json.addData(novaGeometria);
@ -243,7 +243,7 @@
} }
trajeto_dinamico_json_add({"features": []}); trajeto_dinamico_json_add({"features": []});
trajeto_dinamico_json.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad); trajeto_dinamico_json.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
function adicionarGeometriaDinamica(novaGeometria) { function adicionarGeometriaDinamica(novaGeometria) {
trajeto_dinamico_json.addData(novaGeometria); trajeto_dinamico_json.addData(novaGeometria);
@ -296,9 +296,9 @@
var marcadorEquipamento = L.marker([0, 0], { var marcadorEquipamento = L.marker([0, 0], {
icon: customIcon icon: customIcon
}).addTo(map_fa80b7c1ff2d8431553ba20c387f3aad); }).addTo(map_353f6ba1c95182a48ae91b9a891f095d);
var marcadorBase = L.marker([0, 0], {}).addTo(map_fa80b7c1ff2d8431553ba20c387f3aad); var marcadorBase = L.marker([0, 0], {}).addTo(map_353f6ba1c95182a48ae91b9a891f095d);
var icon = L.AwesomeMarkers.icon( var icon = L.AwesomeMarkers.icon(
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"} {"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
); );
@ -380,7 +380,7 @@
} }
if (foco) { if (foco) {
map_fa80b7c1ff2d8431553ba20c387f3aad.setView(novaPosicao, map_fa80b7c1ff2d8431553ba20c387f3aad.getZoom()); map_353f6ba1c95182a48ae91b9a891f095d.setView(novaPosicao, map_353f6ba1c95182a48ae91b9a891f095d.getZoom());
} }
} }
@ -397,7 +397,7 @@
function atualizarSelecaoRuas(selecionadas) { function atualizarSelecaoRuas(selecionadas) {
selecionadas = JSON.parse(selecionadas); selecionadas = JSON.parse(selecionadas);
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : []; RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
geo_json_09ba5926a79ad74ce059c256c57c60e9.eachLayer(function (layer) { geo_json_b417ccdd5de3860de9f9bb6e9ce890b6.eachLayer(function (layer) {
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) { if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {
layer.setStyle({ color: 'blue' }); layer.setStyle({ color: 'blue' });
} else { } else {

View File

@ -199,23 +199,35 @@ class CameraOak:
except Exception as e: except Exception as e:
self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}") self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}")
if self.modelo_ia_seg is not None or self.modelo_ia_det is not None:
N_seg = self.modelo_ia_seg["det_every_n"]
N_det = self.modelo_ia_det["det_every_n"]
script = pipeline.create(dai.node.Script) script = pipeline.create(dai.node.Script)
if self.modelo_ia_seg is not None or self.modelo_ia_det is not None:
try:
N_seg = (self.modelo_ia_seg or {}).get("det_every_n", 1)
N_det = (self.modelo_ia_det or {}).get("det_every_n", 1)
script.setProcessor(dai.ProcessorType.LEON_CSS) script.setProcessor(dai.ProcessorType.LEON_CSS)
script.setScript(f""" script.setScript(f"""
from time import monotonic from time import monotonic
i = 0 i = 0
while True: while True:
f = node.io['in'].get() f = node.io['in'].get()
"""
+
f"""
if i % {N_seg} == 0: if i % {N_seg} == 0:
node.io['toSeg'].send(f) node.io['toSeg'].send(f)
""" if self.modelo_ia_seg is not None else ""
+
f"""
if i % {N_det} == 0: if i % {N_det} == 0:
node.io['toDet'].send(f) node.io['toDet'].send(f)
""" if self.modelo_ia_det is not None else ""
+
f"""
i += 1 i += 1
""") """)
cam.video.link(script.inputs['in']) cam.video.link(script.inputs['in'])
except Exception as e:
self.mostrar_log(f"Erro ao criar script: {e}")
if self.modelo_ia_seg is not None: if self.modelo_ia_seg is not None:
try: try:

View File

@ -93,7 +93,7 @@ def definir_comando(pid: PIDAdaptativo):
}, },
"RegrasAtivas": { "RegrasAtivas": {
"matriz_custo": False, "matriz_custo": False,
"deteccao_obstaculos": False, "deteccao_obstaculos": True,
}, },
"Equipamento": { "Equipamento": {
"largura": _equipamento.get("largura", 0.85), "largura": _equipamento.get("largura", 0.85),
@ -197,7 +197,8 @@ def _regras_taticas(contexto):
# >>> NOVO: gate de bloqueio/parede com block + d_obs_min <<< # >>> NOVO: gate de bloqueio/parede com block + d_obs_min <<<
if vw_operante and contexto.get("RegrasAtivas", {}).get("deteccao_obstaculos", False): if vw_operante and contexto.get("RegrasAtivas", {}).get("deteccao_obstaculos", False):
block = dados_vw.get("MatrizCusto", {}).get("Block", {}) block = dados_vw.get("MatrizCusto", {}).get("Block", None)
if block is not None:
reason = block.get("reason", "none") reason = block.get("reason", "none")
decision = block.get("decision", {}) decision = block.get("decision", {})

View File

@ -25,7 +25,7 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
_contexto = ContextoGlobalRedis.get_contexto() _contexto = ContextoGlobalRedis.get_contexto()
_controle = ContextoGlobalRedis.get_controle() _controle = ContextoGlobalRedis.get_controle()
pulverizador_automatico = _controle.get("pulverizador_automatico", False) pulverizador_automatico = _controle.get("pulverizador_automatico", False)
ervas_no_radar = _contexto.get("Gerais", {}).get("ervas_no_radar") ervas_no_radar = _contexto.get("Gerais", {}).get("ervas_no_radar", False)
vel_min = _operacao.get("Mov", {}).get("percent_vel_min", 0) vel_min = _operacao.get("Mov", {}).get("percent_vel_min", 0)
vel_max = _operacao.get("Mov", {}).get("percent_vel_max", 100) vel_max = _operacao.get("Mov", {}).get("percent_vel_max", 100)
ang_max = _operacao.get("Dir", {}).get("angulo_max", 30) ang_max = _operacao.get("Dir", {}).get("angulo_max", 30)
@ -57,7 +57,7 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
elif status_carro in [StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando]: elif status_carro in [StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando]:
velocidade_sp = vel_min velocidade_sp = vel_min
elif status_carro == StatusCarroMapa.CaminhandoRua: elif status_carro == StatusCarroMapa.CaminhandoRua:
if pulverizador_automatico and ervas_no_radar > 0: if pulverizador_automatico and ervas_no_radar:
velocidade_sp = vel_min velocidade_sp = vel_min
else: else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro) velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro)

View File

@ -43,14 +43,14 @@ class CameraManager:
self.mx_id = mx_id self.mx_id = mx_id
from weed_worker.config import load_seg_config from weed_worker.config import load_seg_config
camera_config = load_seg_config() seg_config = load_seg_config()
try: try:
nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_onboard=camera_config) nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_seg=seg_config)
if nova.iniciado: if nova.iniciado:
self.camera = nova self.camera = nova
except Exception as e: except Exception as e:
self.mostrar_log(f"⚠️ Camera com ID {mx_id} não conectada.") self.mostrar_log(f"⚠️ Camera com ID {mx_id} não conectada: {e}")
self.iniciando = False self.iniciando = False
return return
if self.camera is None: if self.camera is None:

View File

@ -92,7 +92,8 @@ def load_seg_config(force_reload=False):
"min_frac_erva_top_off": 0.0010, "min_frac_erva_top_off": 0.0010,
"min_frac_erva_por_bico": 0.02, "min_frac_erva_por_bico": 0.02,
"usar_morfologia": True, "usar_morfologia": True,
"kernel_morf": 3 "kernel_morf": 3,
"det_every_n": 1,
} }
dadosAtu = ContextoGlobalRedis.get_operacao().get("Atu", {}) dadosAtu = ContextoGlobalRedis.get_operacao().get("Atu", {})
contexto = ContextoGlobalRedis.get_contexto() contexto = ContextoGlobalRedis.get_contexto()

View File

@ -2,7 +2,6 @@ from enum import IntEnum
import time import time
import cv2 import cv2
import numpy as np import numpy as np
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
from shared.utils import encode_image_base64 from shared.utils import encode_image_base64
class ClassesSegmentacao(IntEnum): class ClassesSegmentacao(IntEnum):
@ -47,7 +46,6 @@ class WeedDetector:
self.ervas_ativas_filtradas = [] self.ervas_ativas_filtradas = []
self.next_erva_id = 1 self.next_erva_id = 1
self.frame_idx = 0 self.frame_idx = 0
self.ervas_identificadas = [dict() for _ in range(qtd_bicos)]
self.ultimo_status_bicos = {i: False for i in range(qtd_bicos)} self.ultimo_status_bicos = {i: False for i in range(qtd_bicos)}
self.ervas_registradas_bico = [set() for _ in range(qtd_bicos)] self.ervas_registradas_bico = [set() for _ in range(qtd_bicos)]
self.pred_rgb = np.empty((self.resolucao[1], self.resolucao[0], 3), dtype=np.uint8) self.pred_rgb = np.empty((self.resolucao[1], self.resolucao[0], 3), dtype=np.uint8)
@ -59,12 +57,7 @@ class WeedDetector:
self._erva_frac_global_ema = 0.0 self._erva_frac_global_ema = 0.0
self._ervas_no_radar = False self._ervas_no_radar = False
self._ervas_no_radar_percent = 0.0 self._ervas_no_radar_percent = 0.0
self.debug_estat = False self.debug_estat = True
ContextoGlobalRedis.atualizar_ctx_dict(
CtxKey.DadosWeedWorker,
analise__ervas_identificadas=self.ervas_identificadas
)
def _segmentar_predictions(self, predictions): def _segmentar_predictions(self, predictions):
try: try:
@ -112,9 +105,7 @@ class WeedDetector:
"timestamp": time.time(), "timestamp": time.time(),
"height": predictions.shape[0], "height": predictions.shape[0],
"width": predictions.shape[1], "width": predictions.shape[1],
"deteccoes": [], # deteccoes_filtro,
"controle": controle_bicos, "controle": controle_bicos,
"ervas_identificadas": self.ervas_identificadas,
"ervas_no_radar": ervas_no_radar, "ervas_no_radar": ervas_no_radar,
"estatisticas": { "estatisticas": {
"erva": estat_erva, "erva": estat_erva,
@ -251,7 +242,7 @@ class WeedDetector:
"ema_global": ema_g, "ema_global": ema_g,
"thr_on_global": on_global, "thr_on_global": on_global,
"thr_off_global": off_global, "thr_off_global": off_global,
"vel_adj": adj, "vel_adj": adj
} }
return ervas_no_radar, None return ervas_no_radar, None

View File

@ -2,8 +2,8 @@
#include <SimpleKalmanFilter.h> #include <SimpleKalmanFilter.h>
// Defina os pinos para o módulo HX711 // Defina os pinos para o módulo HX711
#define PINO_DT 4 // Pino de dados do HX711 ao GPIO5 do ESP32 #define PINO_DT 15 // Pino de dados do HX711 ao GPIO5 do ESP32
#define PINO_SCK 5 // Pino de clock do HX711 ao GPIO18 do ESP32 #define PINO_SCK 16 // Pino de clock do HX711 ao GPIO18 do ESP32
SimpleKalmanFilter kalman = SimpleKalmanFilter(2, 2, 0.01); SimpleKalmanFilter kalman = SimpleKalmanFilter(2, 2, 0.01);
double massaRef = 0.8; double massaRef = 0.8;

View File

@ -0,0 +1,276 @@
#include "HX711.h"
#include <SimpleKalmanFilter.h>
// --------- Pinos (ESP32) ----------
#define PINO_DT 15 // HX711 DT
#define PINO_SCK 16 // HX711 SCK
// --------- Opções de filtragem ----------
int USAR_KALMAN = 1;
#define MEDIA_N 4 // média simples antes do Kalman (2~4 é bom)
// Kalman: (measErr, estErr, q)
SimpleKalmanFilter kalman(0.05, 0.05, 0.25);
// --------- HX711 ----------
HX711 balanca;
// --------- Estado ----------
bool temBaseline = false;
long baseline_raw = 0; // raw sem peso (offset "real")
float escala_cnt_por_kg = 1.0f; // scale
bool scale_negativo = false;
bool calibrado = false;
// --------- Utilidades ----------
String rxLine;
long lerRawMedio(uint8_t n = 10, uint16_t dt_ms = 4) {
long soma = 0;
for (uint8_t i = 0; i < n; i++) {
soma += balanca.read();
delay(dt_ms);
}
return soma / (long)n;
}
float lerKgInstantaneo() {
// Sem depender de tare():
// kg = (raw_atual - baseline_raw) / escala_cnt_por_kg
long raw = lerRawMedio(MEDIA_N, 2);
float kg = (float)(raw - baseline_raw) / escala_cnt_por_kg;
#if USAR_KALMAN
return kalman.updateEstimate(kg);
#else
return kg;
#endif
}
void aplicarOffsetEScale(long offset_raw, float scale_cnt_por_kg) {
// Se a tua lib tiver set_offset(), usamos pra manter get_units funcional
// Mas como estamos calculando na mão, isto é opcional.
// Ainda assim setamos por conveniência:
balanca.set_offset(offset_raw);
balanca.set_scale(scale_cnt_por_kg);
baseline_raw = offset_raw;
escala_cnt_por_kg = scale_cnt_por_kg;
temBaseline = true;
calibrado = true;
}
void printStatus() {
Serial.println(F("\n--- STATUS ---"));
Serial.print(F("baseline_raw (offset): ")); Serial.println(baseline_raw);
Serial.print(F("scale (counts/kg): ")); Serial.println(escala_cnt_por_kg, 6);
Serial.print(F("calibrado: ")); Serial.println(calibrado ? "sim" : "nao");
Serial.print(F("kg (inst): ")); Serial.println(lerKgInstantaneo(), 3);
Serial.println(F("--------------\n"));
}
// --------- Comandos ---------
// Formatos aceitos (digite e ENTER no Serial Monitor em 115200):
// baseline -> mede e salva raw sem peso
// calibrar <kg> -> mede raw com peso e calcula scale = (raw1 - baseline) / kg
// peso <kg> -> alias de calibrar <kg>
// scale <valor> -> aplica scale diretamente (mantém baseline salvo)
// offset <valor> -> aplica offset/baseline diretamente
// status -> mostra config atual
// q <valor> -> ajusta Kalman.Q (ruído de processo)
// kalman on/off -> liga/desliga Kalman
// help -> lista
void processCommand(const String& line) {
String cmd = line;
cmd.trim();
cmd.toLowerCase();
if (cmd == "" ) return;
if (cmd == "help") {
Serial.println(F("\nComandos:"));
Serial.println(F(" baseline"));
Serial.println(F(" calibrar <kg> (ex: calibrar 6.045)"));
Serial.println(F(" peso <kg> (alias de calibrar)"));
Serial.println(F(" scale <valor> (counts por kg)"));
Serial.println(F(" offset <valor> (raw sem peso)"));
Serial.println(F(" status"));
Serial.println(F(" q <valor> (ajusta Kalman Q)"));
Serial.println(F(" kalman on | off"));
Serial.println();
return;
}
if (cmd == "baseline") {
Serial.println(F("\n[BASELINE] Deixe SEM peso e mantenha estável..."));
delay(1200);
long raw0 = lerRawMedio(20, 5);
temBaseline = true;
baseline_raw = raw0;
// Mantém scale atual; se ainda não calibrado, fica 1.0
balanca.set_offset(baseline_raw);
Serial.print(F("[OK] baseline_raw = "));
Serial.println(baseline_raw);
if (!calibrado) {
Serial.println(F("Agora coloque uma massa conhecida e use: calibrar <kg>"));
}
return;
}
if (cmd.startsWith("calibrar ") || cmd.startsWith("peso ")) {
int sp = cmd.indexOf(' ');
if (sp > 0) {
float kg_ref = cmd.substring(sp + 1).toFloat();
if (kg_ref <= 0.0f) {
Serial.println(F("[ERRO] Peso de referencia invalido."));
return;
}
if (!temBaseline) {
Serial.println(F("[ERRO] Sem baseline. Rode 'baseline' primeiro (sem peso)."));
return;
}
Serial.print(F("\n[CALIBRAR] Massa de referencia = "));
Serial.print(kg_ref, 3);
Serial.println(F(" kg. Mantenha estavel..."));
delay(1500);
long raw1 = lerRawMedio(30, 5);
long delta = raw1 - baseline_raw;
if (delta == 0) {
Serial.println(F("[ERRO] Delta zero. Cheque conexoes / massa / baseline."));
return;
}
float scale_calc = (float)delta / kg_ref;
if (scale_calc < 0) scale_calc = -scale_calc; // garante sinal correto
aplicarOffsetEScale(baseline_raw, scale_calc);
// Teste rápido
float kg_med = lerKgInstantaneo();
Serial.print(F("[OK] scale = "));
Serial.println(scale_calc, 6);
Serial.print(F("Leitura estimada (esperado ~ "));
Serial.print(kg_ref, 3);
Serial.print(F(" kg): "));
Serial.print(kg_med, 3);
Serial.println(F(" kg\nRemova a massa e use 'status' para conferir.\n"));
return;
}
}
if (cmd.startsWith("scale ")) {
int sp = cmd.indexOf(' ');
float sc = cmd.substring(sp + 1).toFloat();
if (sc <= 0.0f) {
Serial.println(F("[ERRO] Scale invalido."));
return;
}
escala_cnt_por_kg = sc;
balanca.set_scale(sc);
calibrado = temBaseline; // se já tem baseline, agora temos os dois
Serial.print(F("[OK] scale = "));
Serial.println(escala_cnt_por_kg, 6);
return;
}
if (cmd.startsWith("offset ")) {
int sp = cmd.indexOf(' ');
long off = (long)cmd.substring(sp + 1).toDouble(); // aceita grandão
baseline_raw = off;
temBaseline = true;
balanca.set_offset(off);
calibrado = calibrado || (escala_cnt_por_kg > 0.0f);
Serial.print(F("[OK] baseline_raw (offset) = "));
Serial.println(baseline_raw);
return;
}
if (cmd == "status") {
printStatus();
return;
}
if (cmd.startsWith("q ")) {
int sp = cmd.indexOf(' ');
float q = cmd.substring(sp + 1).toFloat();
if (q <= 0) {
Serial.println(F("[ERRO] Q invalido."));
return;
}
// recria o filtro mantendo erros iguais
kalman = SimpleKalmanFilter(0.05, 0.05, q);
Serial.print(F("[OK] Kalman.Q = "));
Serial.println(q, 4);
return;
}
if (cmd == "kalman on") {
//#if USAR_KALMAN
// Serial.println(F("[OK] Kalman ja esta ON (compile-time)."));
//#else
// Serial.println(F("[INFO] Recompile com USAR_KALMAN=1 para ligar."));
//#endif
Serial.println(F("[OK] Kalman ativado."));
USAR_KALMAN = 1;
return;
}
if (cmd == "kalman off") {
//#if USAR_KALMAN
// Serial.println(F("[INFO] Recompile com USAR_KALMAN=0 para desligar."));
//#else
// Serial.println(F("[OK] Kalman OFF (compile-time)."));
//#endif
Serial.println(F("[OK] Kalman desativado."));
USAR_KALMAN = 0;
return;
}
Serial.println(F("[ERRO] Comando desconhecido. Digite 'help' para lista."));
}
// --------- Setup / Loop ---------
void setup() {
Serial.begin(115200);
delay(300);
balanca.begin(PINO_DT, PINO_SCK);
// Não usar tare(). Vamos trabalhar com baseline/offset reais.
balanca.set_scale(1.0); // neutro inicial
// offset será configurado quando fizermos 'baseline' ou 'offset'
Serial.println(F("\n=== Balança 4 células (HX711) ==="));
Serial.println(F("Pinos: DT=15, SCK=16"));
Serial.println(F("Fluxo sugerido:"));
Serial.println(F(" 1) baseline (sem peso)"));
Serial.println(F(" 2) calibrar 6.045 (exemplo)"));
Serial.println(F(" 3) status"));
Serial.println(F("Comandos: help | baseline | calibrar <kg> | peso <kg> | scale <v> | offset <v> | status | q <v> | kalman on/off\n"));
}
void loop() {
// Coleta linha completa do Serial (até '\n')
while (Serial.available()) {
char c = (char)Serial.read();
if (c == '\r') continue;
if (c == '\n') {
processCommand(rxLine);
rxLine = "";
} else {
rxLine += c;
// proteção simples contra linha gigante
if (rxLine.length() > 120) rxLine = "";
}
}
// Leitura contínua
if (temBaseline && (escala_cnt_por_kg > 0.0f)) {
float kg = lerKgInstantaneo();
Serial.print(F("Massa (kg): "));
Serial.println(kg, 3);
} else {
// Ajuda visual até calibrar
long raw = lerRawMedio(4, 2);
Serial.print(F("Aguardando baseline/calibracao... raw="));
Serial.println(raw);
}
delay(120);
}

View File

@ -1,356 +1,466 @@
#include "SerialService.h" #include "SerialService.h"
// CanService.h
#ifndef CANSERVICE_H #ifndef CANSERVICE_H
#define CANSERVICE_H #define CANSERVICE_H
/*
#include <CAN.h>
#include <Arduino.h>
class CanService {
public:
typedef void (*OnReceiveCallback)(int packetSize, int senderId, byte funcCode, byte* data, int dataLength);
CanService(uint8_t nodeId, uint32_t baudRate = 500E3) {
_baudRate = baudRate;
_nodeId = nodeId;
}
void begin() {
if (!CAN.begin(_baudRate)) {
PrintTela("[CAN] Falha ao iniciar");
return;
}
PrintTela("[CAN] Inicializado com sucesso");
CAN.onReceive(onReceiveWrapper);
instance = this;
}
void loop() {
// Apenas processa mensagens recebidas via interrupcao
}
void setReceiveCallback(OnReceiveCallback cb) {
_callback = cb;
}
bool enviarMensagem(byte funcCode, const std::vector<uint8_t>& payload) {
if (payload.size() > 7) return false;
CAN.beginPacket(_nodeId);
CAN.write(funcCode);
for (uint8_t b : payload) {
CAN.write(b);
}
return CAN.endPacket();
}
std::vector<uint8_t> MontarFrameChk(T_Code D_Code, bool Conectado, int Versao) {
std::vector<uint8_t> resposta;
resposta.push_back(static_cast<uint8_t>(D_Code)); // Tipo do módulo
resposta.push_back(Conectado ? 1 : 0); // Conectado
resposta.push_back(Versao); // Versão
return resposta;
}
private:
uint32_t _baudRate;
uint8_t _nodeId;
OnReceiveCallback _callback = nullptr;
static CanService* instance;
static void onReceiveWrapper(int packetSize) {
if (instance) instance->onReceive(packetSize);
}
void onReceive(int packetSize) {
int senderId = CAN.packetId();
byte buffer[8];
int i = 0;
while (CAN.available() && i < 8) {
buffer[i++] = CAN.read();
}
byte funcCode = buffer[0];
if (_callback != nullptr) {
_callback(packetSize, senderId, funcCode, buffer + 1, i - 1);
}
}
};
CanService* CanService::instance = nullptr;
*/
#include <Arduino.h> #include <Arduino.h>
#include <driver/twai.h> #include <driver/twai.h>
#include <esp_task_wdt.h>
// Garante o core de aplicação (ESP32-S3)
#ifndef APP_CPU_NUM
#define APP_CPU_NUM 1
#endif
class CanService { class CanService {
public:
typedef void (*OnReceiveCallback)(int packetSize, int senderId,
CanMessagePosicaoDados posicao, byte* data, int dataLength);
typedef void (*OnReceiveCallback)(int packetSize, int senderId, CanMessagePosicaoDados posicao, byte* data, int dataLength); // --- IDs especiais do teu protocolo ---
typedef struct {
OnReceiveCallback callback;
int packetSize;
int senderId;
CanMessagePosicaoDados posicao;
byte data[7];
int dataLength;
long idMsg;
} CallbackPayload;
static TaskHandle_t handleCallbackTask;
public:
uint8_t ID_Num_sMOD = 0; uint8_t ID_Num_sMOD = 0;
uint8_t ID_Num_sTOD = 250; uint8_t ID_Num_sTOD = 250;
uint8_t ID_Num_sLRA = 251; uint8_t ID_Num_sLRA = 251;
CanService(uint8_t nodeId, uint32_t baudRate = 500000) { CanService(uint8_t nodeId, uint32_t baudRate = 500000)
_nodeId = nodeId; : _nodeId(nodeId), _baudRate(baudRate) {}
_baudRate = baudRate;
}
void begin() { void begin() {
PrintTela("[CAN] Iniciando TWAI..."); PrintTela("[CAN] Iniciando TWAI...");
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_5, GPIO_NUM_4, TWAI_MODE_NORMAL); // modo sem ACK // Modo NORMAL (com ACK)
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_5, GPIO_NUM_4, TWAI_MODE_NORMAL);
// Aumenta filas internas do driver para evitar enrosco
g_config.tx_queue_len = 32;
g_config.rx_queue_len = 32;
// (alerts_enabled será reconfigurado depois de start)
// 500 kbit/s (ajuste se precisar)
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
// Começa aceitando tudo para depurar; depois afinamos a máscara
twai_filter_config_t f_config = { twai_filter_config_t f_config = {
.acceptance_code = (_nodeId << 21), // Shiftado para alinhar com o padrão TWAI .acceptance_code = (uint32_t(_nodeId) << 21),
.acceptance_mask = ~(0x7FF << 21), // Mascara para filtrar apenas esse ID .acceptance_mask = ~(0x7FFu << 21),
.single_filter = true .single_filter = true
}; };
esp_err_t err; esp_err_t err = twai_driver_install(&g_config, &t_config, &f_config);
err = twai_driver_install(&g_config, &t_config, &f_config);
if (err != ESP_OK) { if (err != ESP_OK) {
PrintTela("[CAN] Erro ao instalar driver: ", false); PrintTela("[CAN] Erro ao instalar driver: ", false); PrintTela(String(err));
PrintTela(String(err));
return; return;
} }
err = twai_start(); err = twai_start();
if (err != ESP_OK) { if (err != ESP_OK) {
PrintTela("[CAN] Erro ao iniciar TWAI: ", false); PrintTela("[CAN] Erro ao iniciar TWAI: ", false); PrintTela(String(err));
PrintTela(String(err));
return; return;
} }
if (CanTaskRxHandle == NULL) { // Habilita alerts úteis
canQueueRx = xQueueCreate(50, sizeof(twai_message_t)); uint32_t alert_mask = 0;
if (canQueueRx == NULL) { alert_mask |= TWAI_ALERT_BUS_OFF;
PrintTela("[CAN] Falha ao criar fila RX!"); alert_mask |= TWAI_ALERT_RX_QUEUE_FULL;
} alert_mask |= TWAI_ALERT_TX_FAILED;
xTaskCreatePinnedToCore(CanService::CanTaskRxWrapper, "CanTaskRx", 4096, this, 17, &CanTaskRxHandle, APP_CPU_NUM);
}
if (CanTaskTxHandle == NULL) { #if defined(TWAI_ALERT_PERIPH_ERR)
canQueueTx = xQueueCreate(50, sizeof(twai_message_t)); alert_mask |= TWAI_ALERT_PERIPH_ERR;
if (canQueueTx == NULL) { #elif defined(TWAI_ALERT_PERIPH_RESET)
PrintTela("[CAN] Falha ao criar fila TX!"); alert_mask |= TWAI_ALERT_PERIPH_RESET;
} #endif
xTaskCreatePinnedToCore(CanService::CanTaskTxWrapper, "CanTaskTx", 4096, this, 16, &CanTaskTxHandle, APP_CPU_NUM);
}
if (CanTaskProcessHandle == NULL) { #if defined(TWAI_ALERT_RECOVERY_COMPLETE)
xTaskCreatePinnedToCore(CanService::CanTaskProcessWrapper, "CanTaskProcess", 6144, this, 18, &CanTaskProcessHandle, APP_CPU_NUM); alert_mask |= TWAI_ALERT_RECOVERY_COMPLETE;
} #elif defined(TWAI_ALERT_RECOVERY_IN_PROGRESS)
alert_mask |= TWAI_ALERT_RECOVERY_IN_PROGRESS;
#endif
twai_reconfigure_alerts(alert_mask, nullptr);
// --- Filas da nossa arquitetura ---
callbacksQ = xQueueCreate(64, sizeof(twai_message_t)); // jobs de RX -> callback
keysQ = xQueueCreate(64, sizeof(uint16_t)); // chaves coalescentes para TX
if (!callbacksQ || !keysQ) { PrintTela("[CAN] Falha ao criar filas"); }
// --- Tasks ---
xTaskCreatePinnedToCore(CanTaskRxWrapper, "CanTaskRx", 4096, this, 6, &CanTaskRxHandle, APP_CPU_NUM);
xTaskCreatePinnedToCore(CallbackWorkerWrapper,"CallbackWorker",6144,this,5,&CallbackWorkerHandle,APP_CPU_NUM);
xTaskCreatePinnedToCore(CanTaskTxWrapper, "CanTaskTx", 4096, this, 4, &CanTaskTxHandle, APP_CPU_NUM);
xTaskCreatePinnedToCore(HealthTaskWrapper, "CanHealth", 4096, this, 3, &HealthTaskHandle, APP_CPU_NUM);
// Watchdog por task (vamos resetar dentro dos loops)
esp_task_wdt_init(5, true);
esp_task_wdt_add(CanTaskRxHandle);
esp_task_wdt_add(CallbackWorkerHandle);
esp_task_wdt_add(CanTaskTxHandle);
esp_task_wdt_add(HealthTaskHandle);
PrintTela("[CAN] TWAI iniciado com sucesso"); PrintTela("[CAN] TWAI iniciado com sucesso");
} }
// --- Filas e tasks ---
QueueHandle_t callbacksQ = nullptr; // fila para o callback worker
QueueHandle_t keysQ = nullptr; // fila de chaves para TX coalescente
// (Opcional) Se quiser manter uma RX intermediária: QueueHandle_t canQueueRx = nullptr;
QueueHandle_t canQueueRx;
TaskHandle_t CanTaskRxHandle = NULL; TaskHandle_t CanTaskRxHandle = NULL;
TaskHandle_t CallbackWorkerHandle = NULL;
TaskHandle_t CanTaskTxHandle = NULL;
TaskHandle_t HealthTaskHandle = NULL;
// --- Métricas/health ---
volatile uint64_t last_rx_ts = 0; // us
volatile uint64_t last_tx_ts = 0; // us
volatile uint32_t rx_drops = 0;
volatile uint32_t tx_retries = 0;
// --- TX coalescing (tabela simples) ---
static const int MAX_KEYS = 64; // ajuste conforme nº (pos,addr)
struct Slot {
uint16_t key = 0;
bool used = false;
bool pending = false; // 🔧 NOVO: chave já enfileirada para envio
twai_message_t msg;
uint32_t gen = 0;
};
Slot latestByKey[MAX_KEYS];
// Helpers de chave (pos,addr -> 16 bits)
static inline uint16_t make_key(const twai_message_t& m) {
return (m.data_length_code >= 2) ? ( (uint16_t(m.data[0])<<8) | uint16_t(m.data[1]) ) : 0xFFFF;
}
bool put_latest(uint16_t key, const twai_message_t& m) {
int idx = key % MAX_KEYS;
for (int i=0; i<MAX_KEYS; ++i, idx=(idx+1)%MAX_KEYS) {
if (!latestByKey[idx].used || latestByKey[idx].key == key) {
latestByKey[idx].used = true;
latestByKey[idx].key = key;
latestByKey[idx].msg = m;
latestByKey[idx].gen++;
return true;
}
}
return false;
}
bool get_latest(uint16_t key, twai_message_t* out) {
int idx = key % MAX_KEYS;
for (int i=0; i<MAX_KEYS; ++i, idx=(idx+1)%MAX_KEYS) {
if (latestByKey[idx].used && latestByKey[idx].key == key) { *out = latestByKey[idx].msg; return true; }
if (!latestByKey[idx].used) break;
}
return false;
}
static void CanTaskRxWrapper(void *pvParameters) { static void CanTaskRxWrapper(void *pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters); static_cast<CanService*>(pvParameters)->CanTaskRx(pvParameters);
service->CanTaskRx(pvParameters);
} }
void CanTaskRx(void* pvParameters) { void CanTaskRx(void* pvParameters) {
twai_message_t message; twai_message_t message;
while (true) { for (;;) {
if (twai_receive(&message, pdMS_TO_TICKS(10)) == ESP_OK) { // WDT
esp_task_wdt_reset();
if (message.data_length_code == 0 || message.extd) continue; if (twai_receive(&message, pdMS_TO_TICKS(50)) == ESP_OK) {
// ignore frames que não vamos tratar
if (message.data_length_code == 0 || message.extd || message.rtr) {
continue;
}
bool MensagemTx = message.identifier == static_cast<uint32_t>(_nodeId + 1); // marca vida do barramento SEMPRE que recebeu algo válido
last_rx_ts = esp_timer_get_time();
if (DebugMode) { if (DebugMode) {
PrintTela("[CAN ", false); PrintTela("[CAN RX] ID: 0x" + String(message.identifier, HEX) + " Len: " + String(message.data_length_code));
PrintTela(MensagemTx ? "TX" : "RX", false);
PrintTela("] ID: 0x", false);
PrintTela(String(message.identifier, HEX), false);
PrintTela(" Len: ", false);
PrintTela(String(message.data_length_code), false);
PrintTela(" Data: ", false);
for (int i = 0; i < message.data_length_code; i++) {
PrintTela(String(message.data[i], HEX), false);
PrintTela(" ", false);
}
PrintTela("");
} }
if (MensagemTx) { // tenta enfileirar pro worker
PrintTela("[CAN RX] Ouviu a própria resposta!"); if (callbacksQ && xQueueSend(callbacksQ, &message, 0) != pdTRUE) {
continue; rx_drops++;
// (opcional) política "drop oldest": remove 1 e tenta de novo
// twai_message_t dump;
// if (xQueueReceive(callbacksQ, &dump, 0) == pdTRUE) {
// xQueueSend(callbacksQ, &message, 0);
// }
vTaskDelay(pdMS_TO_TICKS(1)); // dá uma respirada
}
} }
if (!mensagemJaNaFila(canQueueRx, message)) {
if (uxQueueSpacesAvailable(canQueueRx) < 5) {
PrintTela("[CAN RX] Alerta: Fila RX quase cheia (" + String(uxQueueSpacesAvailable(canQueueRx)) + " mensagens restantes)");
}
if (xQueueSend(canQueueRx, &message, 0) != pdTRUE) {
PrintTela("[CAN RX] Fila RX cheia! Mensagem descartada.");
}
else {
//PrintTela("[CAN RX] Mensagem adicionada na fila RX");
}
} else {
PrintTela("[CAN RX] Mensagem duplicada ignorada.");
}
}
vTaskDelay(1);
}
}
QueueHandle_t canQueueTx;
TaskHandle_t CanTaskTxHandle = NULL;
static void CanTaskTxWrapper(void *pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
service->CanTaskTx(pvParameters);
}
void CanTaskTx(void* pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
twai_message_t msg;
while (true) {
if (xQueueReceive(service->canQueueTx, &msg, portMAX_DELAY) == pdTRUE) {
//PrintTela("[CAN TX] Mensagem recebida da fila TX");
if (service->enviarDadosCan(msg)) {
//PrintTela("[CAN TX] Mensagem enviada via CAN com sucesso");
}
else {
PrintTela("[CAN TX] Erro ao enviar mensagem via CAN");
}
}
vTaskDelay(1); vTaskDelay(1);
} }
} }
TaskHandle_t CanTaskProcessHandle = NULL; static void CallbackWorkerWrapper(void *pvParameters) {
static void CanTaskProcessWrapper(void *pvParameters) { static_cast<CanService*>(pvParameters)->CallbackWorker(pvParameters);
CanService *service = static_cast<CanService*>(pvParameters);
service->CanTaskProcess(pvParameters);
} }
void CanTaskProcess(void* pvParameters) { void CallbackWorker(void* pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
twai_message_t msg; twai_message_t msg;
for (;;) {
// WDT
esp_task_wdt_reset();
while (true) { if (xQueueReceive(callbacksQ, &msg, portMAX_DELAY) == pdTRUE) {
while (uxQueueMessagesWaiting(service->canQueueRx) > 0) { if (_callback && msg.data_length_code > 0) {
if (xQueueReceive(service->canQueueRx, &msg, portMAX_DELAY) == pdTRUE) {
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)msg.data[0]; CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)msg.data[0];
_callback(msg.data_length_code, msg.identifier, posicao,
if (service->_callback) { &msg.data[1], msg.data_length_code - 1);
String frameEmProcessamento = ""; }
for (int i = 0; i < msg.data_length_code; i++) {
frameEmProcessamento += String(msg.data[i], HEX) + " ";
} }
idmsg++; // yield leve
taskYIELD();
}
}
CallbackPayload* payload = new CallbackPayload();
payload->idMsg = idmsg;
payload->callback = service->_callback;
payload->packetSize = msg.data_length_code;
payload->senderId = msg.identifier;
payload->posicao = posicao;
memcpy(payload->data, &msg.data[1], msg.data_length_code - 1);
payload->dataLength = msg.data_length_code - 1;
// Captura o handle da task atual (quem vai esperar a notificação) void PublicarTx(const twai_message_t& msg) {
TaskHandle_t callingTaskHandle = xTaskGetCurrentTaskHandle(); // Sanidade básica
if (msg.data_length_code < 2 || msg.data_length_code > 8) return;
if (msg.extd || msg.rtr) return; // trabalhamos com standard data frames
// Cria um pacote com o payload + handle do notificador uint16_t key = make_key(msg);
auto* bundle = new std::pair<CallbackPayload*, TaskHandle_t>(payload, callingTaskHandle); if (key == 0xFFFF) return;
PrintTela("[CAN Process] Processando frame: " + frameEmProcessamento); // Atualiza a versão mais nova na tabela
int idx = key % MAX_KEYS;
bool stored = false;
for (int i=0; i<MAX_KEYS; ++i, idx=(idx+1)%MAX_KEYS) {
if (!latestByKey[idx].used || latestByKey[idx].key == key) {
latestByKey[idx].used = true;
latestByKey[idx].key = key;
latestByKey[idx].msg = msg;
latestByKey[idx].gen++;
stored = true;
// Se ainda não está pendente, enfileira a chave
if (!latestByKey[idx].pending && keysQ) {
if (xQueueSend(keysQ, &key, 0) == pdTRUE) {
latestByKey[idx].pending = true;
} else {
// fila cheia: sem pânico; mantemos a versão mais nova no slot
}
}
break;
}
}
if (!stored) {
// tabela cheia (raríssimo se MAX_KEYS está adequado) — poderia logar/contar.
}
}
String taskName = "TaskCallbackTimeout_" + String(idmsg); // --- Wrapper padrão ---
static void CanTaskTxWrapper(void *pvParameters) {
static_cast<CanService*>(pvParameters)->CanTaskTx(pvParameters);
}
TaskHandle_t handleTask = nullptr; // --- Task de TX com retry/backoff e limpeza do "pending" ---
void CanTaskTx(void* pvParameters) {
uint16_t key;
for (;;) {
// WDT
esp_task_wdt_reset();
// Cria a task do callback if (xQueueReceive(keysQ, &key, portMAX_DELAY) == pdTRUE) {
BaseType_t status = xTaskCreatePinnedToCore( // busca a msg mais nova dessa chave
TaskCallbackWrapper, twai_message_t m{};
taskName.c_str(), int idx = key % MAX_KEYS;
8192, int foundIdx = -1;
bundle, for (int i=0; i<MAX_KEYS; ++i, idx=(idx+1)%MAX_KEYS) {
5, if (latestByKey[idx].used && latestByKey[idx].key == key) {
&handleTask, m = latestByKey[idx].msg;
APP_CPU_NUM foundIdx = idx;
); break;
}
if (!latestByKey[idx].used) break;
}
if (status != pdPASS) { if (foundIdx < 0) {
PrintTela("[CAN Process] ❌ Falha ao criar task de callback!"); // chave desapareceu; nada a fazer
delete payload;
delete bundle;
continue; continue;
} }
if (false && msg.data[1] == ID_Num_sLRA) { // Garantias do frame (caso quem chamou não tenha setado)
// Comandos para o LoRa nao precisam aguardar, podem ser processados sem tempo m.extd = 0; // standard frame
} m.rtr = 0; // data frame
else {
// Aguarda até 5000ms pela notificação de término
BaseType_t sinal = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000));
if (sinal == 0) { bool ok = false;
PrintTela("[CAN Process] ⚠️ Callback demorou demais: " + String(payload->idMsg) + " — timeout ao executar comando: " + frameEmProcessamento); for (int tent=0; tent<3; ++tent) {
if (enviarDadosCan(m, pdMS_TO_TICKS(50))) {
vTaskDelay(pdMS_TO_TICKS(10)); // dá tempo da task morrer sozinha last_tx_ts = esp_timer_get_time();
eTaskState estado = eTaskGetState(handleTask); ok = true;
break;
if (estado != eDeleted && estado != eInvalid) {
vTaskDelete(handleTask);
} }
tx_retries++;
vTaskDelay(pdMS_TO_TICKS(5));
} }
// Limpa o "pending" SEMPRE; se falhou, a app pode chamar PublicarTx de novo
latestByKey[foundIdx].pending = false;
if (!ok) {
// Re-enfileira a chave só se ainda houver interesse (a app pode ter publicado algo novo)
// Re-check: se a slot ainda está usada e não está pendente, refile
if (latestByKey[foundIdx].used && !latestByKey[foundIdx].pending) {
if (xQueueSend(keysQ, &key, 0) == pdTRUE) {
latestByKey[foundIdx].pending = true;
} }
} }
} }
} }
vTaskDelay(1); taskYIELD();
} }
} }
// --- Versão de enviarDadosCan com timeout custom ---
bool enviarDadosCan(const twai_message_t& msg, TickType_t timeoutTicks) {
// Aqui você pode normalizar o ID/dados se precisar
bool mensagemJaNaFila(QueueHandle_t fila, const twai_message_t& novaMsg) { // Ex.: msg.identifier já deve estar correto para o PC
twai_message_t msgTmp; esp_err_t err = twai_transmit(&msg, timeoutTicks);
UBaseType_t items = uxQueueMessagesWaiting(fila); if (err != ESP_OK) {
PrintTela("[CAN TX] Falha transmit (" + String((int)err) + ")");
for (UBaseType_t i = 0; i < items; i++) {
if (xQueuePeek(fila, &msgTmp, 0) == pdTRUE) {
if (msgTmp.identifier == novaMsg.identifier && msgTmp.data[0] == novaMsg.data[0] && msgTmp.data[1] == novaMsg.data[1]) {
return true; // já tem uma igual
}
}
}
return false; return false;
} }
return true;
}
static void HealthTaskWrapper(void *pvParameters) {
static_cast<CanService*>(pvParameters)->HealthTask(pvParameters);
}
bool reinit_twai_unsafe_() {
// ⚠️ Use os mesmos pinos/bitrate do begin(); ajuste se necessário
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_5, GPIO_NUM_4, TWAI_MODE_NORMAL);
g_config.tx_queue_len = 32;
g_config.rx_queue_len = 32;
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) return false;
if (twai_start() != ESP_OK) { twai_driver_uninstall(); return false; }
uint32_t alert_mask = 0;
alert_mask |= TWAI_ALERT_BUS_OFF;
alert_mask |= TWAI_ALERT_RX_QUEUE_FULL;
alert_mask |= TWAI_ALERT_TX_FAILED;
#if defined(TWAI_ALERT_PERIPH_ERR)
alert_mask |= TWAI_ALERT_PERIPH_ERR;
#elif defined(TWAI_ALERT_PERIPH_RESET)
alert_mask |= TWAI_ALERT_PERIPH_RESET;
#endif
#if defined(TWAI_ALERT_RECOVERY_COMPLETE)
alert_mask |= TWAI_ALERT_RECOVERY_COMPLETE;
#elif defined(TWAI_ALERT_RECOVERY_IN_PROGRESS)
alert_mask |= TWAI_ALERT_RECOVERY_IN_PROGRESS;
#endif
twai_reconfigure_alerts(alert_mask, nullptr);
return true;
}
void HealthTask(void* pvParameters) {
const uint32_t RX_TIMEOUT_MS = 3000; // ajuste conforme seu tráfego esperado
const uint32_t TX_STALL_MS = 2000; // quanto tempo uma chave pode ficar sem sair
const uint8_t MAX_REC_FAILS = 3; // depois disso, reboot
static uint8_t consecutive_rec_fails = 0;
for (;;) {
esp_task_wdt_reset();
uint64_t now = esp_timer_get_time();
// ---- Alerts do driver ----
uint32_t alerts = 0;
if (twai_read_alerts(&alerts, 0) == ESP_OK && alerts) {
if (alerts & TWAI_ALERT_BUS_OFF) {
PrintTela("[CAN ALERT] BUS OFF → iniciando recovery...");
if (twai_initiate_recovery() != ESP_OK) {
PrintTela("[CAN ALERT] Falha ao iniciar recovery; reiniciando driver...");
twai_stop(); twai_driver_uninstall();
if (!reinit_twai_unsafe_()) consecutive_rec_fails++;
else consecutive_rec_fails = 0;
}
}
#if defined(TWAI_ALERT_RECOVERY_COMPLETE)
if (alerts & TWAI_ALERT_RECOVERY_COMPLETE) {
PrintTela("[CAN ALERT] Recovery completo.");
consecutive_rec_fails = 0;
}
#elif defined(TWAI_ALERT_RECOVERY_IN_PROGRESS)
// Algumas versões só têm "em progresso"; trate como sinal de vida do recovery
if (alerts & TWAI_ALERT_RECOVERY_IN_PROGRESS) {
PrintTela("[CAN ALERT] Recovery em progresso (OK).");
// opcional: zere contador se quiser considerar como sinal saudável
consecutive_rec_fails = 0;
}
#endif
if (alerts & TWAI_ALERT_RX_QUEUE_FULL) {
PrintTela("[CAN ALERT] RX interno do driver CHEIO.");
}
if (alerts & TWAI_ALERT_TX_FAILED) {
PrintTela("[CAN ALERT] TX FAILED.");
}
#if defined(TWAI_ALERT_PERIPH_ERR)
if (alerts & TWAI_ALERT_PERIPH_ERR) {
PrintTela("[CAN ALERT] ERRO no periférico CAN. Reiniciando driver...");
twai_stop(); twai_driver_uninstall();
if (!reinit_twai_unsafe_()) consecutive_rec_fails++;
else consecutive_rec_fails = 0;
}
#elif defined(TWAI_ALERT_PERIPH_RESET)
if (alerts & TWAI_ALERT_PERIPH_RESET) {
PrintTela("[CAN ALERT] RESET do periférico CAN detectado. Reconfigurando...");
twai_stop(); twai_driver_uninstall();
if (!reinit_twai_unsafe_()) consecutive_rec_fails++;
else consecutive_rec_fails = 0;
}
#endif
}
// ---- Liveness de RX ----
if (last_rx_ts && ((now - last_rx_ts) / 1000ULL) > RX_TIMEOUT_MS) {
PrintTela("[CAN HLTH] Sem RX há " + String((now - last_rx_ts)/1000ULL) + " ms → reiniciando driver...");
twai_stop(); twai_driver_uninstall();
if (!reinit_twai_unsafe_()) consecutive_rec_fails++;
else consecutive_rec_fails = 0;
last_rx_ts = esp_timer_get_time(); // evita loop de reinicialização
}
// ---- Stall de TX (chaves pendentes) ----
// Se houver muitas chaves esperando por muito tempo, force drenagem
UBaseType_t pendentes = keysQ ? uxQueueMessagesWaiting(keysQ) : 0;
if (pendentes > 0 && last_tx_ts && ((now - last_tx_ts) / 1000ULL) > TX_STALL_MS) {
PrintTela("[CAN HLTH] TX aparentemente parado (" + String(pendentes) + " chaves pendentes).");
// Estratégia: deixa o CanTaskTx re-enfileirar; se persistir, reinicia driver
static uint8_t tx_stall_count = 0;
tx_stall_count++;
if (tx_stall_count >= 3) {
PrintTela("[CAN HLTH] TX stall persistente → reiniciando driver...");
twai_stop(); twai_driver_uninstall();
if (!reinit_twai_unsafe_()) consecutive_rec_fails++;
else { consecutive_rec_fails = 0; tx_stall_count = 0; }
last_tx_ts = esp_timer_get_time();
}
}
// ---- Escalonamento final: reboot do chip se não recuperar ----
if (consecutive_rec_fails >= MAX_REC_FAILS) {
PrintTela("[CAN HLTH] Falhas consecutivas ao reiniciar TWAI. Reiniciando ESP32...");
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void setReceiveCallback(OnReceiveCallback cb) { void setReceiveCallback(OnReceiveCallback cb) {
@ -358,44 +468,38 @@ class CanService {
} }
bool adicionarMensagemFila(const std::vector<uint8_t>& payload) { bool adicionarMensagemFila(const std::vector<uint8_t>& payload) {
if (payload.size() > 8) return false; // 1) Sanidade
if (payload.empty() || payload.size() > 8) {
PrintTela("[CAN] Payload inválido (0 ou >8 bytes).");
return false;
}
if (payload.size() < 2) { // precisa de pos (byte0) e addr (byte1) para formar a chave
PrintTela("[CAN] Payload precisa ter pelo menos pos e addr (2 bytes).");
return false;
}
twai_message_t msg; // 2) Monta frame
msg.identifier = _nodeId; // + 1; twai_message_t msg{};
msg.extd = 0; // ⚠️ ID: use o ID que o PC espera para respostas desse nó (ajuste se necessário)
msg.rtr = 0; msg.identifier = _nodeId; // ou outro mapeamento do seu protocolo
msg.extd = 0; // standard frame
msg.rtr = 0; // data frame
msg.ss = 0; msg.ss = 0;
msg.data_length_code = payload.size(); msg.data_length_code = payload.size();
for (uint8_t i = 0; i < payload.size(); ++i) { for (uint8_t i = 0; i < payload.size(); ++i) {
msg.data[i] = payload[i]; // 👈 Preenche os dados corretamente msg.data[i] = payload[i];
} }
if (!mensagemJaNaFila(canQueueTx, msg)) { // 3) Publica via coalescência (substitui a antiga fila TX)
if (xQueueSend(canQueueTx, &msg, 0) != pdTRUE) { PublicarTx(msg);
PrintTela("[CAN] Fila TX cheia! Mensagem descartada.");
return false;
}
else {
//PrintTela("[CAN] Mensagem adicionada na fila TX");
return true; return true;
} }
}
else {
PrintTela("[CAN] Mensagem duplicada ignorada.");
}
}
bool enviarDadosCan(twai_message_t msg) {
return twai_transmit(&msg, pdMS_TO_TICKS(10)) == ESP_OK;
}
F_Code FuncaoPorPosicao(CanMessagePosicaoDados posicao) { F_Code FuncaoPorPosicao(CanMessagePosicaoDados posicao) {
F_Code funcao = F_Code::Nda; F_Code funcao = F_Code::Nda;
int pos = static_cast<int>(posicao); int pos = static_cast<int>(posicao);
if (pos >= 0 && pos <= 50) { if (pos >= 0 && pos <= 50) {
funcao = F_Code::ReqTx; funcao = F_Code::ReqTx;
} }
@ -405,9 +509,7 @@ class CanService {
else if (pos > 100 && pos <= 150) { else if (pos > 100 && pos <= 150) {
funcao = F_Code::CmdTx; funcao = F_Code::CmdTx;
} }
//PrintTela("Funcao parseada para posicao: " + String(pos) + ", funcao: " + String(funcao)); //PrintTela("Funcao parseada para posicao: " + String(pos) + ", funcao: " + String(funcao));
return funcao; return funcao;
} }
@ -417,7 +519,7 @@ class CanService {
data.push_back(ID_Num_sMOD); data.push_back(ID_Num_sMOD);
data.push_back(static_cast<uint8_t>(D_Code)); data.push_back(static_cast<uint8_t>(D_Code));
data.push_back(Conectado ? 1 : 0); data.push_back(Conectado ? 1 : 0);
data.push_back(Versao); data.push_back(static_cast<uint8_t>(Versao));
return data; return data;
} }
@ -430,45 +532,11 @@ class CanService {
} }
private:
uint32_t _baudRate = 500000;
static void TaskCallbackWrapper(void* param) { uint8_t _nodeId = 0;
auto bundle = static_cast<std::pair<CallbackPayload*, TaskHandle_t>*>(param);
CallbackPayload* payload = bundle->first;
TaskHandle_t notifyTo = bundle->second;
payload->callback(
payload->packetSize,
payload->senderId,
payload->posicao,
payload->data,
payload->dataLength
);
PrintTela("[CAN Callback] Processamento finalizado: " + String(payload->idMsg));
// Notifica a task principal que terminou
xTaskNotifyGive(notifyTo);
// Limpa memória
delete payload;
delete bundle;
// Encerra a task
vTaskDelete(NULL);
}
private:
uint8_t _nodeId;
uint32_t _baudRate;
OnReceiveCallback _callback = nullptr; OnReceiveCallback _callback = nullptr;
bool DebugMode = false; bool DebugMode = true;
volatile bool processamentoEmAndamento = false;
volatile long idmsg = 0;
}; };
TaskHandle_t CanService::handleCallbackTask = NULL; #endif // CANSERVICE_H
#endif

View File

@ -0,0 +1,474 @@
#include "SerialService.h"
// CanService.h
#ifndef CANSERVICE_H
#define CANSERVICE_H
/*
#include <CAN.h>
#include <Arduino.h>
class CanService {
public:
typedef void (*OnReceiveCallback)(int packetSize, int senderId, byte funcCode, byte* data, int dataLength);
CanService(uint8_t nodeId, uint32_t baudRate = 500E3) {
_baudRate = baudRate;
_nodeId = nodeId;
}
void begin() {
if (!CAN.begin(_baudRate)) {
PrintTela("[CAN] Falha ao iniciar");
return;
}
PrintTela("[CAN] Inicializado com sucesso");
CAN.onReceive(onReceiveWrapper);
instance = this;
}
void loop() {
// Apenas processa mensagens recebidas via interrupcao
}
void setReceiveCallback(OnReceiveCallback cb) {
_callback = cb;
}
bool enviarMensagem(byte funcCode, const std::vector<uint8_t>& payload) {
if (payload.size() > 7) return false;
CAN.beginPacket(_nodeId);
CAN.write(funcCode);
for (uint8_t b : payload) {
CAN.write(b);
}
return CAN.endPacket();
}
std::vector<uint8_t> MontarFrameChk(T_Code D_Code, bool Conectado, int Versao) {
std::vector<uint8_t> resposta;
resposta.push_back(static_cast<uint8_t>(D_Code)); // Tipo do módulo
resposta.push_back(Conectado ? 1 : 0); // Conectado
resposta.push_back(Versao); // Versão
return resposta;
}
private:
uint32_t _baudRate;
uint8_t _nodeId;
OnReceiveCallback _callback = nullptr;
static CanService* instance;
static void onReceiveWrapper(int packetSize) {
if (instance) instance->onReceive(packetSize);
}
void onReceive(int packetSize) {
int senderId = CAN.packetId();
byte buffer[8];
int i = 0;
while (CAN.available() && i < 8) {
buffer[i++] = CAN.read();
}
byte funcCode = buffer[0];
if (_callback != nullptr) {
_callback(packetSize, senderId, funcCode, buffer + 1, i - 1);
}
}
};
CanService* CanService::instance = nullptr;
*/
#include <Arduino.h>
#include <driver/twai.h>
class CanService {
typedef void (*OnReceiveCallback)(int packetSize, int senderId, CanMessagePosicaoDados posicao, byte* data, int dataLength);
typedef struct {
OnReceiveCallback callback;
int packetSize;
int senderId;
CanMessagePosicaoDados posicao;
byte data[7];
int dataLength;
long idMsg;
} CallbackPayload;
static TaskHandle_t handleCallbackTask;
public:
uint8_t ID_Num_sMOD = 0;
uint8_t ID_Num_sTOD = 250;
uint8_t ID_Num_sLRA = 251;
CanService(uint8_t nodeId, uint32_t baudRate = 500000) {
_nodeId = nodeId;
_baudRate = baudRate;
}
void begin() {
PrintTela("[CAN] Iniciando TWAI...");
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_5, GPIO_NUM_4, TWAI_MODE_NORMAL); // modo sem ACK
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t f_config = {
.acceptance_code = (_nodeId << 21), // Shiftado para alinhar com o padrão TWAI
.acceptance_mask = ~(0x7FF << 21), // Mascara para filtrar apenas esse ID
.single_filter = true
};
esp_err_t err;
err = twai_driver_install(&g_config, &t_config, &f_config);
if (err != ESP_OK) {
PrintTela("[CAN] Erro ao instalar driver: ", false);
PrintTela(String(err));
return;
}
err = twai_start();
if (err != ESP_OK) {
PrintTela("[CAN] Erro ao iniciar TWAI: ", false);
PrintTela(String(err));
return;
}
if (CanTaskRxHandle == NULL) {
canQueueRx = xQueueCreate(50, sizeof(twai_message_t));
if (canQueueRx == NULL) {
PrintTela("[CAN] Falha ao criar fila RX!");
}
xTaskCreatePinnedToCore(CanService::CanTaskRxWrapper, "CanTaskRx", 4096, this, 17, &CanTaskRxHandle, APP_CPU_NUM);
}
if (CanTaskTxHandle == NULL) {
canQueueTx = xQueueCreate(50, sizeof(twai_message_t));
if (canQueueTx == NULL) {
PrintTela("[CAN] Falha ao criar fila TX!");
}
xTaskCreatePinnedToCore(CanService::CanTaskTxWrapper, "CanTaskTx", 4096, this, 16, &CanTaskTxHandle, APP_CPU_NUM);
}
if (CanTaskProcessHandle == NULL) {
xTaskCreatePinnedToCore(CanService::CanTaskProcessWrapper, "CanTaskProcess", 6144, this, 18, &CanTaskProcessHandle, APP_CPU_NUM);
}
PrintTela("[CAN] TWAI iniciado com sucesso");
}
QueueHandle_t canQueueRx;
TaskHandle_t CanTaskRxHandle = NULL;
static void CanTaskRxWrapper(void *pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
service->CanTaskRx(pvParameters);
}
void CanTaskRx(void* pvParameters) {
twai_message_t message;
while (true) {
if (twai_receive(&message, pdMS_TO_TICKS(10)) == ESP_OK) {
if (message.data_length_code == 0 || message.extd) continue;
bool MensagemTx = message.identifier == static_cast<uint32_t>(_nodeId + 1);
if (DebugMode) {
PrintTela("[CAN ", false);
PrintTela(MensagemTx ? "TX" : "RX", false);
PrintTela("] ID: 0x", false);
PrintTela(String(message.identifier, HEX), false);
PrintTela(" Len: ", false);
PrintTela(String(message.data_length_code), false);
PrintTela(" Data: ", false);
for (int i = 0; i < message.data_length_code; i++) {
PrintTela(String(message.data[i], HEX), false);
PrintTela(" ", false);
}
PrintTela("");
}
if (MensagemTx) {
PrintTela("[CAN RX] Ouviu a própria resposta!");
continue;
}
if (!mensagemJaNaFila(canQueueRx, message)) {
if (uxQueueSpacesAvailable(canQueueRx) < 5) {
PrintTela("[CAN RX] Alerta: Fila RX quase cheia (" + String(uxQueueSpacesAvailable(canQueueRx)) + " mensagens restantes)");
}
if (xQueueSend(canQueueRx, &message, 0) != pdTRUE) {
PrintTela("[CAN RX] Fila RX cheia! Mensagem descartada.");
}
else {
//PrintTela("[CAN RX] Mensagem adicionada na fila RX");
}
} else {
PrintTela("[CAN RX] Mensagem duplicada ignorada.");
}
}
vTaskDelay(1);
}
}
QueueHandle_t canQueueTx;
TaskHandle_t CanTaskTxHandle = NULL;
static void CanTaskTxWrapper(void *pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
service->CanTaskTx(pvParameters);
}
void CanTaskTx(void* pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
twai_message_t msg;
while (true) {
if (xQueueReceive(service->canQueueTx, &msg, portMAX_DELAY) == pdTRUE) {
//PrintTela("[CAN TX] Mensagem recebida da fila TX");
if (service->enviarDadosCan(msg)) {
//PrintTela("[CAN TX] Mensagem enviada via CAN com sucesso");
}
else {
PrintTela("[CAN TX] Erro ao enviar mensagem via CAN");
}
}
vTaskDelay(1);
}
}
TaskHandle_t CanTaskProcessHandle = NULL;
static void CanTaskProcessWrapper(void *pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
service->CanTaskProcess(pvParameters);
}
void CanTaskProcess(void* pvParameters) {
CanService *service = static_cast<CanService*>(pvParameters);
twai_message_t msg;
while (true) {
while (uxQueueMessagesWaiting(service->canQueueRx) > 0) {
if (xQueueReceive(service->canQueueRx, &msg, portMAX_DELAY) == pdTRUE) {
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)msg.data[0];
if (service->_callback) {
String frameEmProcessamento = "";
for (int i = 0; i < msg.data_length_code; i++) {
frameEmProcessamento += String(msg.data[i], HEX) + " ";
}
idmsg++;
CallbackPayload* payload = new CallbackPayload();
payload->idMsg = idmsg;
payload->callback = service->_callback;
payload->packetSize = msg.data_length_code;
payload->senderId = msg.identifier;
payload->posicao = posicao;
memcpy(payload->data, &msg.data[1], msg.data_length_code - 1);
payload->dataLength = msg.data_length_code - 1;
// Captura o handle da task atual (quem vai esperar a notificação)
TaskHandle_t callingTaskHandle = xTaskGetCurrentTaskHandle();
// Cria um pacote com o payload + handle do notificador
auto* bundle = new std::pair<CallbackPayload*, TaskHandle_t>(payload, callingTaskHandle);
PrintTela("[CAN Process] Processando frame: " + frameEmProcessamento);
String taskName = "TaskCallbackTimeout_" + String(idmsg);
TaskHandle_t handleTask = nullptr;
// Cria a task do callback
BaseType_t status = xTaskCreatePinnedToCore(
TaskCallbackWrapper,
taskName.c_str(),
8192,
bundle,
5,
&handleTask,
APP_CPU_NUM
);
if (status != pdPASS) {
PrintTela("[CAN Process] ❌ Falha ao criar task de callback!");
delete payload;
delete bundle;
continue;
}
if (false && msg.data[1] == ID_Num_sLRA) {
// Comandos para o LoRa nao precisam aguardar, podem ser processados sem tempo
}
else {
// Aguarda até 5000ms pela notificação de término
BaseType_t sinal = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000));
if (sinal == 0) {
PrintTela("[CAN Process] ⚠️ Callback demorou demais: " + String(payload->idMsg) + " — timeout ao executar comando: " + frameEmProcessamento);
vTaskDelay(pdMS_TO_TICKS(10)); // dá tempo da task morrer sozinha
eTaskState estado = eTaskGetState(handleTask);
if (estado != eDeleted && estado != eInvalid) {
vTaskDelete(handleTask);
}
}
}
}
}
}
vTaskDelay(1);
}
}
bool mensagemJaNaFila(QueueHandle_t fila, const twai_message_t& novaMsg) {
twai_message_t msgTmp;
UBaseType_t items = uxQueueMessagesWaiting(fila);
for (UBaseType_t i = 0; i < items; i++) {
if (xQueuePeek(fila, &msgTmp, 0) == pdTRUE) {
if (msgTmp.identifier == novaMsg.identifier && msgTmp.data[0] == novaMsg.data[0] && msgTmp.data[1] == novaMsg.data[1]) {
return true; // já tem uma igual
}
}
}
return false;
}
void setReceiveCallback(OnReceiveCallback cb) {
_callback = cb;
}
bool adicionarMensagemFila(const std::vector<uint8_t>& payload) {
if (payload.size() > 8) return false;
twai_message_t msg;
msg.identifier = _nodeId; // + 1;
msg.extd = 0;
msg.rtr = 0;
msg.ss = 0;
msg.data_length_code = payload.size();
for (uint8_t i = 0; i < payload.size(); ++i) {
msg.data[i] = payload[i]; // 👈 Preenche os dados corretamente
}
if (!mensagemJaNaFila(canQueueTx, msg)) {
if (xQueueSend(canQueueTx, &msg, 0) != pdTRUE) {
PrintTela("[CAN] Fila TX cheia! Mensagem descartada.");
return false;
}
else {
//PrintTela("[CAN] Mensagem adicionada na fila TX");
return true;
}
}
else {
PrintTela("[CAN] Mensagem duplicada ignorada.");
}
}
bool enviarDadosCan(twai_message_t msg) {
return twai_transmit(&msg, pdMS_TO_TICKS(10)) == ESP_OK;
}
F_Code FuncaoPorPosicao(CanMessagePosicaoDados posicao) {
F_Code funcao = F_Code::Nda;
int pos = static_cast<int>(posicao);
if (pos >= 0 && pos <= 50) {
funcao = F_Code::ReqTx;
}
else if (pos > 50 && pos <= 100) {
funcao = F_Code::CfgTx;
}
else if (pos > 100 && pos <= 150) {
funcao = F_Code::CmdTx;
}
//PrintTela("Funcao parseada para posicao: " + String(pos) + ", funcao: " + String(funcao));
return funcao;
}
std::vector<uint8_t> MontarFrameReqStatusMod(T_Code D_Code, bool Conectado, int Versao) {
std::vector<uint8_t> data;
data.push_back(static_cast<uint8_t>(CanMessagePosicaoDados::Status));
data.push_back(ID_Num_sMOD);
data.push_back(static_cast<uint8_t>(D_Code));
data.push_back(Conectado ? 1 : 0);
data.push_back(Versao);
return data;
}
std::vector<uint8_t> MontarFrameReqDadosFim(int latencia) {
std::vector<uint8_t> data;
data.push_back(static_cast<uint8_t>(CanMessagePosicaoDados::DadosAll));
data.push_back(ID_Num_sTOD);
data.push_back(latencia >> 8); data.push_back(latencia & 0xFF);
return data;
}
static void TaskCallbackWrapper(void* param) {
auto bundle = static_cast<std::pair<CallbackPayload*, TaskHandle_t>*>(param);
CallbackPayload* payload = bundle->first;
TaskHandle_t notifyTo = bundle->second;
payload->callback(
payload->packetSize,
payload->senderId,
payload->posicao,
payload->data,
payload->dataLength
);
PrintTela("[CAN Callback] Processamento finalizado: " + String(payload->idMsg));
// Notifica a task principal que terminou
xTaskNotifyGive(notifyTo);
// Limpa memória
delete payload;
delete bundle;
// Encerra a task
vTaskDelete(NULL);
}
private:
uint8_t _nodeId;
uint32_t _baudRate;
OnReceiveCallback _callback = nullptr;
bool DebugMode = false;
volatile bool processamentoEmAndamento = false;
volatile long idmsg = 0;
};
TaskHandle_t CanService::handleCallbackTask = NULL;
#endif

View File

@ -22,14 +22,13 @@ class SensorMassa : public ComponenteCAN {
PinoModel _pinoDT; PinoModel _pinoDT;
PinoModel _pinoSCK; PinoModel _pinoSCK;
float _Offset = 1895800.0; long _Offset = 1996283;
float _MassaRef = 0.8; float _Escala = 50857.316406;
float _MassaLeituraRef = 40876.37; float _OffsetMassa = 0.0;
HX711 balanca = HX711(); HX711 balanca = HX711();
SimpleKalmanFilter filtro = SimpleKalmanFilter(2, 2, 0.01); SimpleKalmanFilter filtro = SimpleKalmanFilter(0.05, 0.05, 0.25);
double leituraMassa = 0;
float Massa = 0; float Massa = 0;
bool Iniciado = false; bool Iniciado = false;
@ -42,7 +41,8 @@ class SensorMassa : public ComponenteCAN {
if (_pinoDT.Definido() && _pinoSCK.Definido()) { if (_pinoDT.Definido() && _pinoSCK.Definido()) {
balanca.begin(_pinoDT.num, _pinoSCK.num); balanca.begin(_pinoDT.num, _pinoSCK.num);
balanca.set_scale(); balanca.set_offset(_Offset);
balanca.set_scale(_Escala);
//balanca->tare(); //balanca->tare();
Iniciado = true; Iniciado = true;
@ -63,6 +63,7 @@ class SensorMassa : public ComponenteCAN {
// Redefinir as configurações para os valores iniciais // Redefinir as configurações para os valores iniciais
_pinoDT.Desconectar(); _pinoDT.Desconectar();
_pinoSCK.Desconectar(); _pinoSCK.Desconectar();
Massa = 0;
PrintTela(_ID + " Desligado"); PrintTela(_ID + " Desligado");
@ -119,7 +120,7 @@ class SensorMassa : public ComponenteCAN {
sensor->ID_Num = idNum; sensor->ID_Num = idNum;
sensor->_pinoDT = PinoModel(pinoDt, CONTROLADOR, _INPUT, DIGITAL); sensor->_pinoDT = PinoModel(pinoDt, CONTROLADOR, _INPUT, DIGITAL);
sensor->_pinoSCK = PinoModel(pinoSck, CONTROLADOR, _OUTPUT); sensor->_pinoSCK = PinoModel(pinoSck, CONTROLADOR, _OUTPUT);
sensor->_Offset = offset; sensor->_OffsetMassa = offset;
sensor->Inicializar(); sensor->Inicializar();
if (!jaExiste) { if (!jaExiste) {
lista.push_back(sensor); lista.push_back(sensor);
@ -146,22 +147,12 @@ class SensorMassa : public ComponenteCAN {
private: private:
void AferirMassa() { void AferirMassa() {
if (Iniciado) { if (Iniciado) {
float leitura = balanca.get_units(5); float leitura = -balanca.get_units(5);
leituraMassa = filtro.updateEstimate(leitura); leitura += _OffsetMassa;
Massa = CalcularMassa(leituraMassa); Massa = filtro.updateEstimate(leitura);
} }
} }
float CalcularMassa(float _leitura) {
double leituraMassa = _leitura * -1;
leituraMassa += _Offset;
float FatorLeitura = _MassaRef / _MassaLeituraRef;
float massa = FatorLeitura * leituraMassa;
return massa;
}
}; };

View File

@ -11,9 +11,9 @@ RESOLUCAO = config["resolucao"]
pasta_origem = os.path.join(MODELO, "dataset", f"{RESOLUCAO[0]}x{RESOLUCAO[1]}") pasta_origem = os.path.join(MODELO, "dataset", f"{RESOLUCAO[0]}x{RESOLUCAO[1]}")
pasta_destino = os.path.join(MODELO, "dataset", "split") pasta_destino = os.path.join(MODELO, "dataset", "split")
percent_train = 0.9 percent_train = 0.8
percent_val = 0.09 percent_val = 0.18
percent_test = 0.01 percent_test = 0.02
seed = 42 seed = 42
random.seed(seed) random.seed(seed)

View File

@ -1,11 +1,11 @@
{ {
"camera": "oak-d", "camera": "oak-1",
"modelo": "fast_scnn", "modelo": "fast_scnn",
"model_name": "ruas_new", "model_name": "ervas_medium_new",
"main_class_name": "cana", "main_class_name": "erva",
"use_main_class": false, "use_main_class": false,
"resolucao": [512, 288], "resolucao": [512, 288],
"roi_inicio": 0.0, "roi_inicio": 0.0,
"roi_tamanho": 1.0, "roi_tamanho": 1.0,
"shaves": 3 "shaves": 6
} }