ajuste no canservice da firmware
This commit is contained in:
parent
cad0bfaabd
commit
bde1ec5064
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -768,16 +768,18 @@ namespace AgroBase.Forms.IHM
|
|||
idx = PreencheDadosLinha(grid, Testes, idx, CorrenteMotor);
|
||||
|
||||
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);
|
||||
bool AtivarFreio = SenIniciado && ServoFreio != null ? await EnviarComandoMovFreio(Variaveis.OperacaoEmAndamento.DispSen, ServoFreio, angAtivar, Testes[idx], 2000) : false;
|
||||
Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov).UltimaDirecao = Direcao.EmFreio;
|
||||
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);
|
||||
|
||||
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 TensaoFreio = SensorAB7V2 != null ? SensorAB7V2.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Tensao).atual.valor : -1;
|
||||
|
||||
(bool desatuar, int angDesativar) = Modulo.MovMotor.ConfigFreio.AtualizarDadosFreio(false);
|
||||
bool DesativarFreio = SenIniciado && ServoFreio != null ? await EnviarComandoMovFreio(Variaveis.OperacaoEmAndamento.DispSen, ServoFreio, angDesativar, Testes[idx], 2000) : false;
|
||||
Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov).UltimaDirecao = Direcao.Parado;
|
||||
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, TensaoFreio);
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ namespace AgroBase.Forms.IHM
|
|||
txtConcluidoEm.Text = _Sensoriamento.DataFim == DateTime.MinValue ? "-" : _Sensoriamento.DataFim.ToString("dd/MM HH:mm:ss");
|
||||
txtDistanciaPercorrida.Text = _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m";
|
||||
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";
|
||||
txtBateriaConsumida.Text = _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + " %";
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
|
||||
private async Task tmrLeitura_Tick()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Text = Variaveis.OperacaoEmAndamento.TextoFormOperacao("Manual");
|
||||
|
||||
|
|
@ -176,6 +178,12 @@ namespace AgroBase.Forms.Operacoes
|
|||
if (!ReqFrameSnr)
|
||||
_ = AtualizarCameraCaminho();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task AtualizarCameraSolo()
|
||||
{
|
||||
|
|
@ -432,6 +440,8 @@ namespace AgroBase.Forms.Operacoes
|
|||
#region Informacoes gerais
|
||||
|
||||
private void PopularInformacoesGerais()
|
||||
{
|
||||
try
|
||||
{
|
||||
gridDados.Rows.Clear();
|
||||
gridDados.Columns.Clear();
|
||||
|
|
@ -480,6 +490,9 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
AtualizarInformacoesGerais();
|
||||
}
|
||||
catch { }
|
||||
|
||||
}
|
||||
|
||||
private void AtualizarInformacoesGerais()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
lblDistanciaPercorrida.Text = "Distancia Percorrida: " + _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m";
|
||||
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";
|
||||
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";
|
||||
lblHerbicidaPorErva.Text = "Herbicida por Erva: " + _Sensoriamento.Atuador.HerbicidaPorErva.ToString("0.000") + " mL";
|
||||
lblBateriaConsumida.Text = "Bateria Consumida: " + _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + "%";
|
||||
|
|
|
|||
|
|
@ -741,7 +741,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
});
|
||||
|
||||
// Obtemos todos os tipos de ervas identificadas ao longo dos logs
|
||||
var todasErvas = LogsOperacao
|
||||
/*var todasErvas = LogsOperacao
|
||||
.SelectMany(log => log.Atuador.ErvasIdentificadas)
|
||||
.SelectMany(bico => bico.Keys)
|
||||
.Distinct()
|
||||
|
|
@ -795,7 +795,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
)
|
||||
{
|
||||
Titulo = "Ervas Identificadas"
|
||||
};
|
||||
};*/
|
||||
|
||||
List<GraficoInterativoSerieModel> seriesCamerasSolo = new List<GraficoInterativoSerieModel>();
|
||||
foreach (var camSolo in LogsCam_Solo)
|
||||
|
|
@ -1013,7 +1013,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
double scaleY = (double)bitmapComObjetos.Height / frame.height;
|
||||
|
||||
// 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
|
||||
int xAtual = (int)(objeto.bbox[0] * scaleX);
|
||||
|
|
@ -1027,7 +1027,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
// Desenhar a descrição do objeto (id: descricao)
|
||||
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));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// Retornar o bitmap modificado
|
||||
|
|
@ -1069,7 +1069,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
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);
|
||||
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));
|
||||
|
||||
lblPercentualBateria.Text = $"Bateria {Log.Bateria.PorcentagemBateria.ToString("0.00")}%";
|
||||
|
|
@ -1444,9 +1444,9 @@ namespace AgroBase.Forms.Operacoes
|
|||
private void AtualizarInformacoesSonar()
|
||||
{
|
||||
var Log = LogsOperacao[idxMomentoAtual].OperadorVisual.Analises;
|
||||
txtSonarDistanciaObstaculo.Text = Log.matriz_confianca.block.d_obs_true_min_m.ToString("0.00");
|
||||
txtSonarDistanciaParada.Text = Log.matriz_confianca.block.decision.dist_necessaria.ToString("0.00");
|
||||
txtSonarDecisao.Text = Log.matriz_confianca.block.reason_detail;
|
||||
txtSonarDistanciaObstaculo.Text = (Log.matriz_confianca?.block?.d_obs_true_min_m ?? 0).ToString("0.00");
|
||||
txtSonarDistanciaParada.Text = (Log.matriz_confianca?.block?.decision?.dist_necessaria ?? 0).ToString("0.00");
|
||||
txtSonarDecisao.Text = (Log.matriz_confianca?.block?.reason_detail ?? "");
|
||||
|
||||
if (gridObstaculos.Columns.Count == 0)
|
||||
{
|
||||
|
|
@ -1458,7 +1458,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
gridObstaculos.Rows.Clear();
|
||||
|
||||
if (Log != null)
|
||||
if (Log?.deteccao != null)
|
||||
{
|
||||
foreach (var obstaculo in Log.deteccao)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace AgroBase.Models.Modules
|
|||
FuncoesPinout.SCL,
|
||||
};
|
||||
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<AtuadorBombaModel> BombasPressurizadoras { get; set; } = new List<AtuadorBombaModel>();
|
||||
public List<SensorModel> Sensores { get; set; } = new List<SensorModel>();
|
||||
|
|
@ -1330,7 +1330,7 @@ namespace AgroBase.Models.Modules
|
|||
bool conectado = data[3] == 1;
|
||||
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.Dispositivo = tipoModulo;
|
||||
|
|
@ -1348,7 +1348,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
double latenciaLoop = ConverterByteParaDouble(data, 2) * 100;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ namespace AgroBase.Models.Modules
|
|||
public List<MvdMotorMOVSensorimanetoGrafico> LogGrafico { get; set; } = new List<MvdMotorMOVSensorimanetoGrafico>();
|
||||
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)
|
||||
|
|
@ -279,9 +279,9 @@ namespace AgroBase.Models.Modules
|
|||
FuncoesPinout.PWM,
|
||||
FuncoesPinout.BRK,
|
||||
},
|
||||
ConfigFreio = new MvdServoFreioModel()
|
||||
ConfigFreio = new MvdServoFreioModel(Mod_ID)
|
||||
{
|
||||
AnguloInicial = 10,
|
||||
AnguloInicial = Mod_ID.Contains("E") ? 10 : 170,
|
||||
Incremental = Mod_ID.Contains("E"),
|
||||
Leitura = false,
|
||||
},
|
||||
|
|
@ -291,70 +291,77 @@ namespace AgroBase.Models.Modules
|
|||
TipoMovimentoDirecional.RodasDianteiras,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
{
|
||||
TipoMovimentoDirecional.RodasTraseiras,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
{
|
||||
TipoMovimentoDirecional.MovimentoDiagonal,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
{
|
||||
TipoMovimentoDirecional.MovimentoArco,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
{
|
||||
TipoMovimentoDirecional.RotacionarNoEixo,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Sentido.Horario,
|
||||
Tras = Sentido.Antihorario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Sentido.Horario,
|
||||
Tras = Sentido.Antihorario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
{
|
||||
TipoMovimentoDirecional.MovimentoLateral,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Mod_ID.Contains("T") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("T") ? Sentido.Antihorario : Sentido.Horario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Mod_ID.Contains("T") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("T") ? Sentido.Antihorario : Sentido.Horario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
{
|
||||
TipoMovimentoDirecional.Diagnostico,
|
||||
new ConfiguracaoSentidoMotor()
|
||||
{
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario,
|
||||
Direita = Sentido.Parado,
|
||||
Esquerda = Sentido.Parado,
|
||||
Frente = Mod_ID.Contains("E") ? Sentido.Horario : Sentido.Antihorario,
|
||||
Tras = Mod_ID.Contains("E") ? Sentido.Antihorario : Sentido.Horario
|
||||
Parado = Sentido.Parado,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -367,7 +374,6 @@ namespace AgroBase.Models.Modules
|
|||
var simulando = Variaveis.OperacaoEmAndamento.Simulando || Variaveis.OperacaoEmAndamento.Treinando;
|
||||
|
||||
bool Freado = DirecaoAtual == Direcao.EmFreio;
|
||||
(bool AtuarServo, int AnguloFreio) = ConfigFreio.AtualizarDadosFreio(Freado, 0, -1, 5);
|
||||
|
||||
if ((Comandar && Inicializado) || simulando)
|
||||
{
|
||||
|
|
@ -377,7 +383,6 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
DirecaoAtual = Direcao.Parado;
|
||||
Freado = false;
|
||||
(AtuarServo, AnguloFreio) = ConfigFreio.AtualizarDadosFreio(Freado, 0, -1, 5);
|
||||
}
|
||||
|
||||
RPM_SP_Controle = _Controle.RPM_Motor;
|
||||
|
|
@ -437,16 +442,6 @@ namespace AgroBase.Models.Modules
|
|||
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 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 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; }
|
||||
private bool _controle { get; set; }
|
||||
public bool Controle
|
||||
public int AnguloControle { get; set; } = -1;
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
bool mudou = _controle != status;
|
||||
_controle = status;
|
||||
RPM_SP = rpm_sp;
|
||||
private double VelocidadeAtual =>
|
||||
FuncoesMatematicas.CalculaVelocidadePercentualMs(Variaveis.OperacaoEmAndamento.Sensoriamento.Movimentacao?.VelocidadeMedia ?? 0);
|
||||
|
||||
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
|
||||
return (mudou, AnguloControle);
|
||||
get
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (AnguloControle == -1)
|
||||
var erro = Math.Abs(sp - atual);
|
||||
if (VelOkMemo)
|
||||
{
|
||||
AnguloControle = AnguloControleMin;
|
||||
}
|
||||
|
||||
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
|
||||
// estava OK → só sai se passar da banda externa
|
||||
if (erro > TolVelOut) VelOkMemo = false;
|
||||
}
|
||||
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
|
||||
int limiteMax = AnguloInicial + (Incremental ? AnguloControleMax : -AnguloControleMax);
|
||||
if (Incremental)
|
||||
// condição “atingiu SP” com tolerância angular
|
||||
private bool AtingiuSPComTol(int? lido, int alvo)
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
|
|
@ -591,7 +647,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
var ctrl = Variaveis.OperacaoEmAndamento?.Controle;
|
||||
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);
|
||||
return;
|
||||
|
|
@ -755,13 +811,6 @@ namespace AgroBase.Models.Modules
|
|||
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
|
||||
fFL = Math.Min(fatorMaximo, fFL);
|
||||
fFR = Math.Min(fatorMaximo, fFR);
|
||||
|
|
|
|||
|
|
@ -417,6 +417,19 @@ namespace AgroBase.Models.Modules
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2466,7 +2466,7 @@ namespace AgroBase.Models.Modules
|
|||
|
||||
// FLOAT com ou sem ponto
|
||||
float f = float.Parse(valor, CultureInfo.InvariantCulture);
|
||||
ushort final = (ushort)(Escalar ? f * 100 : f);
|
||||
short final = (short)(Escalar ? f * 100 : f);
|
||||
|
||||
if (QtdBytes == 1)
|
||||
return new byte[] { (byte)(final & 0xFF) };
|
||||
|
|
|
|||
|
|
@ -1019,6 +1019,10 @@ namespace AgroBase.Models
|
|||
}
|
||||
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++)
|
||||
{
|
||||
if (ForcarEnvio || _Controle.BicosAtuados[i].ComandoAtuar != _ControleAnterior.BicosAtuados[i].ComandoAtuar)
|
||||
|
|
@ -1677,6 +1681,7 @@ namespace AgroBase.Models
|
|||
Variaveis.OperacaoEmAndamento.DadosPerformance.AtualizarDadosPerformance();
|
||||
|
||||
MovimentacaoMovimentoCompensadoModel.AtualizarDados();
|
||||
Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.AtualizarDadosFreio();
|
||||
|
||||
// Soma total de atuações para todas as ervas de todos os bicos
|
||||
int totalAtuacoes = 0;
|
||||
|
|
@ -1702,8 +1707,7 @@ namespace AgroBase.Models
|
|||
HerbicidaConsumido = _DispAtu.Dados.VolumeVazaoML,
|
||||
PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno,
|
||||
HerbicidaPorErva = HerbicidaPorErva,
|
||||
ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.deteccoes?.Count ?? 0,
|
||||
ErvasIdentificadas = WeedWorkerService.DadosLeitura?.Analise?.ervas_identificadas ?? new List<Dictionary<string, int>>(),
|
||||
ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.ervas_no_radar ?? false,
|
||||
PercentualReservatorio = FuncoesMatematicas.Clamp(_DispAtu.Dados.PercentualReservatorio, 0, 100),
|
||||
VolumeReservatorio = _DispAtu.Dados.VolumeReservatorio,
|
||||
PressaoLinha = _DispAtu.Dados.PressaoLinha,
|
||||
|
|
@ -1880,7 +1884,7 @@ namespace AgroBase.Models
|
|||
public double HerbicidaConsumido { get; set; }
|
||||
public double PercentualErvasTerreno { get; set; }
|
||||
public double HerbicidaPorErva { get; set; }
|
||||
public int ErvasNoRadar { get; set; }
|
||||
public bool ErvasNoRadar { get; set; }
|
||||
public double PercentualReservatorio { get; set; }
|
||||
public double VolumeReservatorio { get; set; }
|
||||
public double PressaoLinha { get; set; }
|
||||
|
|
@ -1888,26 +1892,12 @@ namespace AgroBase.Models
|
|||
public double VazaoMedia { get; set; }
|
||||
public double LeituraPotenciaBomba { 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()
|
||||
{
|
||||
return new OperacaoSensoriamentoLogAtuModel()
|
||||
{
|
||||
QtdCamerasSolo = QtdCamerasSolo,
|
||||
ErvasIdentificadas = new List<Dictionary<string, int>>(ErvasIdentificadas),
|
||||
ErvasNoRadar = ErvasNoRadar,
|
||||
HerbicidaConsumido = HerbicidaConsumido,
|
||||
HerbicidaPorErva = HerbicidaPorErva,
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ namespace AgroBase.Models.Operadores
|
|||
Pronto = Pronto,
|
||||
ProntoEm = ProntoEm,
|
||||
CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>(CameraFrames ?? new Dictionary<CameraFrameType, OAKCameraFrameModel>()),
|
||||
Analises = Analises.Clone(),
|
||||
Imu = Imu.Clone(),
|
||||
Analises = Analises?.Clone(),
|
||||
Imu = Imu?.Clone(),
|
||||
UltimaMensagem = UltimaMensagem,
|
||||
};
|
||||
}
|
||||
|
|
@ -522,9 +522,9 @@ namespace AgroBase.Models.Operadores
|
|||
grid_w = grid_w,
|
||||
grid_h = grid_h,
|
||||
y_range_m = y_range_m,
|
||||
row_dist_m = new List<float>(row_dist_m),
|
||||
row_scale_x_m = new List<float>(row_scale_x_m),
|
||||
block = block.Clone()
|
||||
row_dist_m = new List<float>(row_dist_m ?? new List<float>()),
|
||||
row_scale_x_m = new List<float>(row_scale_x_m ?? new List<float>()),
|
||||
block = block?.Clone() ?? new VisualWorkerMessageMatrizConfiancaBlockModel()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static OpenTK.Graphics.OpenGL.GL;
|
||||
|
||||
namespace AgroBase.Models.Operadores
|
||||
{
|
||||
|
|
@ -41,23 +42,30 @@ namespace AgroBase.Models.Operadores
|
|||
|
||||
public class WeedWorkerAnaliseModel
|
||||
{
|
||||
public DateTime timestamp { get; set; }
|
||||
public int width { get; set; }
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get
|
||||
{
|
||||
return FuncoesGlobais.UnixToDateTime(timestamp.ToString());
|
||||
}
|
||||
}
|
||||
public double timestamp { 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 List<Dictionary<string, int>> ervas_identificadas { get; set; }
|
||||
public bool ervas_no_radar { get; set; }
|
||||
public WeedWorkerAnaliseEstatisticasModel estatisticas { get; set; }
|
||||
|
||||
public WeedWorkerAnaliseModel Clone()
|
||||
{
|
||||
return new WeedWorkerAnaliseModel()
|
||||
{
|
||||
timestamp = timestamp,
|
||||
deteccoes = new List<WeedWorkerAnaliseDeteccaoModel>(deteccoes ?? new List<WeedWorkerAnaliseDeteccaoModel>()),
|
||||
controle = new Dictionary<int, bool>(controle ?? new Dictionary<int, bool>()),
|
||||
ervas_identificadas = ervas_identificadas?.Select(dict => new Dictionary<string, int>(dict)).ToList(),
|
||||
height = height,
|
||||
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 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
|
||||
{
|
||||
ScriptCarregado = 1,
|
||||
|
|
|
|||
|
|
@ -1425,6 +1425,12 @@ namespace AgroBase.Models
|
|||
return Velocidade;
|
||||
}
|
||||
|
||||
public static double CalculaVelocidadePercentualMs(double vel_ms)
|
||||
{
|
||||
double Velocidade = vel_ms / CalculaVelocidadeMsPercentual(100);
|
||||
return Velocidade;
|
||||
}
|
||||
|
||||
public static double GrausParaRadianos(double angle)
|
||||
{
|
||||
return Math.PI / 180.0 * angle;
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ namespace AgroBase.Services
|
|||
|
||||
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}");
|
||||
|
|
@ -793,7 +793,7 @@ namespace AgroBase.Services
|
|||
|
||||
private static async Task AplicarCorrecaoRTK_Ntrip()
|
||||
{
|
||||
if (LoopRTK_Ntrip || !APIService.HasInternet)
|
||||
if (LoopRTK_Ntrip || !APIService.HasInternet || true)
|
||||
return;
|
||||
|
||||
LoopRTK_Ntrip = true;
|
||||
|
|
@ -853,8 +853,9 @@ namespace AgroBase.Services
|
|||
}
|
||||
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
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ namespace AgroBase.Services
|
|||
|
||||
if (!EnviarComando(comando))
|
||||
{
|
||||
porta.Close();
|
||||
_PortaLoRa.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -330,7 +330,8 @@ namespace AgroBase.Services
|
|||
_pulverizador.AddRange(CanPrefix(DadosLoRaParse.DadosPulverizador));
|
||||
_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.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.HerbicidaPorErva)); // 1 byte
|
||||
_pulverizador.Add((byte)Convert.ToInt32(sensoriamento.Atuador.PercentualErvasTerreno)); // 1 byte
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ namespace AgroBase.Services.Operadores
|
|||
("Gerais", new
|
||||
{
|
||||
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,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ namespace AgroBase.Services.Operadores
|
|||
public SaudeWorkerModel Saude { get; set; } = new SaudeWorkerModel();
|
||||
|
||||
private bool DebugMode = true;
|
||||
static long _lastTs = -1;
|
||||
|
||||
public void MostrarLog(string message, int limite = 500)
|
||||
{
|
||||
|
|
@ -172,70 +171,23 @@ namespace AgroBase.Services.Operadores
|
|||
var analise = root["analise"] as JObject;
|
||||
if (analise == null) return;
|
||||
|
||||
// 1) pega timestamp sem recriar dicionários
|
||||
long tsUnix = analise["timestamp"]?.Value<long>() ?? 0;
|
||||
if (tsUnix != 0)
|
||||
try
|
||||
{
|
||||
var ts = FuncoesGlobais.UnixToDateTime(tsUnix.ToString());
|
||||
DadosLeitura.Analise.timestamp = ts;
|
||||
DadosLeitura.Analise = JsonConvert.DeserializeObject<WeedWorkerAnaliseModel>(root["analise"].ToString());
|
||||
}
|
||||
|
||||
// 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)
|
||||
catch (Exception ex)
|
||||
{
|
||||
var lista = detToken.ToObject<List<WeedWorkerAnaliseDeteccaoModel>>();
|
||||
DadosLeitura.Analise.deteccoes = lista;
|
||||
Console.WriteLine($"Erro ao deserializar analise de ervas: {ex.Message}");
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
RedisService.AtualizarCampos(
|
||||
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.ervas_identificadas", new List<Dictionary<string, int>>())
|
||||
("analise.estatisticas", new WeedWorkerAnaliseEstatisticasModel())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
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;
|
||||
|
|
@ -55,22 +55,14 @@ namespace AgroBase.Services
|
|||
{
|
||||
try
|
||||
{
|
||||
if (Porta == null && (_PortaCAN?.IsOpen ?? false))
|
||||
{
|
||||
Porta = _PortaCAN;
|
||||
}
|
||||
|
||||
if (Porta == null && _PortaCAN == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
Porta.Close();
|
||||
}
|
||||
Porta?.Close();
|
||||
|
||||
if (_PortaCAN == null || !IsConnected)
|
||||
if ((Porta != null && _PortaCAN == null) || !IsConnected)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -208,7 +208,6 @@ namespace AgroBase.Services
|
|||
bool LoRa = await ProcurarDispositivoLoRa(_Porta);
|
||||
if (LoRa)
|
||||
{
|
||||
_Porta.Close();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -218,7 +217,6 @@ namespace AgroBase.Services
|
|||
bool GPSEncontrado = await ProcurarDispositivoGPS(_Porta);
|
||||
if (GPSEncontrado)
|
||||
{
|
||||
_Porta.Close();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +227,6 @@ namespace AgroBase.Services
|
|||
bool dispositivoCan = await ProcurarDispositivosCAN(_Porta);
|
||||
if (dispositivoCan)
|
||||
{
|
||||
_Porta.Close();
|
||||
AtualizarConsole("Barramento CAN encontrado");
|
||||
continue;
|
||||
}
|
||||
|
|
@ -253,6 +250,10 @@ namespace AgroBase.Services
|
|||
GPSService.PortaGPS?.Close();
|
||||
GPSService.PortaGPS = null;
|
||||
break;
|
||||
case T_Code.Lra:
|
||||
LoRaBaseService._PortaLoRa?.Close();
|
||||
LoRaBaseService._PortaLoRa = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -396,6 +397,11 @@ namespace AgroBase.Services
|
|||
if (!CanManager.CanService.Inicializar(Porta))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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");
|
||||
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");
|
||||
bool DispOid = await OIDCanService.VerificaDispositivoConectado();
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"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",
|
||||
"config_version": 0,
|
||||
"model_version": "0",
|
||||
|
|
@ -29,5 +22,5 @@
|
|||
"top_topics_and_observing_domains": [ ]
|
||||
} ],
|
||||
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
|
||||
"next_scheduled_calculation_time": "13400432611771339"
|
||||
"next_scheduled_calculation_time": "13400432611772238"
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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/18-17:01:38.592 14e0 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.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/22-10:57:48.753 3fc4 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/18-17:00:07.334 1a28 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.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/22-10:47:37.345 2a88 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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"}}}
|
||||
|
|
@ -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
|
|
@ -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/18-17:06:16.352 14e0 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.240 3fc4 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.242 3fc4 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/18-17:00:57.071 1a28 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.955 2a88 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.957 2a88 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/18-17:01:38.509 481c 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.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/22-10:57:48.676 c54 Recovering log #7
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/18-17:00:07.255 4f4c 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.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/22-10:47:37.265 3d50 Recovering log #7
|
||||
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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
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
|
|
@ -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]]}}]}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_67e0a7b51ff20c385c068ee0a8d7495e {
|
||||
#map_7d7f70bc75999486401c90b97c0360ad {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,14 +54,14 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_67e0a7b51ff20c385c068ee0a8d7495e" ></div>
|
||||
<div class="folium-map" id="map_7d7f70bc75999486401c90b97c0360ad" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_67e0a7b51ff20c385c068ee0a8d7495e = L.map(
|
||||
"map_67e0a7b51ff20c385c068ee0a8d7495e",
|
||||
var map_7d7f70bc75999486401c90b97c0360ad = L.map(
|
||||
"map_7d7f70bc75999486401c90b97c0360ad",
|
||||
{
|
||||
center: [0.0, 0.0],
|
||||
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>
|
||||
|
|
@ -116,7 +96,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_67e0a7b51ff20c385c068ee0a8d7495e);
|
||||
trajeto_json.addTo(map_7d7f70bc75999486401c90b97c0360ad);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -179,9 +159,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
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(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -246,7 +226,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_67e0a7b51ff20c385c068ee0a8d7495e.setView(novaPosicao, map_67e0a7b51ff20c385c068ee0a8d7495e.getZoom());
|
||||
map_7d7f70bc75999486401c90b97c0360ad.setView(novaPosicao, map_7d7f70bc75999486401c90b97c0360ad.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -268,7 +248,7 @@
|
|||
marcadorDinamico.setRotationAngle(angulo);
|
||||
|
||||
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
|
||||
map_67e0a7b51ff20c385c068ee0a8d7495e.setView(novaPosicao, map_67e0a7b51ff20c385c068ee0a8d7495e.getZoom());*/
|
||||
map_7d7f70bc75999486401c90b97c0360ad.setView(novaPosicao, map_7d7f70bc75999486401c90b97c0360ad.getZoom());*/
|
||||
});
|
||||
|
||||
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_fa80b7c1ff2d8431553ba20c387f3aad {
|
||||
#map_353f6ba1c95182a48ae91b9a891f095d {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,16 +54,16 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_fa80b7c1ff2d8431553ba20c387f3aad" ></div>
|
||||
<div class="folium-map" id="map_353f6ba1c95182a48ae91b9a891f095d" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_fa80b7c1ff2d8431553ba20c387f3aad = L.map(
|
||||
"map_fa80b7c1ff2d8431553ba20c387f3aad",
|
||||
var map_353f6ba1c95182a48ae91b9a891f095d = L.map(
|
||||
"map_353f6ba1c95182a48ae91b9a891f095d",
|
||||
{
|
||||
center: [0.0, 0.0],
|
||||
center: [-22.172444564750002, -47.395241953833334],
|
||||
crs: L.CRS.EPSG3857,
|
||||
...{
|
||||
"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",
|
||||
{
|
||||
"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({
|
||||
|
||||
|
|
@ -148,23 +148,23 @@
|
|||
}*/
|
||||
});
|
||||
};
|
||||
var geo_json_09ba5926a79ad74ce059c256c57c60e9 = L.geoJson(null, {
|
||||
onEachFeature: geo_json_09ba5926a79ad74ce059c256c57c60e9_onEachFeature,
|
||||
var geo_json_b417ccdd5de3860de9f9bb6e9ce890b6 = L.geoJson(null, {
|
||||
onEachFeature: geo_json_b417ccdd5de3860de9f9bb6e9ce890b6_onEachFeature,
|
||||
|
||||
...{
|
||||
}
|
||||
});
|
||||
|
||||
function geo_json_09ba5926a79ad74ce059c256c57c60e9_add (data) {
|
||||
geo_json_09ba5926a79ad74ce059c256c57c60e9
|
||||
function geo_json_b417ccdd5de3860de9f9bb6e9ce890b6_add (data) {
|
||||
geo_json_b417ccdd5de3860de9f9bb6e9ce890b6
|
||||
.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_09ba5926a79ad74ce059c256c57c60e9.setStyle(function(feature) {return feature.properties.style;});
|
||||
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_b417ccdd5de3860de9f9bb6e9ce890b6.setStyle(function(feature) {return feature.properties.style;});
|
||||
|
||||
|
||||
|
||||
geo_json_09ba5926a79ad74ce059c256c57c60e9.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad);
|
||||
geo_json_b417ccdd5de3860de9f9bb6e9ce890b6.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad);
|
||||
trajeto_json.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
}
|
||||
trajeto_dinamico_json_add({"features": []});
|
||||
|
||||
trajeto_dinamico_json.addTo(map_fa80b7c1ff2d8431553ba20c387f3aad);
|
||||
trajeto_dinamico_json.addTo(map_353f6ba1c95182a48ae91b9a891f095d);
|
||||
|
||||
function adicionarGeometriaDinamica(novaGeometria) {
|
||||
trajeto_dinamico_json.addData(novaGeometria);
|
||||
|
|
@ -296,9 +296,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
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(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_fa80b7c1ff2d8431553ba20c387f3aad.setView(novaPosicao, map_fa80b7c1ff2d8431553ba20c387f3aad.getZoom());
|
||||
map_353f6ba1c95182a48ae91b9a891f095d.setView(novaPosicao, map_353f6ba1c95182a48ae91b9a891f095d.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@
|
|||
function atualizarSelecaoRuas(selecionadas) {
|
||||
selecionadas = JSON.parse(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))) {
|
||||
layer.setStyle({ color: 'blue' });
|
||||
} else {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -199,23 +199,35 @@ class CameraOak:
|
|||
except Exception as 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)
|
||||
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.setScript(f"""
|
||||
from time import monotonic
|
||||
i = 0
|
||||
while True:
|
||||
f = node.io['in'].get()
|
||||
"""
|
||||
+
|
||||
f"""
|
||||
if i % {N_seg} == 0:
|
||||
node.io['toSeg'].send(f)
|
||||
""" if self.modelo_ia_seg is not None else ""
|
||||
+
|
||||
f"""
|
||||
if i % {N_det} == 0:
|
||||
node.io['toDet'].send(f)
|
||||
""" if self.modelo_ia_det is not None else ""
|
||||
+
|
||||
f"""
|
||||
i += 1
|
||||
""")
|
||||
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:
|
||||
try:
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -93,7 +93,7 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
},
|
||||
"RegrasAtivas": {
|
||||
"matriz_custo": False,
|
||||
"deteccao_obstaculos": False,
|
||||
"deteccao_obstaculos": True,
|
||||
},
|
||||
"Equipamento": {
|
||||
"largura": _equipamento.get("largura", 0.85),
|
||||
|
|
@ -197,7 +197,8 @@ def _regras_taticas(contexto):
|
|||
|
||||
# >>> NOVO: gate de bloqueio/parede com block + d_obs_min <<<
|
||||
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")
|
||||
decision = block.get("decision", {})
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
|
|||
_contexto = ContextoGlobalRedis.get_contexto()
|
||||
_controle = ContextoGlobalRedis.get_controle()
|
||||
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_max = _operacao.get("Mov", {}).get("percent_vel_max", 100)
|
||||
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]:
|
||||
velocidade_sp = vel_min
|
||||
elif status_carro == StatusCarroMapa.CaminhandoRua:
|
||||
if pulverizador_automatico and ervas_no_radar > 0:
|
||||
if pulverizador_automatico and ervas_no_radar:
|
||||
velocidade_sp = vel_min
|
||||
else:
|
||||
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -43,14 +43,14 @@ class CameraManager:
|
|||
self.mx_id = mx_id
|
||||
|
||||
from weed_worker.config import load_seg_config
|
||||
camera_config = load_seg_config()
|
||||
seg_config = load_seg_config()
|
||||
|
||||
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:
|
||||
self.camera = nova
|
||||
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
|
||||
return
|
||||
if self.camera is None:
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ def load_seg_config(force_reload=False):
|
|||
"min_frac_erva_top_off": 0.0010,
|
||||
"min_frac_erva_por_bico": 0.02,
|
||||
"usar_morfologia": True,
|
||||
"kernel_morf": 3
|
||||
"kernel_morf": 3,
|
||||
"det_every_n": 1,
|
||||
}
|
||||
dadosAtu = ContextoGlobalRedis.get_operacao().get("Atu", {})
|
||||
contexto = ContextoGlobalRedis.get_contexto()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from enum import IntEnum
|
|||
import time
|
||||
import cv2
|
||||
import numpy as np
|
||||
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
|
||||
from shared.utils import encode_image_base64
|
||||
|
||||
class ClassesSegmentacao(IntEnum):
|
||||
|
|
@ -47,7 +46,6 @@ class WeedDetector:
|
|||
self.ervas_ativas_filtradas = []
|
||||
self.next_erva_id = 1
|
||||
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.ervas_registradas_bico = [set() for _ in range(qtd_bicos)]
|
||||
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._ervas_no_radar = False
|
||||
self._ervas_no_radar_percent = 0.0
|
||||
self.debug_estat = False
|
||||
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosWeedWorker,
|
||||
analise__ervas_identificadas=self.ervas_identificadas
|
||||
)
|
||||
self.debug_estat = True
|
||||
|
||||
def _segmentar_predictions(self, predictions):
|
||||
try:
|
||||
|
|
@ -112,9 +105,7 @@ class WeedDetector:
|
|||
"timestamp": time.time(),
|
||||
"height": predictions.shape[0],
|
||||
"width": predictions.shape[1],
|
||||
"deteccoes": [], # deteccoes_filtro,
|
||||
"controle": controle_bicos,
|
||||
"ervas_identificadas": self.ervas_identificadas,
|
||||
"ervas_no_radar": ervas_no_radar,
|
||||
"estatisticas": {
|
||||
"erva": estat_erva,
|
||||
|
|
@ -251,7 +242,7 @@ class WeedDetector:
|
|||
"ema_global": ema_g,
|
||||
"thr_on_global": on_global,
|
||||
"thr_off_global": off_global,
|
||||
"vel_adj": adj,
|
||||
"vel_adj": adj
|
||||
}
|
||||
return ervas_no_radar, None
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
#include <SimpleKalmanFilter.h>
|
||||
|
||||
// Defina os pinos para o módulo HX711
|
||||
#define PINO_DT 4 // Pino de dados do HX711 ao GPIO5 do ESP32
|
||||
#define PINO_SCK 5 // Pino de clock do HX711 ao GPIO18 do ESP32
|
||||
#define PINO_DT 15 // Pino de dados do HX711 ao GPIO5 do ESP32
|
||||
#define PINO_SCK 16 // Pino de clock do HX711 ao GPIO18 do ESP32
|
||||
SimpleKalmanFilter kalman = SimpleKalmanFilter(2, 2, 0.01);
|
||||
|
||||
double massaRef = 0.8;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -1,356 +1,466 @@
|
|||
#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>
|
||||
#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 {
|
||||
|
||||
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:
|
||||
typedef void (*OnReceiveCallback)(int packetSize, int senderId,
|
||||
CanMessagePosicaoDados posicao, byte* data, int dataLength);
|
||||
|
||||
// --- IDs especiais do teu protocolo ---
|
||||
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;
|
||||
}
|
||||
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
|
||||
// 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();
|
||||
|
||||
// Começa aceitando tudo para depurar; depois afinamos a máscara
|
||||
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
|
||||
.acceptance_code = (uint32_t(_nodeId) << 21),
|
||||
.acceptance_mask = ~(0x7FFu << 21),
|
||||
.single_filter = true
|
||||
};
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
err = twai_driver_install(&g_config, &t_config, &f_config);
|
||||
esp_err_t err = twai_driver_install(&g_config, &t_config, &f_config);
|
||||
if (err != ESP_OK) {
|
||||
PrintTela("[CAN] Erro ao instalar driver: ", false);
|
||||
PrintTela(String(err));
|
||||
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));
|
||||
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);
|
||||
}
|
||||
// Habilita alerts úteis
|
||||
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 (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 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 (CanTaskProcessHandle == NULL) {
|
||||
xTaskCreatePinnedToCore(CanService::CanTaskProcessWrapper, "CanTaskProcess", 6144, this, 18, &CanTaskProcessHandle, APP_CPU_NUM);
|
||||
}
|
||||
#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);
|
||||
|
||||
// --- 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");
|
||||
}
|
||||
|
||||
// --- 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 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) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
service->CanTaskRx(pvParameters);
|
||||
static_cast<CanService*>(pvParameters)->CanTaskRx(pvParameters);
|
||||
}
|
||||
|
||||
void CanTaskRx(void* pvParameters) {
|
||||
twai_message_t message;
|
||||
while (true) {
|
||||
if (twai_receive(&message, pdMS_TO_TICKS(10)) == ESP_OK) {
|
||||
for (;;) {
|
||||
// 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) {
|
||||
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("");
|
||||
PrintTela("[CAN RX] ID: 0x" + String(message.identifier, HEX) + " Len: " + String(message.data_length_code));
|
||||
}
|
||||
|
||||
if (MensagemTx) {
|
||||
PrintTela("[CAN RX] Ouviu a própria resposta!");
|
||||
continue;
|
||||
// tenta enfileirar pro worker
|
||||
if (callbacksQ && xQueueSend(callbacksQ, &message, 0) != pdTRUE) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
TaskHandle_t CanTaskProcessHandle = NULL;
|
||||
static void CanTaskProcessWrapper(void *pvParameters) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
service->CanTaskProcess(pvParameters);
|
||||
static void CallbackWorkerWrapper(void *pvParameters) {
|
||||
static_cast<CanService*>(pvParameters)->CallbackWorker(pvParameters);
|
||||
}
|
||||
|
||||
void CanTaskProcess(void* pvParameters) {
|
||||
CanService *service = static_cast<CanService*>(pvParameters);
|
||||
void CallbackWorker(void* pvParameters) {
|
||||
twai_message_t msg;
|
||||
for (;;) {
|
||||
// WDT
|
||||
esp_task_wdt_reset();
|
||||
|
||||
while (true) {
|
||||
while (uxQueueMessagesWaiting(service->canQueueRx) > 0) {
|
||||
if (xQueueReceive(service->canQueueRx, &msg, portMAX_DELAY) == pdTRUE) {
|
||||
if (xQueueReceive(callbacksQ, &msg, portMAX_DELAY) == pdTRUE) {
|
||||
if (_callback && msg.data_length_code > 0) {
|
||||
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) + " ";
|
||||
_callback(msg.data_length_code, msg.identifier, posicao,
|
||||
&msg.data[1], msg.data_length_code - 1);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
TaskHandle_t callingTaskHandle = xTaskGetCurrentTaskHandle();
|
||||
void PublicarTx(const twai_message_t& msg) {
|
||||
// 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
|
||||
auto* bundle = new std::pair<CallbackPayload*, TaskHandle_t>(payload, callingTaskHandle);
|
||||
uint16_t key = make_key(msg);
|
||||
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
|
||||
BaseType_t status = xTaskCreatePinnedToCore(
|
||||
TaskCallbackWrapper,
|
||||
taskName.c_str(),
|
||||
8192,
|
||||
bundle,
|
||||
5,
|
||||
&handleTask,
|
||||
APP_CPU_NUM
|
||||
);
|
||||
if (xQueueReceive(keysQ, &key, portMAX_DELAY) == pdTRUE) {
|
||||
// busca a msg mais nova dessa chave
|
||||
twai_message_t m{};
|
||||
int idx = key % MAX_KEYS;
|
||||
int foundIdx = -1;
|
||||
for (int i=0; i<MAX_KEYS; ++i, idx=(idx+1)%MAX_KEYS) {
|
||||
if (latestByKey[idx].used && latestByKey[idx].key == key) {
|
||||
m = latestByKey[idx].msg;
|
||||
foundIdx = idx;
|
||||
break;
|
||||
}
|
||||
if (!latestByKey[idx].used) break;
|
||||
}
|
||||
|
||||
if (status != pdPASS) {
|
||||
PrintTela("[CAN Process] ❌ Falha ao criar task de callback!");
|
||||
delete payload;
|
||||
delete bundle;
|
||||
if (foundIdx < 0) {
|
||||
// chave desapareceu; nada a fazer
|
||||
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));
|
||||
// Garantias do frame (caso quem chamou não tenha setado)
|
||||
m.extd = 0; // standard frame
|
||||
m.rtr = 0; // data frame
|
||||
|
||||
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);
|
||||
bool ok = false;
|
||||
for (int tent=0; tent<3; ++tent) {
|
||||
if (enviarDadosCan(m, pdMS_TO_TICKS(50))) {
|
||||
last_tx_ts = esp_timer_get_time();
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
// --- 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
|
||||
// Ex.: msg.identifier já deve estar correto para o PC
|
||||
esp_err_t err = twai_transmit(&msg, timeoutTicks);
|
||||
if (err != ESP_OK) {
|
||||
PrintTela("[CAN TX] Falha transmit (" + String((int)err) + ")");
|
||||
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) {
|
||||
|
|
@ -358,44 +468,38 @@ class CanService {
|
|||
}
|
||||
|
||||
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;
|
||||
msg.identifier = _nodeId; // + 1;
|
||||
msg.extd = 0;
|
||||
msg.rtr = 0;
|
||||
// 2) Monta frame
|
||||
twai_message_t msg{};
|
||||
// ⚠️ ID: use o ID que o PC espera para respostas desse nó (ajuste se necessário)
|
||||
msg.identifier = _nodeId; // ou outro mapeamento do seu protocolo
|
||||
msg.extd = 0; // standard frame
|
||||
msg.rtr = 0; // data frame
|
||||
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
|
||||
msg.data[i] = payload[i];
|
||||
}
|
||||
|
||||
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");
|
||||
// 3) Publica via coalescência (substitui a antiga fila TX)
|
||||
PublicarTx(msg);
|
||||
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;
|
||||
}
|
||||
|
|
@ -405,9 +509,7 @@ class CanService {
|
|||
else if (pos > 100 && pos <= 150) {
|
||||
funcao = F_Code::CmdTx;
|
||||
}
|
||||
|
||||
//PrintTela("Funcao parseada para posicao: " + String(pos) + ", funcao: " + String(funcao));
|
||||
|
||||
return funcao;
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +519,7 @@ class CanService {
|
|||
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);
|
||||
data.push_back(static_cast<uint8_t>(Versao));
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -430,45 +532,11 @@ class CanService {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
uint32_t _baudRate = 500000;
|
||||
uint8_t _nodeId = 0;
|
||||
OnReceiveCallback _callback = nullptr;
|
||||
bool DebugMode = false;
|
||||
volatile bool processamentoEmAndamento = false;
|
||||
volatile long idmsg = 0;
|
||||
|
||||
|
||||
bool DebugMode = true;
|
||||
};
|
||||
|
||||
TaskHandle_t CanService::handleCallbackTask = NULL;
|
||||
|
||||
#endif
|
||||
#endif // CANSERVICE_H
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -22,14 +22,13 @@ class SensorMassa : public ComponenteCAN {
|
|||
PinoModel _pinoDT;
|
||||
PinoModel _pinoSCK;
|
||||
|
||||
float _Offset = 1895800.0;
|
||||
float _MassaRef = 0.8;
|
||||
float _MassaLeituraRef = 40876.37;
|
||||
long _Offset = 1996283;
|
||||
float _Escala = 50857.316406;
|
||||
float _OffsetMassa = 0.0;
|
||||
|
||||
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;
|
||||
|
||||
bool Iniciado = false;
|
||||
|
|
@ -42,7 +41,8 @@ class SensorMassa : public ComponenteCAN {
|
|||
|
||||
if (_pinoDT.Definido() && _pinoSCK.Definido()) {
|
||||
balanca.begin(_pinoDT.num, _pinoSCK.num);
|
||||
balanca.set_scale();
|
||||
balanca.set_offset(_Offset);
|
||||
balanca.set_scale(_Escala);
|
||||
//balanca->tare();
|
||||
|
||||
Iniciado = true;
|
||||
|
|
@ -63,6 +63,7 @@ class SensorMassa : public ComponenteCAN {
|
|||
// Redefinir as configurações para os valores iniciais
|
||||
_pinoDT.Desconectar();
|
||||
_pinoSCK.Desconectar();
|
||||
Massa = 0;
|
||||
|
||||
PrintTela(_ID + " Desligado");
|
||||
|
||||
|
|
@ -119,7 +120,7 @@ class SensorMassa : public ComponenteCAN {
|
|||
sensor->ID_Num = idNum;
|
||||
sensor->_pinoDT = PinoModel(pinoDt, CONTROLADOR, _INPUT, DIGITAL);
|
||||
sensor->_pinoSCK = PinoModel(pinoSck, CONTROLADOR, _OUTPUT);
|
||||
sensor->_Offset = offset;
|
||||
sensor->_OffsetMassa = offset;
|
||||
sensor->Inicializar();
|
||||
if (!jaExiste) {
|
||||
lista.push_back(sensor);
|
||||
|
|
@ -146,22 +147,12 @@ class SensorMassa : public ComponenteCAN {
|
|||
private:
|
||||
void AferirMassa() {
|
||||
if (Iniciado) {
|
||||
float leitura = balanca.get_units(5);
|
||||
leituraMassa = filtro.updateEstimate(leitura);
|
||||
Massa = CalcularMassa(leituraMassa);
|
||||
float leitura = -balanca.get_units(5);
|
||||
leitura += _OffsetMassa;
|
||||
Massa = filtro.updateEstimate(leitura);
|
||||
}
|
||||
}
|
||||
|
||||
float CalcularMassa(float _leitura) {
|
||||
double leituraMassa = _leitura * -1;
|
||||
leituraMassa += _Offset;
|
||||
|
||||
float FatorLeitura = _MassaRef / _MassaLeituraRef;
|
||||
float massa = FatorLeitura * leituraMassa;
|
||||
return massa;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ RESOLUCAO = config["resolucao"]
|
|||
pasta_origem = os.path.join(MODELO, "dataset", f"{RESOLUCAO[0]}x{RESOLUCAO[1]}")
|
||||
pasta_destino = os.path.join(MODELO, "dataset", "split")
|
||||
|
||||
percent_train = 0.9
|
||||
percent_val = 0.09
|
||||
percent_test = 0.01
|
||||
percent_train = 0.8
|
||||
percent_val = 0.18
|
||||
percent_test = 0.02
|
||||
|
||||
seed = 42
|
||||
random.seed(seed)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"camera": "oak-d",
|
||||
"camera": "oak-1",
|
||||
"modelo": "fast_scnn",
|
||||
"model_name": "ruas_new",
|
||||
"main_class_name": "cana",
|
||||
"model_name": "ervas_medium_new",
|
||||
"main_class_name": "erva",
|
||||
"use_main_class": false,
|
||||
"resolucao": [512, 288],
|
||||
"roi_inicio": 0.0,
|
||||
"roi_tamanho": 1.0,
|
||||
"shaves": 3
|
||||
"shaves": 6
|
||||
}
|
||||
Loading…
Reference in New Issue