ajustes gps e movimentacao

This commit is contained in:
Diego Freitas 2025-09-02 10:49:11 -03:00
parent 96211aa734
commit 34537a4233
166 changed files with 2715 additions and 1163 deletions

Binary file not shown.

View File

@ -1277,7 +1277,7 @@ namespace AgroBase.Forms.IHM
double Precisao = GpsInicializado ? (int)Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.QualidadeFix : -1; double Precisao = GpsInicializado ? (int)Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.QualidadeFix : -1;
idx = PreencheDadosLinha(grid, Testes, idx, Precisao); idx = PreencheDadosLinha(grid, Testes, idx, Precisao);
double Satelites = GpsInicializado ? Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.NumeroSatelites.valor : -1; double Satelites = GpsInicializado ? Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.NumeroSatelites : -1;
idx = PreencheDadosLinha(grid, Testes, idx, Satelites); idx = PreencheDadosLinha(grid, Testes, idx, Satelites);
double OrientacaoGPS = GpsInicializado ? Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.OrientacaoReal : -1; double OrientacaoGPS = GpsInicializado ? Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.OrientacaoReal : -1;

View File

@ -46,8 +46,8 @@
this.lblDistanciaPercorrida = new System.Windows.Forms.Label(); this.lblDistanciaPercorrida = new System.Windows.Forms.Label();
this.txtTempoOperacao = new System.Windows.Forms.TextBox(); this.txtTempoOperacao = new System.Windows.Forms.TextBox();
this.lblTempoOperacao = new System.Windows.Forms.Label(); this.lblTempoOperacao = new System.Windows.Forms.Label();
this.txtErvasIdentificadas = new System.Windows.Forms.TextBox(); this.txtLatenciaControle = new System.Windows.Forms.TextBox();
this.lblErvasIdentificadas = new System.Windows.Forms.Label(); this.lblLatenciaControle = new System.Windows.Forms.Label();
this.txtHerbicidaAplicado = new System.Windows.Forms.TextBox(); this.txtHerbicidaAplicado = new System.Windows.Forms.TextBox();
this.lblHerbicidaAplicado = new System.Windows.Forms.Label(); this.lblHerbicidaAplicado = new System.Windows.Forms.Label();
this.txtBateriaConsumida = new System.Windows.Forms.TextBox(); this.txtBateriaConsumida = new System.Windows.Forms.TextBox();
@ -271,24 +271,24 @@
this.lblTempoOperacao.TabIndex = 13; this.lblTempoOperacao.TabIndex = 13;
this.lblTempoOperacao.Text = "Tempo de Operação"; this.lblTempoOperacao.Text = "Tempo de Operação";
// //
// txtErvasIdentificadas // txtLatenciaControle
// //
this.txtErvasIdentificadas.Font = new System.Drawing.Font("Microsoft Sans Serif", 12.25F); this.txtLatenciaControle.Font = new System.Drawing.Font("Microsoft Sans Serif", 12.25F);
this.txtErvasIdentificadas.Location = new System.Drawing.Point(164, 261); this.txtLatenciaControle.Location = new System.Drawing.Point(164, 261);
this.txtErvasIdentificadas.Name = "txtErvasIdentificadas"; this.txtLatenciaControle.Name = "txtLatenciaControle";
this.txtErvasIdentificadas.ReadOnly = true; this.txtLatenciaControle.ReadOnly = true;
this.txtErvasIdentificadas.Size = new System.Drawing.Size(120, 26); this.txtLatenciaControle.Size = new System.Drawing.Size(120, 26);
this.txtErvasIdentificadas.TabIndex = 16; this.txtLatenciaControle.TabIndex = 16;
this.txtErvasIdentificadas.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.txtLatenciaControle.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
// //
// lblErvasIdentificadas // lblLatenciaControle
// //
this.lblErvasIdentificadas.AutoSize = true; this.lblLatenciaControle.AutoSize = true;
this.lblErvasIdentificadas.Location = new System.Drawing.Point(3, 264); this.lblLatenciaControle.Location = new System.Drawing.Point(3, 264);
this.lblErvasIdentificadas.Name = "lblErvasIdentificadas"; this.lblLatenciaControle.Name = "lblLatenciaControle";
this.lblErvasIdentificadas.Size = new System.Drawing.Size(144, 20); this.lblLatenciaControle.Size = new System.Drawing.Size(134, 20);
this.lblErvasIdentificadas.TabIndex = 15; this.lblLatenciaControle.TabIndex = 15;
this.lblErvasIdentificadas.Text = "Ervas Identificadas"; this.lblLatenciaControle.Text = "Latencia Controle";
// //
// txtHerbicidaAplicado // txtHerbicidaAplicado
// //
@ -462,9 +462,9 @@
this.pnlSensores.Controls.Add(this.txtBateriaConsumida); this.pnlSensores.Controls.Add(this.txtBateriaConsumida);
this.pnlSensores.Controls.Add(this.txtTempoOperacao); this.pnlSensores.Controls.Add(this.txtTempoOperacao);
this.pnlSensores.Controls.Add(this.lblBateriaConsumida); this.pnlSensores.Controls.Add(this.lblBateriaConsumida);
this.pnlSensores.Controls.Add(this.lblErvasIdentificadas); this.pnlSensores.Controls.Add(this.lblLatenciaControle);
this.pnlSensores.Controls.Add(this.txtHerbicidaAplicado); this.pnlSensores.Controls.Add(this.txtHerbicidaAplicado);
this.pnlSensores.Controls.Add(this.txtErvasIdentificadas); this.pnlSensores.Controls.Add(this.txtLatenciaControle);
this.pnlSensores.Controls.Add(this.lblHerbicidaAplicado); this.pnlSensores.Controls.Add(this.lblHerbicidaAplicado);
this.pnlSensores.Dock = System.Windows.Forms.DockStyle.Fill; this.pnlSensores.Dock = System.Windows.Forms.DockStyle.Fill;
this.pnlSensores.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.pnlSensores.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
@ -957,8 +957,8 @@
private System.Windows.Forms.Label lblDistanciaPercorrida; private System.Windows.Forms.Label lblDistanciaPercorrida;
private System.Windows.Forms.TextBox txtTempoOperacao; private System.Windows.Forms.TextBox txtTempoOperacao;
private System.Windows.Forms.Label lblTempoOperacao; private System.Windows.Forms.Label lblTempoOperacao;
private System.Windows.Forms.TextBox txtErvasIdentificadas; private System.Windows.Forms.TextBox txtLatenciaControle;
private System.Windows.Forms.Label lblErvasIdentificadas; private System.Windows.Forms.Label lblLatenciaControle;
private System.Windows.Forms.TextBox txtHerbicidaAplicado; private System.Windows.Forms.TextBox txtHerbicidaAplicado;
private System.Windows.Forms.Label lblHerbicidaAplicado; private System.Windows.Forms.Label lblHerbicidaAplicado;
private System.Windows.Forms.TextBox txtBateriaConsumida; private System.Windows.Forms.TextBox txtBateriaConsumida;

View File

@ -162,13 +162,13 @@ namespace AgroBase.Forms.IHM
txtOperacaoLiberada.Text = opLiberada ? "Sim" : "Não"; txtOperacaoLiberada.Text = opLiberada ? "Sim" : "Não";
txtOperacaoLiberada.ForeColor = opLiberada ? Color.Black : Color.Red; txtOperacaoLiberada.ForeColor = opLiberada ? Color.Black : Color.Red;
CodigoFalha = _Sensoriamento.CodigoFalha; CodigoFalha = _Sensoriamento.CodigoFalha;
btnCodFalha.Text = CodigoFalha.ToString(); btnCodFalha.Text = CodigoFalha.ToString("0.000");
txtStatusAtual.Text = _Sensoriamento.StatusOperacao.ToString(); txtStatusAtual.Text = _Sensoriamento.StatusOperacao.ToString();
txtIniciadoEm.Text = _Sensoriamento.DataInicio == DateTime.MinValue ? "Aguardando" : _Sensoriamento.DataInicio.ToString("dd/MM HH:mm:ss"); txtIniciadoEm.Text = _Sensoriamento.DataInicio == DateTime.MinValue ? "Aguardando" : _Sensoriamento.DataInicio.ToString("dd/MM HH:mm:ss");
txtConcluidoEm.Text = _Sensoriamento.DataFim == DateTime.MinValue ? "-" : _Sensoriamento.DataFim.ToString("dd/MM HH:mm:ss"); txtConcluidoEm.Text = _Sensoriamento.DataFim == DateTime.MinValue ? "-" : _Sensoriamento.DataFim.ToString("dd/MM HH:mm:ss");
txtDistanciaPercorrida.Text = _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m"; txtDistanciaPercorrida.Text = _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m";
txtTempoOperacao.Text = _Sensoriamento.TempoDecorrido.ToString("HH:mm:ss"); txtTempoOperacao.Text = _Sensoriamento.TempoDecorrido.ToString("HH:mm:ss");
//txtErvasIdentificadas.Text = _Sensoriamento.Atuador.QtdErvasIdentificadas.ToString(); txtLatenciaControle.Text = _Sensoriamento.Controle.Latencia.ToString();
txtHerbicidaAplicado.Text = _Sensoriamento.Atuador.HerbicidaConsumido.ToString("0.000") + " L"; txtHerbicidaAplicado.Text = _Sensoriamento.Atuador.HerbicidaConsumido.ToString("0.000") + " L";
txtBateriaConsumida.Text = _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + " %"; txtBateriaConsumida.Text = _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + " %";

View File

@ -80,6 +80,7 @@
this.lblTipoMovimento = new System.Windows.Forms.Label(); this.lblTipoMovimento = new System.Windows.Forms.Label();
this.chbPulverizador = new System.Windows.Forms.CheckBox(); this.chbPulverizador = new System.Windows.Forms.CheckBox();
this.flwCamerasSolo = new System.Windows.Forms.FlowLayoutPanel(); this.flwCamerasSolo = new System.Windows.Forms.FlowLayoutPanel();
this.chbSonarAtivado = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.chartGraficos)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.chartGraficos)).BeginInit();
this.tabControl1.SuspendLayout(); this.tabControl1.SuspendLayout();
this.tabMapa.SuspendLayout(); this.tabMapa.SuspendLayout();
@ -637,12 +638,25 @@
this.flwCamerasSolo.Size = new System.Drawing.Size(597, 172); this.flwCamerasSolo.Size = new System.Drawing.Size(597, 172);
this.flwCamerasSolo.TabIndex = 36; this.flwCamerasSolo.TabIndex = 36;
// //
// chbSonarAtivado
//
this.chbSonarAtivado.AutoSize = true;
this.chbSonarAtivado.Location = new System.Drawing.Point(390, 310);
this.chbSonarAtivado.Margin = new System.Windows.Forms.Padding(2);
this.chbSonarAtivado.Name = "chbSonarAtivado";
this.chbSonarAtivado.Size = new System.Drawing.Size(92, 17);
this.chbSonarAtivado.TabIndex = 37;
this.chbSonarAtivado.Text = "Sonar ativado";
this.chbSonarAtivado.UseVisualStyleBackColor = true;
this.chbSonarAtivado.CheckedChanged += new System.EventHandler(this.chbSonarAtivado_CheckedChanged);
//
// frmOperacaoManual // frmOperacaoManual
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White; this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(1243, 649); this.ClientSize = new System.Drawing.Size(1243, 649);
this.Controls.Add(this.chbSonarAtivado);
this.Controls.Add(this.flwCamerasSolo); this.Controls.Add(this.flwCamerasSolo);
this.Controls.Add(this.chbPulverizador); this.Controls.Add(this.chbPulverizador);
this.Controls.Add(this.lblTipoMovimento); this.Controls.Add(this.lblTipoMovimento);
@ -742,5 +756,6 @@
private System.Windows.Forms.Label lblMov_DF; private System.Windows.Forms.Label lblMov_DF;
private System.Windows.Forms.FlowLayoutPanel flwCamerasSolo; private System.Windows.Forms.FlowLayoutPanel flwCamerasSolo;
private System.Windows.Forms.Label lblGPSFix; private System.Windows.Forms.Label lblGPSFix;
private System.Windows.Forms.CheckBox chbSonarAtivado;
} }
} }

View File

@ -61,6 +61,7 @@ namespace AgroBase.Forms.Operacoes
} }
chbPulverizador.Checked = Variaveis.OperacaoEmAndamento.Controle.PulverizadorAutomatico; chbPulverizador.Checked = Variaveis.OperacaoEmAndamento.Controle.PulverizadorAutomatico;
chbSonarAtivado.Checked = Variaveis.OperacaoEmAndamento.Controle.SonarAtivado;
InicializarComandosDirecional(); InicializarComandosDirecional();
PopularInformacoesGerais(); PopularInformacoesGerais();
@ -154,7 +155,7 @@ namespace AgroBase.Forms.Operacoes
lblDistanciaEsquerda.Text = "Distancia Esq: " + _Sensoriamento.Trajetoria.DistanciaEsquerda.ToString("0.00") + " cm"; lblDistanciaEsquerda.Text = "Distancia Esq: " + _Sensoriamento.Trajetoria.DistanciaEsquerda.ToString("0.00") + " cm";
lblDistanciaDireita.Text = "Distancia Dir: " + _Sensoriamento.Trajetoria.DistanciaDireita.ToString("0.00") + " cm"; lblDistanciaDireita.Text = "Distancia Dir: " + _Sensoriamento.Trajetoria.DistanciaDireita.ToString("0.00") + " cm";
lblGPSsatelites.Text = _Sensoriamento.Gps.NumeroSatelites.valor.ToString() + " satélites"; lblGPSsatelites.Text = _Sensoriamento.Gps.NumeroSatelites.ToString() + " satélites";
lblGPSFix.Text = (_Sensoriamento.Gps.Ntrip_ativado ? "N " : "B ") + _Sensoriamento.Gps.IdadeCorrecao + ": " + _Sensoriamento.Gps.QualidadeFix.ToString(); lblGPSFix.Text = (_Sensoriamento.Gps.Ntrip_ativado ? "N " : "B ") + _Sensoriamento.Gps.IdadeCorrecao + ": " + _Sensoriamento.Gps.QualidadeFix.ToString();
lblGPSprecisao.Text = "Precisão: " + _Sensoriamento.Gps.PrecisaoCm.ToString("0.00") + " cm"; lblGPSprecisao.Text = "Precisão: " + _Sensoriamento.Gps.PrecisaoCm.ToString("0.00") + " cm";
lblGPSvelocidade.Text = "Vel.: " + _Sensoriamento.Gps.Velocidade.ToString("0.00") + " km/h"; lblGPSvelocidade.Text = "Vel.: " + _Sensoriamento.Gps.Velocidade.ToString("0.00") + " km/h";
@ -165,13 +166,20 @@ namespace AgroBase.Forms.Operacoes
lblRPM.Text = _Sensoriamento.Movimentacao.RPMMedio.ToString("0") + " rpm"; lblRPM.Text = _Sensoriamento.Movimentacao.RPMMedio.ToString("0") + " rpm";
lblVelocidade.Text = (_Sensoriamento.Movimentacao.VelocidadeMedia * 3.6).ToString("0.00"); lblVelocidade.Text = (_Sensoriamento.Movimentacao.VelocidadeMedia * 3.6).ToString("0.00");
bool atuadorAtivado = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ConexaoAtiva || true; bool atuadorAtivado = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ConexaoAtiva;
chbPulverizador.Enabled = atuadorAtivado; chbPulverizador.Enabled = atuadorAtivado;
if (!atuadorAtivado) if (!atuadorAtivado)
{ {
chbPulverizador.Checked = false; chbPulverizador.Checked = false;
} }
bool sonarAtivado = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual.Status == StatusModulo.Operante;
chbSonarAtivado.Enabled = sonarAtivado;
if (!sonarAtivado)
{
chbSonarAtivado.Checked = false;
}
if (!ReqFrameCam) if (!ReqFrameCam)
_ = AtualizarCameraSolo(); _ = AtualizarCameraSolo();
@ -674,7 +682,7 @@ namespace AgroBase.Forms.Operacoes
private void chbSonarAtivado_CheckedChanged(object sender, EventArgs e) private void chbSonarAtivado_CheckedChanged(object sender, EventArgs e)
{ {
Variaveis.OperacaoEmAndamento.Controle.SonarAtivado = ((CheckBox)sender).Checked;
} }
private void chbSonarDesvioAutomatico_CheckedChanged(object sender, EventArgs e) private void chbSonarDesvioAutomatico_CheckedChanged(object sender, EventArgs e)

View File

@ -1085,12 +1085,12 @@ namespace AgroBase.Forms.Operacoes
txtStatusCarro.Text = Log.Trajetoria.StatusCarro.ToString(); txtStatusCarro.Text = Log.Trajetoria.StatusCarro.ToString();
txtHerbicidaPorErva.Text = herbicidaPorErva.ToString("0.00") + " ml"; txtHerbicidaPorErva.Text = herbicidaPorErva.ToString("0.00") + " ml";
txtDentroRua.Text = Log.Trajetoria.CorredorAtual.Dentro ? "Sim" : "Não"; txtDentroRua.Text = Log.Trajetoria.CorredorAtual.Dentro ? "Sim" : "Não";
txtDirecaoCarro.Text = Log.Trajetoria.DirecaoCaminho.ToString(); txtDirecaoCarro.Text = Log.Trajetoria.DirecaoCaminho.ToString() + " (" + Log.Trajetoria.ProximoPonto.idxPonto + ")";
txtErvasNoRadar.Text = Log.Atuador.ErvasNoRadar.ToString(); txtErvasNoRadar.Text = Log.Atuador.ErvasNoRadar.ToString();
txtErvasTerreno.Text = Log.Atuador.PercentualErvasTerreno.ToString("0.00"); txtErvasTerreno.Text = Log.Atuador.PercentualErvasTerreno.ToString("0.00");
txtTipoMovimento.Text = Log.Controle.TipoMovimento.ToString(); txtTipoMovimento.Text = Log.Controle.TipoMovimento.ToString();
txtVelocidade.Text = (Log.Movimentacao.VelocidadeMedia * 3.6).ToString("0.00") + " km/h (" + Log.Movimentacao.VelocidadeMedia.ToString("0.00") + " m/s)"; txtVelocidade.Text = (Log.Movimentacao.VelocidadeMedia * 3.6).ToString("0.00") + " km/h (" + Log.Movimentacao.VelocidadeMedia.ToString("0.00") + " m/s)";
txtDirecaoControle.Text = (Log.Controle?.Direcao ?? Enums.Direcao.Parado).ToString(); txtDirecaoControle.Text = (Log.Controle?.Direcao ?? Enums.Direcao.Parado).ToString() + " (" + (Log.Controle?.Latencia ?? 0).ToString("0.00") + " s)";
txtVelocidadeControle.Text = ((Log.Controle?.PercentualVelocidadeSP ?? 0) / VariaveisEquipamento.RPM_Max_Roda * 100).ToString() + " rpm"; txtVelocidadeControle.Text = ((Log.Controle?.PercentualVelocidadeSP ?? 0) / VariaveisEquipamento.RPM_Max_Roda * 100).ToString() + " rpm";
txtErvasIdentificadas.Text = $"{totalAtuacoes}"; txtErvasIdentificadas.Text = $"{totalAtuacoes}";
@ -1170,7 +1170,7 @@ namespace AgroBase.Forms.Operacoes
txtGPS_Longitude.Text = (Log?.Longitude ?? 0).ToString(); txtGPS_Longitude.Text = (Log?.Longitude ?? 0).ToString();
txtGPS_Correcao.Text = ((Log?.Ntrip_ativado ?? false) ? "N " : "B ") + (Log?.IdadeCorrecao ?? -1) + ": " + (Log?.QualidadeFix ?? Enums.TiposCorrecaoGPS.SemCorrecao).ToString(); txtGPS_Correcao.Text = ((Log?.Ntrip_ativado ?? false) ? "N " : "B ") + (Log?.IdadeCorrecao ?? -1) + ": " + (Log?.QualidadeFix ?? Enums.TiposCorrecaoGPS.SemCorrecao).ToString();
txtGPS_Precisao.Text = (Log?.PrecisaoCm ?? 0).ToString("0.00") + " cm"; txtGPS_Precisao.Text = (Log?.PrecisaoCm ?? 0).ToString("0.00") + " cm";
txtGPS_Satelites.Text = (Log?.NumeroSatelites?.valor ?? 0).ToString(); txtGPS_Satelites.Text = (Log?.NumeroSatelites ?? 0).ToString();
txtGPS_Velocidade.Text = (Log?.Velocidade ?? 0).ToString("0.00") + " (" + ((Log?.Velocidade ?? 0) * GPSUtils.ConversaoNosKmh).ToString("0.00") + " km/h)"; txtGPS_Velocidade.Text = (Log?.Velocidade ?? 0).ToString("0.00") + " (" + ((Log?.Velocidade ?? 0) * GPSUtils.ConversaoNosKmh).ToString("0.00") + " km/h)";
var LogLra = LogsOperacao.FirstOrDefault(x => x.Momento == MomentoAtual).DadosLoRa; var LogLra = LogsOperacao.FirstOrDefault(x => x.Momento == MomentoAtual).DadosLoRa;

View File

@ -30,8 +30,8 @@
{ {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmSimulacaoMapaGPS)); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmSimulacaoMapaGPS));
this.pnlOpcoes = new System.Windows.Forms.Panel(); this.pnlOpcoes = new System.Windows.Forms.Panel();
this.label7 = new System.Windows.Forms.Label(); this.lblDelayPosicao = new System.Windows.Forms.Label();
this.txtErroTempoComando = new System.Windows.Forms.TextBox(); this.txtDelayPosicao = new System.Windows.Forms.TextBox();
this.lblCarregado = new System.Windows.Forms.Label(); this.lblCarregado = new System.Windows.Forms.Label();
this.btnCarregarTrajetoria = new System.Windows.Forms.Button(); this.btnCarregarTrajetoria = new System.Windows.Forms.Button();
this.label6 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label();
@ -46,13 +46,15 @@
this.lblKp = new System.Windows.Forms.Label(); this.lblKp = new System.Windows.Forms.Label();
this.txtKp = new System.Windows.Forms.TextBox(); this.txtKp = new System.Windows.Forms.TextBox();
this.btnReiniciar = new System.Windows.Forms.Button(); this.btnReiniciar = new System.Windows.Forms.Button();
this.lblQtdErro = new System.Windows.Forms.Label(); this.lblDelayComando = new System.Windows.Forms.Label();
this.chbAutomatico = new System.Windows.Forms.CheckBox(); this.chbAutomatico = new System.Windows.Forms.CheckBox();
this.txtQtdErro = new System.Windows.Forms.TextBox(); this.txtDelayComando = new System.Windows.Forms.TextBox();
this.btnAtualizarLeitura = new System.Windows.Forms.Button(); this.btnAtualizarLeitura = new System.Windows.Forms.Button();
this.btnCarregarMapa = new System.Windows.Forms.Button(); this.btnCarregarMapa = new System.Windows.Forms.Button();
this.btnIniciarSimulacao = new System.Windows.Forms.Button(); this.btnIniciarSimulacao = new System.Windows.Forms.Button();
this.gpbSonar = new System.Windows.Forms.GroupBox(); this.gpbSonar = new System.Windows.Forms.GroupBox();
this.label8 = new System.Windows.Forms.Label();
this.cmbStatusCorredor = new System.Windows.Forms.ComboBox();
this.chbMoverObstaculo = new System.Windows.Forms.CheckBox(); this.chbMoverObstaculo = new System.Windows.Forms.CheckBox();
this.chbSonarVirando = new System.Windows.Forms.CheckBox(); this.chbSonarVirando = new System.Windows.Forms.CheckBox();
this.btnAdicionarDeteccao = new System.Windows.Forms.Button(); this.btnAdicionarDeteccao = new System.Windows.Forms.Button();
@ -102,8 +104,6 @@
this.lblTempoRestante = new System.Windows.Forms.Label(); this.lblTempoRestante = new System.Windows.Forms.Label();
this.lblPercentualRua = new System.Windows.Forms.Label(); this.lblPercentualRua = new System.Windows.Forms.Label();
this.lblPercentualOperacao = new System.Windows.Forms.Label(); this.lblPercentualOperacao = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.cmbStatusCorredor = new System.Windows.Forms.ComboBox();
this.pnlOpcoes.SuspendLayout(); this.pnlOpcoes.SuspendLayout();
this.gpbSonar.SuspendLayout(); this.gpbSonar.SuspendLayout();
this.gpbGPS.SuspendLayout(); this.gpbGPS.SuspendLayout();
@ -112,8 +112,8 @@
// //
// pnlOpcoes // pnlOpcoes
// //
this.pnlOpcoes.Controls.Add(this.label7); this.pnlOpcoes.Controls.Add(this.lblDelayPosicao);
this.pnlOpcoes.Controls.Add(this.txtErroTempoComando); this.pnlOpcoes.Controls.Add(this.txtDelayPosicao);
this.pnlOpcoes.Controls.Add(this.lblCarregado); this.pnlOpcoes.Controls.Add(this.lblCarregado);
this.pnlOpcoes.Controls.Add(this.btnCarregarTrajetoria); this.pnlOpcoes.Controls.Add(this.btnCarregarTrajetoria);
this.pnlOpcoes.Controls.Add(this.label6); this.pnlOpcoes.Controls.Add(this.label6);
@ -128,9 +128,9 @@
this.pnlOpcoes.Controls.Add(this.lblKp); this.pnlOpcoes.Controls.Add(this.lblKp);
this.pnlOpcoes.Controls.Add(this.txtKp); this.pnlOpcoes.Controls.Add(this.txtKp);
this.pnlOpcoes.Controls.Add(this.btnReiniciar); this.pnlOpcoes.Controls.Add(this.btnReiniciar);
this.pnlOpcoes.Controls.Add(this.lblQtdErro); this.pnlOpcoes.Controls.Add(this.lblDelayComando);
this.pnlOpcoes.Controls.Add(this.chbAutomatico); this.pnlOpcoes.Controls.Add(this.chbAutomatico);
this.pnlOpcoes.Controls.Add(this.txtQtdErro); this.pnlOpcoes.Controls.Add(this.txtDelayComando);
this.pnlOpcoes.Controls.Add(this.btnAtualizarLeitura); this.pnlOpcoes.Controls.Add(this.btnAtualizarLeitura);
this.pnlOpcoes.Controls.Add(this.btnCarregarMapa); this.pnlOpcoes.Controls.Add(this.btnCarregarMapa);
this.pnlOpcoes.Controls.Add(this.btnIniciarSimulacao); this.pnlOpcoes.Controls.Add(this.btnIniciarSimulacao);
@ -143,25 +143,25 @@
this.pnlOpcoes.Size = new System.Drawing.Size(1379, 100); this.pnlOpcoes.Size = new System.Drawing.Size(1379, 100);
this.pnlOpcoes.TabIndex = 0; this.pnlOpcoes.TabIndex = 0;
// //
// label7 // lblDelayPosicao
// //
this.label7.AutoSize = true; this.lblDelayPosicao.AutoSize = true;
this.label7.Location = new System.Drawing.Point(1277, 20); this.lblDelayPosicao.Location = new System.Drawing.Point(1277, 20);
this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblDelayPosicao.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label7.Name = "label7"; this.lblDelayPosicao.Name = "lblDelayPosicao";
this.label7.Size = new System.Drawing.Size(76, 13); this.lblDelayPosicao.Size = new System.Drawing.Size(75, 13);
this.label7.TabIndex = 45; this.lblDelayPosicao.TabIndex = 45;
this.label7.Text = "Delay Controle"; this.lblDelayPosicao.Text = "Delay Posição";
// //
// txtErroTempoComando // txtDelayPosicao
// //
this.txtErroTempoComando.Location = new System.Drawing.Point(1279, 35); this.txtDelayPosicao.Location = new System.Drawing.Point(1279, 35);
this.txtErroTempoComando.Margin = new System.Windows.Forms.Padding(2); this.txtDelayPosicao.Margin = new System.Windows.Forms.Padding(2);
this.txtErroTempoComando.Name = "txtErroTempoComando"; this.txtDelayPosicao.Name = "txtDelayPosicao";
this.txtErroTempoComando.Size = new System.Drawing.Size(88, 20); this.txtDelayPosicao.Size = new System.Drawing.Size(88, 20);
this.txtErroTempoComando.TabIndex = 44; this.txtDelayPosicao.TabIndex = 44;
this.txtErroTempoComando.Text = "1"; this.txtDelayPosicao.Text = "1";
this.txtErroTempoComando.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.txtDelayPosicao.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
// //
// lblCarregado // lblCarregado
// //
@ -307,15 +307,15 @@
this.btnReiniciar.UseVisualStyleBackColor = true; this.btnReiniciar.UseVisualStyleBackColor = true;
this.btnReiniciar.Click += new System.EventHandler(this.btnReiniciar_Click); this.btnReiniciar.Click += new System.EventHandler(this.btnReiniciar_Click);
// //
// lblQtdErro // lblDelayComando
// //
this.lblQtdErro.AutoSize = true; this.lblDelayComando.AutoSize = true;
this.lblQtdErro.Location = new System.Drawing.Point(1087, 20); this.lblDelayComando.Location = new System.Drawing.Point(1087, 20);
this.lblQtdErro.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblDelayComando.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblQtdErro.Name = "lblQtdErro"; this.lblDelayComando.Name = "lblDelayComando";
this.lblQtdErro.Size = new System.Drawing.Size(45, 13); this.lblDelayComando.Size = new System.Drawing.Size(58, 13);
this.lblQtdErro.TabIndex = 29; this.lblDelayComando.TabIndex = 29;
this.lblQtdErro.Text = "Qtd erro"; this.lblDelayComando.Text = "Delay Cmd";
// //
// chbAutomatico // chbAutomatico
// //
@ -329,15 +329,15 @@
this.chbAutomatico.UseVisualStyleBackColor = true; this.chbAutomatico.UseVisualStyleBackColor = true;
this.chbAutomatico.CheckedChanged += new System.EventHandler(this.chbAutomatico_CheckedChanged); this.chbAutomatico.CheckedChanged += new System.EventHandler(this.chbAutomatico_CheckedChanged);
// //
// txtQtdErro // txtDelayComando
// //
this.txtQtdErro.Location = new System.Drawing.Point(1089, 35); this.txtDelayComando.Location = new System.Drawing.Point(1089, 35);
this.txtQtdErro.Margin = new System.Windows.Forms.Padding(2); this.txtDelayComando.Margin = new System.Windows.Forms.Padding(2);
this.txtQtdErro.Name = "txtQtdErro"; this.txtDelayComando.Name = "txtDelayComando";
this.txtQtdErro.Size = new System.Drawing.Size(54, 20); this.txtDelayComando.Size = new System.Drawing.Size(54, 20);
this.txtQtdErro.TabIndex = 28; this.txtDelayComando.TabIndex = 28;
this.txtQtdErro.Text = "1"; this.txtDelayComando.Text = "1";
this.txtQtdErro.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.txtDelayComando.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
// //
// btnAtualizarLeitura // btnAtualizarLeitura
// //
@ -394,6 +394,26 @@
this.gpbSonar.TabStop = false; this.gpbSonar.TabStop = false;
this.gpbSonar.Text = "Sonar"; this.gpbSonar.Text = "Sonar";
// //
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(117, 53);
this.label8.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(80, 13);
this.label8.TabIndex = 41;
this.label8.Text = "Status Corredor";
//
// cmbStatusCorredor
//
this.cmbStatusCorredor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbStatusCorredor.FormattingEnabled = true;
this.cmbStatusCorredor.Location = new System.Drawing.Point(121, 68);
this.cmbStatusCorredor.Name = "cmbStatusCorredor";
this.cmbStatusCorredor.Size = new System.Drawing.Size(106, 21);
this.cmbStatusCorredor.TabIndex = 40;
this.cmbStatusCorredor.SelectedIndexChanged += new System.EventHandler(this.cmbStatusCorredor_SelectedIndexChanged);
//
// chbMoverObstaculo // chbMoverObstaculo
// //
this.chbMoverObstaculo.AutoSize = true; this.chbMoverObstaculo.AutoSize = true;
@ -953,26 +973,6 @@
this.lblPercentualOperacao.TabIndex = 42; this.lblPercentualOperacao.TabIndex = 42;
this.lblPercentualOperacao.Text = "Operação: 0,00%"; this.lblPercentualOperacao.Text = "Operação: 0,00%";
// //
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(117, 53);
this.label8.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(80, 13);
this.label8.TabIndex = 41;
this.label8.Text = "Status Corredor";
//
// cmbStatusCorredor
//
this.cmbStatusCorredor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbStatusCorredor.FormattingEnabled = true;
this.cmbStatusCorredor.Location = new System.Drawing.Point(121, 68);
this.cmbStatusCorredor.Name = "cmbStatusCorredor";
this.cmbStatusCorredor.Size = new System.Drawing.Size(106, 21);
this.cmbStatusCorredor.TabIndex = 40;
this.cmbStatusCorredor.SelectedIndexChanged += new System.EventHandler(this.cmbStatusCorredor_SelectedIndexChanged);
//
// frmSimulacaoMapaGPS // frmSimulacaoMapaGPS
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1071,8 +1071,8 @@
private System.Windows.Forms.Label lblStatusOperacao; private System.Windows.Forms.Label lblStatusOperacao;
private System.Windows.Forms.Label lblTempoOperacao; private System.Windows.Forms.Label lblTempoOperacao;
private System.Windows.Forms.CheckBox chbAutomatico; private System.Windows.Forms.CheckBox chbAutomatico;
private System.Windows.Forms.Label lblQtdErro; private System.Windows.Forms.Label lblDelayComando;
private System.Windows.Forms.TextBox txtQtdErro; private System.Windows.Forms.TextBox txtDelayComando;
private System.Windows.Forms.Button btnReiniciar; private System.Windows.Forms.Button btnReiniciar;
private System.Windows.Forms.Label lblKd; private System.Windows.Forms.Label lblKd;
private System.Windows.Forms.TextBox txtKd; private System.Windows.Forms.TextBox txtKd;
@ -1091,8 +1091,8 @@
private System.Windows.Forms.TextBox txtTempoGPS; private System.Windows.Forms.TextBox txtTempoGPS;
private System.Windows.Forms.Button btnCarregarTrajetoria; private System.Windows.Forms.Button btnCarregarTrajetoria;
private System.Windows.Forms.Label lblCarregado; private System.Windows.Forms.Label lblCarregado;
private System.Windows.Forms.Label label7; private System.Windows.Forms.Label lblDelayPosicao;
private System.Windows.Forms.TextBox txtErroTempoComando; private System.Windows.Forms.TextBox txtDelayPosicao;
private System.Windows.Forms.Label label8; private System.Windows.Forms.Label label8;
private System.Windows.Forms.ComboBox cmbStatusCorredor; private System.Windows.Forms.ComboBox cmbStatusCorredor;
} }

View File

@ -26,6 +26,7 @@ namespace AgroBase.Forms
DateTime UltimaAtualizacaoGps = DateTime.MinValue; DateTime UltimaAtualizacaoGps = DateTime.MinValue;
Queue<double> historicoAngulosControle = new Queue<double>(); Queue<double> historicoAngulosControle = new Queue<double>();
GPSModel posicaoAtual = new GPSModel();
List<MPCSimulacaoModel> simulacao = new List<MPCSimulacaoModel>(); List<MPCSimulacaoModel> simulacao = new List<MPCSimulacaoModel>();
@ -67,7 +68,7 @@ namespace AgroBase.Forms
private void frmSimulacaoMapaGPS_FormClosing(object sender, FormClosingEventArgs e) private void frmSimulacaoMapaGPS_FormClosing(object sender, FormClosingEventArgs e)
{ {
tmrLeitura.Dispose(); tmrLeitura?.Dispose();
Variaveis.OperacaoEmAndamento.Iniciado = false; Variaveis.OperacaoEmAndamento.Iniciado = false;
} }
@ -196,6 +197,14 @@ namespace AgroBase.Forms
private void btnIniciarGPS_Click(object sender, EventArgs e) private void btnIniciarGPS_Click(object sender, EventArgs e)
{ {
posicaoAtual = new GPSModel()
{
DataHora = DateTime.Now,
Longitude = double.Parse(txtLongitude.Text.Replace(".", ",")),
Latitude = double.Parse(txtLatitude.Text.Replace(".", ",")),
Heartbeat = GPSService.UltimaLeitura.Heartbeat,
OrientacaoReal = double.Parse(txtAnguloGPS.Text.Replace(".", ",")),
};
AtualizarPosicaoGPS(); AtualizarPosicaoGPS();
btnAcrescentarGPS.Enabled = true; btnAcrescentarGPS.Enabled = true;
txtAnguloGPS.Enabled = true; txtAnguloGPS.Enabled = true;
@ -213,29 +222,24 @@ namespace AgroBase.Forms
Longitude = GPSService.UltimaLeitura.Longitude, Longitude = GPSService.UltimaLeitura.Longitude,
Latitude = GPSService.UltimaLeitura.Latitude, Latitude = GPSService.UltimaLeitura.Latitude,
Heartbeat = GPSService.UltimaLeitura.Heartbeat, Heartbeat = GPSService.UltimaLeitura.Heartbeat,
OrientacaoReal = GPSService.UltimaLeitura.OrientacaoReal
}; };
if (!GPSService.Iniciado || (txtLongitude.Text != "" && txtLatitude.Text != "")) if (!GPSService.Iniciado || (txtLongitude.Text != "" && txtLatitude.Text != ""))
{ {
GPSService.UltimaLeitura = new GPSModel() GPSService.UltimaLeitura = posicaoAtual;
{
DataHora = DateTime.Now,
Longitude = double.Parse(txtLongitude.Text.Replace(".", ",")),
Latitude = double.Parse(txtLatitude.Text.Replace(".", ",")),
Heartbeat = GPSService.UltimaLeitura.Heartbeat
};
} }
GPSService.AtualizarAtrasoPosicoes(Convert.ToInt32(txtDelayPosicao.Text));
GPSService.AtualizarCoordenadasGPS(); GPSService.AtualizarCoordenadasGPS();
} }
private async void btnAcrescentarGPS_Click(object sender, EventArgs e) private async void btnAcrescentarGPS_Click(object sender, EventArgs e)
{ {
GPSModel novaPosicao = new GPSModel(); GPSModel novaPosicao = null;
double velocidadeMs = FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.SimulacaoRpmControle); velocidadeCarroMs = FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.SimulacaoRpmControle);
velocidadeCarroMs = FuncoesMatematicas.ConverteMsParaKmh(velocidadeMs);
Variaveis.OperacaoEmAndamento.DispMvd.Dados.VelocidadeMedia = velocidadeMs; Variaveis.OperacaoEmAndamento.DispMvd.Dados.VelocidadeMedia = velocidadeCarroMs;
double tempoPassado = double tempoPassado =
!chbAutomatico.Checked || UltimaAtualizacaoGps == DateTime.MinValue ? // esta no modo manual !chbAutomatico.Checked || UltimaAtualizacaoGps == DateTime.MinValue ? // esta no modo manual
@ -266,10 +270,7 @@ namespace AgroBase.Forms
anguloControle = double.Parse(txtAnguloControle.Text); anguloControle = double.Parse(txtAnguloControle.Text);
double.TryParse(txtErroTempoComando.Text, out double fatorErro); bool atualizarAnguloControle = (ticksControle >= ticksControleMax);
int ticksMax = (int)(ticksControleMax * Math.Max(1, fatorErro));
bool atualizarAnguloControle = (ticksControle >= ticksMax);
if (atualizarAnguloControle) if (atualizarAnguloControle)
{ {
ticksControle = 0; ticksControle = 0;
@ -288,20 +289,23 @@ namespace AgroBase.Forms
// Calcula a nova posicao do robo // Calcula a nova posicao do robo
//GPSModel pos = NovaPosicaoSimulacao(velocidadeMs, tempoPassado); //GPSModel pos = NovaPosicaoSimulacao(velocidadeMs, tempoPassado);
if (true || novaPosicao == null) if (novaPosicao == null)
{ {
novaPosicao = VariaveisEquipamento.SimularNovaPosicao(tipoMovimento, anguloControle, velocidadeMs, tempoPassado, anguloAtual, _Gps); novaPosicao = VariaveisEquipamento.SimularNovaPosicao(tipoMovimento, anguloControle, velocidadeCarroMs, tempoPassado, anguloAtual, _Gps);
} }
posicaoAtual = novaPosicao;
//Console.WriteLine($"C#: {DadosPosicao(novaPosicao)}"); //Console.WriteLine($"C#: {DadosPosicao(novaPosicao)}");
//Console.WriteLine($"Py: {DadosPosicao(pos)}"); //Console.WriteLine($"Py: {DadosPosicao(pos)}");
// Atualiza a interface gráfica // Atualiza a interface gráfica
txtAnguloGPS.Text = novaPosicao.OrientacaoReal.ToString("0.00"); txtAnguloGPS.Text = posicaoAtual.OrientacaoReal.ToString("0.00");
txtVelocidade.Text = velocidadeCarroMs.ToString("0.00"); txtVelocidade.Text = velocidadeCarroMs.ToString("0.00");
txtDistanciaGPS.Text = novaPosicao.Distancia.ToString("0.0000"); txtDistanciaGPS.Text = posicaoAtual.Distancia.ToString("0.0000");
txtLatitude.Text = novaPosicao.Latitude.ToString(); txtLatitude.Text = posicaoAtual.Latitude.ToString();
txtLongitude.Text = novaPosicao.Longitude.ToString(); txtLongitude.Text = posicaoAtual.Longitude.ToString();
UltimaAtualizacaoGps = DateTime.Now; UltimaAtualizacaoGps = DateTime.Now;
@ -420,8 +424,10 @@ namespace AgroBase.Forms
{ {
try try
{ {
double angulo = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloCaminho; //double angulo = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloCaminho;
double anguloCorredor = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloMedioCorredor; //double anguloCorredor = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloMedioCorredor;
double angulo = Variaveis.OperacaoEmAndamento.Trajetoria.ProximoPonto.Orientacao;
double anguloCorredor = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloCaminho;
FuncoesGlobais.DesenharInclinacao((Panel)sender, e, (float)angulo, 90, (float)anguloCorredor); FuncoesGlobais.DesenharInclinacao((Panel)sender, e, (float)angulo, 90, (float)anguloCorredor);
} }
catch { } catch { }
@ -455,19 +461,14 @@ namespace AgroBase.Forms
private void btnCalcularAngulo_Click(object sender, EventArgs e) private void btnCalcularAngulo_Click(object sender, EventArgs e)
{ {
//Variaveis.OperacaoEmAndamento.OpMapaGPS.CalculaDadosMovimentacaoAutonoma(); historicoAngulosControle.Enqueue(Variaveis.OperacaoEmAndamento.SimulacaoAnguloControle);
int errosConsiderar = int.Parse(txtDelayComando.Text);
while (historicoAngulosControle.Count > 1 && (historicoAngulosControle.Count > errosConsiderar || errosConsiderar == 0))
{
historicoAngulosControle.Dequeue();
}
int errosConsiderar = int.Parse(txtQtdErro.Text); txtAnguloControle.Text = historicoAngulosControle.Peek().ToString("0.00");
double anguloControle = Variaveis.OperacaoEmAndamento.SimulacaoAnguloControle;
if (historicoAngulosControle.Count > 0 && (historicoAngulosControle.Count >= errosConsiderar || errosConsiderar == 0))
historicoAngulosControle.Dequeue(); // Remove o valor mais antigo
historicoAngulosControle.Enqueue(anguloControle);
double anguloAtrasado = errosConsiderar == 0 ? anguloControle : historicoAngulosControle.Peek(); // Valor de 5 ciclos atrás
txtAnguloControle.Text = anguloAtrasado.ToString("0.00");
} }
private void chbSonarVirando_CheckedChanged(object sender, EventArgs e) private void chbSonarVirando_CheckedChanged(object sender, EventArgs e)

View File

@ -112,7 +112,7 @@
// lblVariacaoMagnetica // lblVariacaoMagnetica
// //
this.lblVariacaoMagnetica.AutoSize = true; this.lblVariacaoMagnetica.AutoSize = true;
this.lblVariacaoMagnetica.Location = new System.Drawing.Point(768, 41); this.lblVariacaoMagnetica.Location = new System.Drawing.Point(815, 41);
this.lblVariacaoMagnetica.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblVariacaoMagnetica.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblVariacaoMagnetica.Name = "lblVariacaoMagnetica"; this.lblVariacaoMagnetica.Name = "lblVariacaoMagnetica";
this.lblVariacaoMagnetica.Size = new System.Drawing.Size(129, 13); this.lblVariacaoMagnetica.Size = new System.Drawing.Size(129, 13);
@ -122,7 +122,7 @@
// lblCursoVerdadeiro // lblCursoVerdadeiro
// //
this.lblCursoVerdadeiro.AutoSize = true; this.lblCursoVerdadeiro.AutoSize = true;
this.lblCursoVerdadeiro.Location = new System.Drawing.Point(627, 41); this.lblCursoVerdadeiro.Location = new System.Drawing.Point(674, 41);
this.lblCursoVerdadeiro.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblCursoVerdadeiro.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblCursoVerdadeiro.Name = "lblCursoVerdadeiro"; this.lblCursoVerdadeiro.Name = "lblCursoVerdadeiro";
this.lblCursoVerdadeiro.Size = new System.Drawing.Size(119, 13); this.lblCursoVerdadeiro.Size = new System.Drawing.Size(119, 13);
@ -197,7 +197,7 @@
// lblFix // lblFix
// //
this.lblFix.AutoSize = true; this.lblFix.AutoSize = true;
this.lblFix.Location = new System.Drawing.Point(499, 41); this.lblFix.Location = new System.Drawing.Point(506, 41);
this.lblFix.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblFix.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblFix.Name = "lblFix"; this.lblFix.Name = "lblFix";
this.lblFix.Size = new System.Drawing.Size(96, 13); this.lblFix.Size = new System.Drawing.Size(96, 13);
@ -225,7 +225,7 @@
// lblOrientacao // lblOrientacao
// //
this.lblOrientacao.AutoSize = true; this.lblOrientacao.AutoSize = true;
this.lblOrientacao.Location = new System.Drawing.Point(627, 17); this.lblOrientacao.Location = new System.Drawing.Point(674, 17);
this.lblOrientacao.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblOrientacao.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblOrientacao.Name = "lblOrientacao"; this.lblOrientacao.Name = "lblOrientacao";
this.lblOrientacao.Size = new System.Drawing.Size(90, 13); this.lblOrientacao.Size = new System.Drawing.Size(90, 13);
@ -255,7 +255,7 @@
// lblDatahora // lblDatahora
// //
this.lblDatahora.AutoSize = true; this.lblDatahora.AutoSize = true;
this.lblDatahora.Location = new System.Drawing.Point(768, 17); this.lblDatahora.Location = new System.Drawing.Point(815, 17);
this.lblDatahora.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblDatahora.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblDatahora.Name = "lblDatahora"; this.lblDatahora.Name = "lblDatahora";
this.lblDatahora.Size = new System.Drawing.Size(77, 13); this.lblDatahora.Size = new System.Drawing.Size(77, 13);
@ -265,7 +265,7 @@
// lblSatelites // lblSatelites
// //
this.lblSatelites.AutoSize = true; this.lblSatelites.AutoSize = true;
this.lblSatelites.Location = new System.Drawing.Point(499, 17); this.lblSatelites.Location = new System.Drawing.Point(506, 17);
this.lblSatelites.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblSatelites.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblSatelites.Name = "lblSatelites"; this.lblSatelites.Name = "lblSatelites";
this.lblSatelites.Size = new System.Drawing.Size(99, 13); this.lblSatelites.Size = new System.Drawing.Size(99, 13);

View File

@ -49,12 +49,12 @@ namespace AgroBase.Forms
txtLongitude.Text = Gps.Longitude.ToString(); txtLongitude.Text = Gps.Longitude.ToString();
lblPrecisaoHorizontal.Text = "Precisão Horizontal: " + Gps.PrecisaoHorizontal.ToString("0.00"); lblPrecisaoHorizontal.Text = "Precisão Horizontal: " + Gps.PrecisaoHorizontal.ToString("0.00");
lblPrecisaoCm.Text = "Precisão Leitura: " + Gps.PrecisaoCm.ToString("0.00") + " cm"; lblPrecisaoCm.Text = "Precisão Leitura: " + Gps.PrecisaoCm.ToString("0.00") + " cm";
lblSatelites.Text = "Satélites Visíveis: " + Gps.NumeroSatelites.valor.ToString(); lblSatelites.Text = "Satélites Visíveis: " + Gps.NumeroSatelites.ToString();
lblVelocidade.Text = "Velocidade: " + Gps.Velocidade.ToString("0.00") + " km/h"; lblVelocidade.Text = "Velocidade: " + Gps.Velocidade.ToString("0.00") + " km/h";
lblOrientacao.Text = "Orientação: " + Gps.OrientacaoReal.ToString("0.00") + "º (" + Gps.OrientacaoMovimento.ToString("0.00") + "º)"; lblOrientacao.Text = "Orientação: " + Gps.OrientacaoReal.ToString("0.00") + "º (" + Gps.OrientacaoMovimento.ToString("0.00") + "º)";
lblCursoVerdadeiro.Text = "Curso Verdadeiro: " + Gps.CursoVerdadeiro.ToString("0.00") + "º"; lblCursoVerdadeiro.Text = "Curso Verdadeiro: " + Gps.CursoVerdadeiro.ToString("0.00") + "º";
lblVariacaoMagnetica.Text = "Variação Magnética: " + Gps.VariacaoMagnetica.ToString("0.00") + "º"; lblVariacaoMagnetica.Text = "Variação Magnética: " + Gps.VariacaoMagnetica.ToString("0.00") + "º";
lblFix.Text = "Correção: " + Gps.QualidadeFix.ToString(); lblFix.Text = "Correção: " + Gps.QualidadeFix.ToString() + " (" + Gps.IdadeCorrecao.ToString("0.0") + " s)";
if (chbSalvar.Checked) if (chbSalvar.Checked)
{ {

View File

@ -10,6 +10,8 @@ namespace AgroBase.Models
public class GPSModel public class GPSModel
{ {
public DateTime Momento { get; set; } public DateTime Momento { get; set; }
public CanCommandFreqModel<double> TimestampPos { get; set; } = new CanCommandFreqModel<double>();
public CanCommandFreqModel<double> TimestampOri { get; set; } = new CanCommandFreqModel<double>();
public bool Inicializado { get; set; } public bool Inicializado { get; set; }
public bool Ntrip_ativado { get; set; } public bool Ntrip_ativado { get; set; }
public double Latitude { get; set; } public double Latitude { get; set; }
@ -19,7 +21,8 @@ namespace AgroBase.Models
public double Velocidade { get; set; } public double Velocidade { get; set; }
public double Distancia { get; set; } public double Distancia { get; set; }
public double OrientacaoMovimento { get; set; } public double OrientacaoMovimento { get; set; }
public CanCommandFreqModel<int> NumeroSatelites { get; set; } = new CanCommandFreqModel<int>(); public int NumeroSatelites { get; set; }
//public CanCommandFreqModel<int> NumeroSatelites { get; set; } = new CanCommandFreqModel<int>();
public double PrecisaoHorizontal { get; set; } public double PrecisaoHorizontal { get; set; }
public double CursoVerdadeiro { get; set; } public double CursoVerdadeiro { get; set; }
public double OrientacaoReal { get; set; } public double OrientacaoReal { get; set; }
@ -59,6 +62,8 @@ namespace AgroBase.Models
{ {
return new GPSModel() return new GPSModel()
{ {
TimestampPos = TimestampPos.Clone(),
TimestampOri = TimestampOri.Clone(),
Ntrip_ativado = Ntrip_ativado, Ntrip_ativado = Ntrip_ativado,
NumeroSatelites = NumeroSatelites, NumeroSatelites = NumeroSatelites,
Altitude = Altitude, Altitude = Altitude,

View File

@ -686,7 +686,7 @@ namespace AgroBase
var Bico = DispAtu.Dados.BicosPulverizadores.FirstOrDefault(x => x.ID == ID); var Bico = DispAtu.Dados.BicosPulverizadores.FirstOrDefault(x => x.ID == ID);
if (Bico != null) if (Bico != null)
{ {
Console.WriteLine($"Bico {Bico.Posicao} atualizado de {Bico.ComandoAtuar} para {Status}"); //Console.WriteLine($"Bico {Bico.Posicao} atualizado de {Bico.ComandoAtuar} para {Status}");
Bico.ComandoAtuar = Status; Bico.ComandoAtuar = Status;
(id_num, posicao, data) = Bico.ProtocoloComando(); (id_num, posicao, data) = Bico.ProtocoloComando();
} }
@ -755,10 +755,11 @@ namespace AgroBase
{ {
List<Keys> ComandosImpedidos = new List<Keys>(); List<Keys> ComandosImpedidos = new List<Keys>();
/*var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual; var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
if (Sonar.Iniciado && Variaveis.OperacaoEmAndamento.Controle.SonarAtivado && Sonar.Resumo.DesvioNecessario) if (Variaveis.OperacaoEmAndamento.Controle.SonarAtivado && Sonar.Iniciado && Sonar.Status == StatusModulo.Operante && (Sonar.Analises.matriz_confianca?.block?.decision?.parar ?? false))
{ {
switch (Sonar.Resumo.DirecaoDesvio) ComandosImpedidos.Add(Keys.Up);
/*switch (Sonar.Resumo.DirecaoDesvio)
{ {
case Direcao.Parado: case Direcao.Parado:
ComandosImpedidos.Add(Keys.Up); ComandosImpedidos.Add(Keys.Up);
@ -769,8 +770,8 @@ namespace AgroBase
case Direcao.Direita: case Direcao.Direita:
ComandosImpedidos.Add(Keys.Left); ComandosImpedidos.Add(Keys.Left);
break; break;
}
}*/ }*/
}
if (UltrasonicA05Service.Iniciado) if (UltrasonicA05Service.Iniciado)
{ {

View File

@ -786,7 +786,7 @@ namespace AgroBase.Models.Modules
bool AtingiuPressao() => PressaoLinha >= Variaveis.OperacaoEmAndamento.Controle.PressaoLinha; bool AtingiuPressao() => PressaoLinha >= Variaveis.OperacaoEmAndamento.Controle.PressaoLinha;
await FuncoesGlobais.AguardarCondicaoAsync(AtingiuPressao, 10000, 200); await FuncoesGlobais.AguardarCondicaoAsync(AtingiuPressao, 6000, 200);
BombaOk = AtingiuPressao(); BombaOk = AtingiuPressao();
} }

View File

@ -2958,7 +2958,7 @@ namespace AgroBase.Models.Modules
VariaveisOperacao.PosicaoBase.Inicializado = iniciado; VariaveisOperacao.PosicaoBase.Inicializado = iniciado;
VariaveisOperacao.PosicaoBase.QualidadeFix = qualidadeFix; VariaveisOperacao.PosicaoBase.QualidadeFix = qualidadeFix;
VariaveisOperacao.PosicaoBase.NumeroSatelites.valor = numeroSatelites; VariaveisOperacao.PosicaoBase.NumeroSatelites = numeroSatelites;
VariaveisOperacao.PosicaoBase.PrecisaoHorizontal = precisao; VariaveisOperacao.PosicaoBase.PrecisaoHorizontal = precisao;
VariaveisOperacao.PosicaoBase.Momento = DateTime.Now; VariaveisOperacao.PosicaoBase.Momento = DateTime.Now;
break; break;

View File

@ -727,6 +727,8 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.Finalizando = true; Variaveis.OperacaoEmAndamento.Finalizando = true;
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, false, Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombasPressurizadoras.FirstOrDefault()?.ID ?? "", true);
Variaveis.OperacaoEmAndamento.Trajetoria?.LoopAtualizaDados(); Variaveis.OperacaoEmAndamento.Trajetoria?.LoopAtualizaDados();
await Task.Delay(2000); await Task.Delay(2000);
@ -1023,12 +1025,24 @@ namespace AgroBase.Models
{ {
ControleAnterior.BicosAtuados = Controle.BicosAtuados.Select(x => x.Clone()).ToList(); ControleAnterior.BicosAtuados = Controle.BicosAtuados.Select(x => x.Clone()).ToList();
} }
bool enviarComando = false;
for (int i = 0; i < _Controle.BicosAtuados.Count; i++) for (int i = 0; i < _Controle.BicosAtuados.Count; i++)
{ {
if (ForcarEnvio || _Controle.BicosAtuados[i].ComandoAtuar != _ControleAnterior.BicosAtuados[i].ComandoAtuar) if (ForcarEnvio || _Controle.BicosAtuados[i].ComandoAtuar != _ControleAnterior.BicosAtuados[i].ComandoAtuar)
{ {
GeneralJoystick.EnviarComandoAtuador(S_Code.sBIC, _Controle.BicosAtuados[i].ComandoAtuar, _Controle.BicosAtuados[i].ID); GeneralJoystick.EnviarComandoAtuador(S_Code.sBIC, _Controle.BicosAtuados[i].ComandoAtuar, _Controle.BicosAtuados[i].ID);
_ControleAnterior.BicosAtuados[i].ComandoAtuar = _Controle.BicosAtuados[i].ComandoAtuar; _ControleAnterior.BicosAtuados[i].ComandoAtuar = _Controle.BicosAtuados[i].ComandoAtuar;
enviarComando = true;
}
}
if (enviarComando)
{
var Bomba = Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombasPressurizadoras.FirstOrDefault();
bool Ligado = Bomba.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Potencia)?.atual?.valor > 0;
if (Bomba != null && Bomba.ComandoAtuar && !Ligado)
{
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, true, Bomba.ID);
} }
} }
@ -1056,6 +1070,17 @@ namespace AgroBase.Models
{ {
Variaveis.OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas(); Variaveis.OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas();
} }
if (Variaveis.OperacaoEmAndamento.Modo == ModoOperacao.Manual)
{
var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
bool deveParar = (Sonar.Analises.matriz_confianca?.block?.decision?.parar ?? false);
bool emMovimento = Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov)?.UltimaDirecao != Direcao.Parado;
if (Variaveis.OperacaoEmAndamento.Controle.SonarAtivado && Sonar.Iniciado && Sonar.Status == StatusModulo.Operante && deveParar && emMovimento)
{
GeneralJoystick.EnviaComandoMotor(Keys.Escape, T_Code.Mov);
}
}
} }
public void ExecutaComandoDaBase(OperacaoControleBaseModel controleBase) public void ExecutaComandoDaBase(OperacaoControleBaseModel controleBase)
@ -1423,6 +1448,22 @@ namespace AgroBase.Models
nodoModulo.Nodes.Add(new TreeNode(motivo)); nodoModulo.Nodes.Add(new TreeNode(motivo));
} }
} }
if (modulo.condicoes_operacionais?.Any() ?? false)
{
// Agrupamento das condições operacionais (se houver)
if (modulo.condicoes_operacionais != null && modulo.condicoes_operacionais.Count > 0)
{
TreeNode nodoCondicoes = new TreeNode("Condições Operacionais");
foreach (var cond in modulo.condicoes_operacionais)
{
string textoCond = $"{cond.descricao} (Valor: {cond.valor}, Severidade: {cond.severidade:0}%)";
nodoCondicoes.Nodes.Add(new TreeNode(textoCond));
}
nodoModulo.Nodes.Add(nodoCondicoes);
}
}
treeView.Nodes.Add(nodoModulo); treeView.Nodes.Add(nodoModulo);
} }
@ -1554,6 +1595,7 @@ namespace AgroBase.Models
public TiposControladorDirecional TipoControleDirecional { get; set; } = TiposControladorDirecional.PID; public TiposControladorDirecional TipoControleDirecional { get; set; } = TiposControladorDirecional.PID;
public List<MPCSimulacaoModel> SimulacaoMPC { get; set; } = new List<MPCSimulacaoModel>(); public List<MPCSimulacaoModel> SimulacaoMPC { get; set; } = new List<MPCSimulacaoModel>();
public double Latencia { get; set; }
public OperacaoControleModel Clone() public OperacaoControleModel Clone()
{ {
@ -1580,7 +1622,8 @@ namespace AgroBase.Models
PulverizadorAutomatico = PulverizadorAutomatico, PulverizadorAutomatico = PulverizadorAutomatico,
MovimentoAutomatico = MovimentoAutomatico, MovimentoAutomatico = MovimentoAutomatico,
heartbeat = heartbeat, heartbeat = heartbeat,
ticks_sem_resposta = ticks_sem_resposta ticks_sem_resposta = ticks_sem_resposta,
Latencia = Latencia,
}; };
} }
} }
@ -1663,6 +1706,7 @@ namespace AgroBase.Models
public VisualWorkerModel OperadorVisual { get; set; } public VisualWorkerModel OperadorVisual { get; set; }
public WeedWorkerModel OperadorErvas { get; set; } public WeedWorkerModel OperadorErvas { get; set; }
public List<ManagerWorkerMessageResponseModulosPendentesModel> ModulosSaude { get; set; } public List<ManagerWorkerMessageResponseModulosPendentesModel> ModulosSaude { get; set; }
public bool CanSerialAtivado { get; set; }
@ -1708,6 +1752,7 @@ namespace AgroBase.Models
PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno, PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno,
HerbicidaPorErva = HerbicidaPorErva, HerbicidaPorErva = HerbicidaPorErva,
ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.ervas_no_radar ?? false, ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.ervas_no_radar ?? false,
PercentualErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.estatisticas?.erva?.frac_global ?? 0,
PercentualReservatorio = FuncoesMatematicas.Clamp(_DispAtu.Dados.PercentualReservatorio, 0, 100), PercentualReservatorio = FuncoesMatematicas.Clamp(_DispAtu.Dados.PercentualReservatorio, 0, 100),
VolumeReservatorio = _DispAtu.Dados.VolumeReservatorio, VolumeReservatorio = _DispAtu.Dados.VolumeReservatorio,
PressaoLinha = _DispAtu.Dados.PressaoLinha, PressaoLinha = _DispAtu.Dados.PressaoLinha,
@ -1772,6 +1817,7 @@ namespace AgroBase.Models
PercentualOperacao = _Trajetoria?.PercentualTrajetoria ?? 0, PercentualOperacao = _Trajetoria?.PercentualTrajetoria ?? 0,
PercentualRuaAtual = _Trajetoria?.CorredorAtual?.Progresso ?? 0, PercentualRuaAtual = _Trajetoria?.CorredorAtual?.Progresso ?? 0,
IdxRuaAtual = _Trajetoria?.CorredorAtual?.Idx ?? 0, IdxRuaAtual = _Trajetoria?.CorredorAtual?.Idx ?? 0,
QtdPontos = _Trajetoria?.TrajetoriaFixa?.Count ?? 0
}; };
var dadosGps = GPSService.UltimaLeitura.Clone(); var dadosGps = GPSService.UltimaLeitura.Clone();
@ -1800,6 +1846,7 @@ namespace AgroBase.Models
DataInicio = Variaveis.OperacaoEmAndamento.DataInicio, DataInicio = Variaveis.OperacaoEmAndamento.DataInicio,
DataFim = Variaveis.OperacaoEmAndamento.DataFim, DataFim = Variaveis.OperacaoEmAndamento.DataFim,
TempoDecorridoSeg = Variaveis.OperacaoEmAndamento.TempoDecorridoSegs, TempoDecorridoSeg = Variaveis.OperacaoEmAndamento.TempoDecorridoSegs,
CanSerialAtivado = CanManager.UseCanSerial,
IMU = dadosIMU, IMU = dadosIMU,
Atuador = dadosAtuador, Atuador = dadosAtuador,
Movimentacao = dadosMovimentacao, Movimentacao = dadosMovimentacao,
@ -1834,6 +1881,7 @@ namespace AgroBase.Models
DataInicio = DataInicio, DataInicio = DataInicio,
DataFim = DataFim, DataFim = DataFim,
TempoDecorridoSeg = TempoDecorridoSeg, TempoDecorridoSeg = TempoDecorridoSeg,
CanSerialAtivado = CanSerialAtivado,
Atuador = Atuador?.Clone(), Atuador = Atuador?.Clone(),
Movimentacao = Movimentacao?.Clone(), Movimentacao = Movimentacao?.Clone(),
Bateria = Bateria?.Clone(), Bateria = Bateria?.Clone(),
@ -1885,6 +1933,7 @@ namespace AgroBase.Models
public double PercentualErvasTerreno { get; set; } public double PercentualErvasTerreno { get; set; }
public double HerbicidaPorErva { get; set; } public double HerbicidaPorErva { get; set; }
public bool ErvasNoRadar { get; set; } public bool ErvasNoRadar { get; set; }
public double PercentualErvasNoRadar { get; set; }
public double PercentualReservatorio { get; set; } public double PercentualReservatorio { get; set; }
public double VolumeReservatorio { get; set; } public double VolumeReservatorio { get; set; }
public double PressaoLinha { get; set; } public double PressaoLinha { get; set; }
@ -1899,6 +1948,7 @@ namespace AgroBase.Models
{ {
QtdCamerasSolo = QtdCamerasSolo, QtdCamerasSolo = QtdCamerasSolo,
ErvasNoRadar = ErvasNoRadar, ErvasNoRadar = ErvasNoRadar,
PercentualErvasNoRadar = PercentualErvasNoRadar,
HerbicidaConsumido = HerbicidaConsumido, HerbicidaConsumido = HerbicidaConsumido,
HerbicidaPorErva = HerbicidaPorErva, HerbicidaPorErva = HerbicidaPorErva,
PercentualErvasTerreno = PercentualErvasTerreno, PercentualErvasTerreno = PercentualErvasTerreno,
@ -2007,6 +2057,7 @@ namespace AgroBase.Models
public int IdxRuaAtual { get; set; } public int IdxRuaAtual { get; set; }
public double PercentualOperacao { get; set; } public double PercentualOperacao { get; set; }
public double PercentualRuaAtual { get; set; } public double PercentualRuaAtual { get; set; }
public int QtdPontos { get; set; }
public OperacaoSensoriamentoLogTrajetoriaModel Clone() public OperacaoSensoriamentoLogTrajetoriaModel Clone()
{ {
@ -2038,6 +2089,7 @@ namespace AgroBase.Models
IdxRuaAtual = IdxRuaAtual, IdxRuaAtual = IdxRuaAtual,
PercentualOperacao = PercentualOperacao, PercentualOperacao = PercentualOperacao,
PercentualRuaAtual = PercentualRuaAtual, PercentualRuaAtual = PercentualRuaAtual,
QtdPontos = QtdPontos
}; };
} }
} }

View File

@ -52,6 +52,7 @@ namespace AgroBase.Models.Operadores
public TipoMovimentoDirecional tipo_movimento { get; set; } public TipoMovimentoDirecional tipo_movimento { get; set; }
public List<double[]> simulacao { get; set; } public List<double[]> simulacao { get; set; }
public int heartbeat { get; set; } public int heartbeat { get; set; }
public double latencia { get; set; }
} }
public class ManagerWorkerMessageResponseModulosPendentesModel public class ManagerWorkerMessageResponseModulosPendentesModel
@ -62,6 +63,7 @@ namespace AgroBase.Models.Operadores
public double saude { get; set; } public double saude { get; set; }
public List<string> motivos { get; set; } public List<string> motivos { get; set; }
public List<ManagerWorkerMessageResponseModulosPendentesSaudeModel> saude_individual { get; set; } public List<ManagerWorkerMessageResponseModulosPendentesSaudeModel> saude_individual { get; set; }
public List<ManagerWorkerMessageResponseModulosPendentesSaudeCondicoesModel> condicoes_operacionais { get; set; }
} }
public class ManagerWorkerMessageResponseModulosPendentesSaudeModel public class ManagerWorkerMessageResponseModulosPendentesSaudeModel

View File

@ -16,6 +16,7 @@ namespace AgroBase.Models.Operadores
public Dictionary<CameraFrameType, OAKCameraFrameModel> CameraFrames { get; set; } = new Dictionary<CameraFrameType, OAKCameraFrameModel>(); public Dictionary<CameraFrameType, OAKCameraFrameModel> CameraFrames { get; set; } = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
public VisualWorkerMessageAnaliseModel Analises { get; set; } = new VisualWorkerMessageAnaliseModel(); public VisualWorkerMessageAnaliseModel Analises { get; set; } = new VisualWorkerMessageAnaliseModel();
public VisualWorkerMessageIMUModel Imu { get; set; } = new VisualWorkerMessageIMUModel(); public VisualWorkerMessageIMUModel Imu { get; set; } = new VisualWorkerMessageIMUModel();
public StatusModulo Status { get; set; }
public VisualWorkerModel Clone() public VisualWorkerModel Clone()
{ {
@ -29,6 +30,7 @@ namespace AgroBase.Models.Operadores
Analises = Analises?.Clone(), Analises = Analises?.Clone(),
Imu = Imu?.Clone(), Imu = Imu?.Clone(),
UltimaMensagem = UltimaMensagem, UltimaMensagem = UltimaMensagem,
Status = Status
}; };
} }
@ -41,6 +43,7 @@ namespace AgroBase.Models.Operadores
CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>(); CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
Analises = new VisualWorkerMessageAnaliseModel(); Analises = new VisualWorkerMessageAnaliseModel();
Imu = new VisualWorkerMessageIMUModel(); Imu = new VisualWorkerMessageIMUModel();
Status = StatusModulo.Desconectado;
} }
} }

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using static OpenTK.Graphics.OpenGL.GL;
namespace AgroBase.Models.Operadores namespace AgroBase.Models.Operadores
{ {

View File

@ -699,13 +699,15 @@ namespace AgroBase.Models
dictParams.TryGetValue("tipo_movimento_direcional", out var _movimento); dictParams.TryGetValue("tipo_movimento_direcional", out var _movimento);
dictParams.TryGetValue("simulacao", out var _simulacao); dictParams.TryGetValue("simulacao", out var _simulacao);
dictParams.TryGetValue("heartbeat", out var _heartbeat); dictParams.TryGetValue("heartbeat", out var _heartbeat);
dictParams.TryGetValue("latencia", out var _latencia);
novoComando = new ManagerWorkerMessageResponseComandoModel() novoComando = new ManagerWorkerMessageResponseComandoModel()
{ {
angulo = Convert.ToDouble(_angulo), angulo = Convert.ToDouble(_angulo),
percentual_velocidade = Convert.ToDouble(_velocidade), percentual_velocidade = Convert.ToDouble(_velocidade),
tipo_movimento = (TipoMovimentoDirecional)Convert.ToInt32(_movimento), tipo_movimento = (TipoMovimentoDirecional)Convert.ToInt32(_movimento),
simulacao = JsonConvert.DeserializeObject<List<double[]>>((_simulacao ?? "").ToString()), simulacao = JsonConvert.DeserializeObject<List<double[]>>((_simulacao ?? "").ToString()),
heartbeat = Convert.ToInt32(_heartbeat) heartbeat = Convert.ToInt32(_heartbeat),
latencia = Convert.ToDouble(_latencia.ToString().Replace(".", ",")),
}; };
} }
catch (Exception ex) catch (Exception ex)
@ -724,6 +726,7 @@ namespace AgroBase.Models
_Controle.TipoMovimento = novoComando.tipo_movimento; _Controle.TipoMovimento = novoComando.tipo_movimento;
_Controle.PercentualVelocidadeSP = novoComando.percentual_velocidade; _Controle.PercentualVelocidadeSP = novoComando.percentual_velocidade;
_Controle.SimulacaoMPC = novoComando.simulacao?.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1], orientacao = x[2] }).ToList() ?? new List<MPCSimulacaoModel>(); _Controle.SimulacaoMPC = novoComando.simulacao?.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1], orientacao = x[2] }).ToList() ?? new List<MPCSimulacaoModel>();
_Controle.Latencia = novoComando.latencia;
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao(); Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao();
} }
@ -1075,7 +1078,7 @@ namespace AgroBase.Models
double distancia = GPSUtils.DistanciaEntrePontos(ultimoPontoTrajetoria.Posicao, PontoInicial.Posicao); double distancia = GPSUtils.DistanciaEntrePontos(ultimoPontoTrajetoria.Posicao, PontoInicial.Posicao);
bool distanciaMinima = distancia < DistanciaManobraEntreRuas; bool distanciaMinima = distancia < DistanciaManobraEntreRuas;
if (distanciaMinima) if (distanciaMinima && false)
{ {
int pontosAdicionr = Convert.ToInt32(CorredoresLarguras[idx] / (DistanciaEntrePontosCurva * _FatorLarguraCorredor)); int pontosAdicionr = Convert.ToInt32(CorredoresLarguras[idx] / (DistanciaEntrePontosCurva * _FatorLarguraCorredor));
@ -1149,7 +1152,8 @@ namespace AgroBase.Models
anguloProjetar2 += anguloAcrescentar; anguloProjetar2 += anguloAcrescentar;
anguloAcrescentar *= -1; anguloAcrescentar *= -1;
} }
GPSModel UltimoPonto = GPSUtils.ProjetarPontoDeslocado(CorredorAtual.Last(), !ultimoCorredor ? (DistanciaProjecaoRua * 1.5) : (DistanciaProjecaoRua * 2.0), anguloProjetar2); double percentualAcrescimoPontoAberturaCurva = 1.5;
GPSModel UltimoPonto = GPSUtils.ProjetarPontoDeslocado(CorredorAtual.Last(), !ultimoCorredor ? (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva) : (DistanciaProjecaoRua * 2.0), anguloProjetar2);
PontoTrajetoriaModel PontoFinal = new PontoTrajetoriaModel(TipoPontoRua.LigacaoSaida) PontoTrajetoriaModel PontoFinal = new PontoTrajetoriaModel(TipoPontoRua.LigacaoSaida)
{ {

View File

@ -18,6 +18,8 @@ using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.Core;
using System.Text.Json; using System.Text.Json;
using AgroBase.Services.Operadores; using AgroBase.Services.Operadores;
using MathNet.Numerics.Distributions;
using System.Diagnostics;
namespace AgroBase.Models namespace AgroBase.Models
{ {
@ -113,7 +115,7 @@ namespace AgroBase.Models
public static double TensaoMaximaBateria { get; set; } = 42.0; public static double TensaoMaximaBateria { get; set; } = 42.0;
public static double CorrenteMaximaBateria { get; set; } = 20.0; public static double CorrenteMaximaBateria { get; set; } = 20.0;
public static double PercentualTensaoBateriaMin { get; set; } = 25.0; public static double PercentualTensaoBateriaMin { get; set; } = 25.0;
public static double PercentualReservatorioMin { get; set; } = 5.0; public static double PercentualReservatorioMin { get; set; } = 8.0;
public static double PercentualToleranciaPressaoLinha { get; set; } = 0.15; public static double PercentualToleranciaPressaoLinha { get; set; } = 0.15;
public static double ReducaoDirecional { get; set; } = 20.0; public static double ReducaoDirecional { get; set; } = 20.0;
public static int RPM_Min_Roda { get; set; } = 15; public static int RPM_Min_Roda { get; set; } = 15;
@ -381,40 +383,41 @@ namespace AgroBase.Models
// Integração // Integração
double theta = anguloAtualDeg * Math.PI / 180.0; double theta = anguloAtualDeg * Math.PI / 180.0;
double omega = -velocidadeMs * kappaEf; // rad/s double omega = velocidadeMs * kappaEf; // rad/s
double dtheta = omega * tempoDelta; double dtheta = omega * tempoDelta;
theta += dtheta;
double moveHeading = theta; // direção de deslocamento double heading = theta;
if (tipoMovimento == TipoMovimentoDirecional.MovimentoDiagonal) if (tipoMovimento == TipoMovimentoDirecional.MovimentoDiagonal)
moveHeading = theta + dfDeg * Math.PI / 180.0; // translada na direção do steering, sem girar heading = theta + dfDeg * Math.PI / 180.0; // crab: desloca na direção do steering
double dx, dy; // >>> mesma convenção do Python: 0=Norte
if (tipoMovimento == TipoMovimentoDirecional.MovimentoDiagonal || Math.Abs(omega) < 1e-6) double dx = velocidadeMs * tempoDelta * Math.Sin(heading); // Leste(+)
{ double dy = velocidadeMs * tempoDelta * Math.Cos(heading); // Norte(+)
dx = velocidadeMs * tempoDelta * Math.Cos(moveHeading);
dy = velocidadeMs * tempoDelta * Math.Sin(moveHeading);
}
else
{
dx = (velocidadeMs / omega) * (Math.Sin(theta + dtheta) - Math.Sin(theta));
dy = (velocidadeMs / omega) * (-Math.Cos(theta + dtheta) + Math.Cos(theta));
theta += dtheta; // atualiza yaw
}
// Atualiza posição geográfica (aproximação local) // geo
double R_earth = GPSUtils.RaioDaTerra; double R_earth = GPSUtils.RaioDaTerra;
double dLat = (dy / R_earth) * 180.0 / Math.PI; double dLat = (dy / R_earth) * 180.0 / Math.PI;
double dLon = (dx / (R_earth * Math.Cos(pos.Latitude * Math.PI / 180.0))) * 180.0 / Math.PI; double dLon = (dx / (R_earth * Math.Cos(pos.Latitude * Math.PI / 180.0))) * 180.0 / Math.PI;
return new GPSModel GPSModel ultimaPosicao = GPSService.historicoPosicao.Peek();
GPSModel novaPosicao = new GPSModel
{ {
Momento = DateTime.Now, Momento = DateTime.Now,
Latitude = pos.Latitude + dLat, Latitude = pos.Latitude + dLat,
Longitude = pos.Longitude + dLon, Longitude = pos.Longitude + dLon,
OrientacaoReal = GPSUtils.NormalizarAngulo(theta * 180.0 / Math.PI), OrientacaoReal = GPSUtils.NormalizarAngulo(theta * 180.0 / Math.PI),
Distancia = velocidadeMs * tempoDelta, Distancia = velocidadeMs * tempoDelta,
Velocidade = velocidadeMs Velocidade = velocidadeMs,
Heartbeat = ultimaPosicao.Heartbeat,
TimestampOri = ultimaPosicao.TimestampOri.Clone(),
TimestampPos = ultimaPosicao.TimestampPos.Clone(),
}; };
novaPosicao.TimestampOri.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
novaPosicao.TimestampPos.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
return novaPosicao;
} }
} }

View File

@ -104,6 +104,20 @@ namespace AgroBase.Services
// Não é um número válido: ignora // Não é um número válido: ignora
} }
} }
public CanCommandFreqModel<T> Clone()
{
return new CanCommandFreqModel<T>()
{
_valor = _valor,
lidoEm = lidoEm,
frequencia = frequencia,
dt = dt,
ValorMaximo = ValorMaximo,
ValorMinimo = ValorMinimo
};
}
} }
public enum CanMessagePosicaoDados public enum CanMessagePosicaoDados
@ -143,7 +157,7 @@ namespace AgroBase.Services
public interface ICanService : IDisposable public interface ICanService : IDisposable
{ {
void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler); void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler);
bool Inicializar(SerialPort Porta = null); bool Inicializar(SerialPort Porta = null, int BitRate = 250);
void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true); void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true);
void Fechar(); void Fechar();
string _portName { get; set; } string _portName { get; set; }

View File

@ -16,6 +16,7 @@ namespace AgroBase.Services
public SerialPort _PortaCAN; public SerialPort _PortaCAN;
public string _portName { get; set; } public string _portName { get; set; }
private int _baudRate; private int _baudRate;
private int _bitRate;
private object _lock = new object(); private object _lock = new object();
public bool IsConnected { get; private set; } public bool IsConnected { get; private set; }
public bool Iniciado public bool Iniciado
@ -31,6 +32,7 @@ namespace AgroBase.Services
private ConcurrentQueue<string> FramesRecebidos = new ConcurrentQueue<string>(); private ConcurrentQueue<string> FramesRecebidos = new ConcurrentQueue<string>();
private StringBuilder SerialCANBuffer = new StringBuilder(); private StringBuilder SerialCANBuffer = new StringBuilder();
private readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>(); private readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>();
private readonly ConcurrentDictionary<byte, bool> _roteadoresRegistrados = new ConcurrentDictionary<byte, bool>();
private byte? _ultimoIdEnviado = null; private byte? _ultimoIdEnviado = null;
private List<CanMessage> Mensagens = new List<CanMessage>(); private List<CanMessage> Mensagens = new List<CanMessage>();
@ -50,14 +52,20 @@ namespace AgroBase.Services
public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler) public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
{ {
_roteadores[endereco_rx] = handler; _roteadoresRegistrados.TryGetValue(endereco_rx, out bool registrado);
if (!registrado)
{
_roteadoresRegistrados.TryAdd(endereco_rx, true);
_roteadores.TryAdd(endereco_rx, handler);
MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx); MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx);
} }
}
public bool Inicializar(SerialPort Porta = null) public bool Inicializar(SerialPort Porta = null, int BitRate = 250)
{ {
try try
{ {
_bitRate = BitRate;
if (Porta == null && _PortaCAN == null) if (Porta == null && _PortaCAN == null)
{ {
return false; return false;
@ -86,7 +94,7 @@ namespace AgroBase.Services
Thread.Sleep(50); Thread.Sleep(50);
EnviarComando("Z0\r"); // desabilita timestamp EnviarComando("Z0\r"); // desabilita timestamp
Thread.Sleep(50); Thread.Sleep(50);
EnviarComando("S6\r"); // 500 kbps EnviarComando(ComandoBitRate(_bitRate)); // 500 kbps
Thread.Sleep(50); Thread.Sleep(50);
EnviarComando("O\r"); // open EnviarComando("O\r"); // open
Thread.Sleep(50); Thread.Sleep(50);
@ -94,16 +102,19 @@ namespace AgroBase.Services
_PortaCAN.DiscardInBuffer(); _PortaCAN.DiscardInBuffer();
_PortaCAN.DiscardOutBuffer(); _PortaCAN.DiscardOutBuffer();
UltimoRx = DateTime.Now;
UltimoTx = DateTime.Now;
tmrOuvirCAN?.Dispose(); tmrOuvirCAN?.Dispose();
tmrOuvirCAN = new AsyncTaskTimerModel("tmrOuvirCAN", OuvirCAN, 5); tmrOuvirCAN = new AsyncTaskTimerModel("tmrOuvirCAN", OuvirCAN, 1);
tmrOuvirCAN.Start(); tmrOuvirCAN.Start();
tmrEnviarCAN?.Dispose(); tmrEnviarCAN?.Dispose();
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 5); tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 3);
tmrEnviarCAN.Start(); tmrEnviarCAN.Start();
tmrProcessarCAN?.Dispose(); tmrProcessarCAN?.Dispose();
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 10); tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 5);
tmrProcessarCAN.Start(); tmrProcessarCAN.Start();
IsConnected = true; IsConnected = true;
@ -126,20 +137,22 @@ namespace AgroBase.Services
private async Task ProcessarCAN() private async Task ProcessarCAN()
{ {
int mensagensParaProcessar = Math.Min(FramesRecebidos.Count, 10); const int MAX_BATCH = 256; // teto de itens por tick
const int TIME_BUDGET_US = 1000; // ~1 ms de processamento
for (int i = 0; i < mensagensParaProcessar; i++) var sw = System.Diagnostics.Stopwatch.StartNew();
{ int processed = 0;
if (FramesRecebidos.TryDequeue(out string frame))
while (processed < MAX_BATCH && FramesRecebidos.TryDequeue(out string frame))
{ {
ProcessarFrameCAN(frame); ProcessarFrameCAN(frame);
} processed++;
else
{ // Para não monopolizar o loop se entrar enxurrada de frames
// A fila ficou vazia antes de atingir o limite if (sw.ElapsedTicks * (1_000_000.0 / System.Diagnostics.Stopwatch.Frequency) > TIME_BUDGET_US)
break; break;
} }
}
LimparMensagensAntigas(); LimparMensagensAntigas();
VerificaSaudeConexao(); VerificaSaudeConexao();
} }
@ -371,31 +384,36 @@ namespace AgroBase.Services
{ {
if (_PortaCAN?.IsOpen != true) return; if (_PortaCAN?.IsOpen != true) return;
CanMessage msg = ObterProximaMensagem(); RefillTokens();
if (msg != null) // pequeno burst controlado para reduzir latência:
const int MAX_BURST = 8;
int sent = 0;
while (_tokens >= 1 && sent < MAX_BURST)
{ {
string frame = CriarFrame(msg); var msg = ObterProximaMensagem();
if (msg == null) break;
if (_ultimoIdEnviado.HasValue && msg.IdTx == _ultimoIdEnviado.Value) //if (!PodeEnviarAgora(msg)) break; // sem token “perdido”: sai do burst e tenta no próximo tick
{
await Task.Delay(5);
}
if (msg.Dispositivo == T_Code.Mks) // Evite delays arbitrários em caminho “quente”
{ // Se precisar espaçar MKS, use cooldown por-ID (abaixo)
await Task.Delay(10); if (!EnviarComando(CriarFrame(msg)))
}
if (!EnviarComando(frame))
{ {
MostrarLog("Erro ao enviar frame", msg.IdTx); MostrarLog("Erro ao enviar frame", msg.IdTx);
break; // evita apertar ainda mais se o driver falhar
} }
msg.Enviado = true; msg.Enviado = true;
msg.EnviadoEm = DateTime.Now; msg.EnviadoEm = DateTime.Now;
_ultimoIdEnviado = msg.IdTx; _ultimoIdEnviado = msg.IdTx;
_tokens -= 1;
sent++;
} }
if (sent == 0)
await Task.Delay(1); // cede o scheduler quando não tinha token/mensagem
} }
private string CriarFrame(CanMessage msg) private string CriarFrame(CanMessage msg)
@ -444,8 +462,11 @@ namespace AgroBase.Services
private void VerificaSaudeConexao() private void VerificaSaudeConexao()
{ {
if (IsConnected && (UltimoTx - UltimoRx).TotalSeconds > 2) DateTime Agora = DateTime.Now;
if (IsConnected && ((Agora - UltimoTx).TotalSeconds > 10 || (Agora - UltimoRx).TotalSeconds > 10))
{ {
UltimoRx = Agora;
UltimoTx = Agora;
MostrarLog("Muito tempo sem receber novos dados, reiniciando conexao CAN...", 0); MostrarLog("Muito tempo sem receber novos dados, reiniciando conexao CAN...", 0);
try { _PortaCAN?.Close(); } catch { } try { _PortaCAN?.Close(); } catch { }
try try
@ -453,10 +474,8 @@ namespace AgroBase.Services
_PortaCAN?.Open(); _PortaCAN?.Open();
EnviarComando("C\r"); Thread.Sleep(50); EnviarComando("C\r"); Thread.Sleep(50);
EnviarComando("Z0\r"); Thread.Sleep(50); EnviarComando("Z0\r"); Thread.Sleep(50);
EnviarComando("S6\r"); Thread.Sleep(50); EnviarComando(ComandoBitRate(_bitRate)); Thread.Sleep(50);
EnviarComando("O\r"); Thread.Sleep(50); EnviarComando("O\r"); Thread.Sleep(50);
UltimoRx = DateTime.MinValue;
UltimoTx = DateTime.MinValue;
} }
catch { } catch { }
} }
@ -514,5 +533,52 @@ namespace AgroBase.Services
return $"{Id}_{data[0]}_{parametro}"; return $"{Id}_{data[0]}_{parametro}";
} }
// alvo: ~200 fps (ajuste conforme bitrate/carga)
private const double TARGET_FPS = 200;
private double _tokens = TARGET_FPS;
private const double _capacity = TARGET_FPS;
private const double _refillPerMs = TARGET_FPS / 1000.0;
private DateTime _lastRefill = DateTime.UtcNow;
private void RefillTokens()
{
var now = DateTime.UtcNow;
var dtMs = (now - _lastRefill).TotalMilliseconds;
if (dtMs > 0)
{
_tokens = Math.Min(_capacity, _tokens + dtMs * _refillPerMs);
_lastRefill = now;
}
}
private string ComandoBitRate(int rate)
{
switch (rate)
{
case 10:
return "S0\r";
case 20:
return "S1\r";
case 50:
return "S2\r";
case 100:
return "S3\r";
case 125:
return "S4\r";
case 250:
return "S5\r";
case 500:
return "S6\r";
case 800:
return "S7\r";
case 1000:
return "S8\r";
default:
return "S6\r";
}
}
} }
} }

View File

@ -15,6 +15,7 @@ namespace AgroBase.Services
{ {
public WaveshareCANFDService Connector = new WaveshareCANFDService(0); public WaveshareCANFDService Connector = new WaveshareCANFDService(0);
public string _portName { get; set; } = "CAN-FD"; public string _portName { get; set; } = "CAN-FD";
private int _bitRate;
public bool IsConnected { get; private set; } public bool IsConnected { get; private set; }
public bool Iniciado public bool Iniciado
{ {
@ -24,11 +25,11 @@ namespace AgroBase.Services
} }
} }
private AsyncTaskTimerModel tmrEnviarCAN; private AsyncTaskTimerModel tmrEnviarCAN;
private AsyncTaskTimerModel tmrOuvirCAN;
private AsyncTaskTimerModel tmrProcessarCAN; private AsyncTaskTimerModel tmrProcessarCAN;
private StringBuilder SerialCANBuffer = new StringBuilder(); private StringBuilder SerialCANBuffer = new StringBuilder();
private ConcurrentQueue<(uint, byte[])> FramesRecebidos = new ConcurrentQueue<(uint, byte[])>(); private readonly ConcurrentQueue<(uint id, byte[] data)> FramesRecebidos = new ConcurrentQueue<(uint, byte[])>();
private readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>(); private readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>();
private readonly ConcurrentDictionary<byte, bool> _roteadoresRegistrados = new ConcurrentDictionary<byte, bool>();
private byte? _ultimoIdEnviado = null; private byte? _ultimoIdEnviado = null;
private List<CanMessage> MensagensPendentes = new List<CanMessage>(); private List<CanMessage> MensagensPendentes = new List<CanMessage>();
private readonly object _LockMensagens = new object(); private readonly object _LockMensagens = new object();
@ -46,23 +47,48 @@ namespace AgroBase.Services
public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler) public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
{ {
_roteadores[endereco_rx] = handler; _roteadoresRegistrados.TryGetValue(endereco_rx, out bool registrado);
if (!registrado)
{
_roteadoresRegistrados.TryAdd(endereco_rx, true);
_roteadores.TryAdd(endereco_rx, handler);
MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx); MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx);
} }
}
public bool Inicializar(SerialPort Porta = null) public bool Inicializar(SerialPort Porta = null, int BitRate = 250)
{ {
_bitRate = BitRate;
bool canIniciado = Connector.IsOpen; bool canIniciado = Connector.IsOpen;
if (!canIniciado) if (!canIniciado)
{ {
canIniciado = Connector.Open((VCI_CAN_OBJ frame) => canIniciado = Connector.Open((VCI_CAN_OBJ[] frames) =>
{ {
byte[] dataRx = new byte[frame.DataLen]; try
Array.Copy(frame.Data, dataRx, frame.DataLen); {
FramesRecebidos.Enqueue((frame.ID, dataRx)); var now = DateTime.Now;
UltimoRx = DateTime.Now; for (int i = 0; i < frames.Length; i++)
}, samplePoint: 0.8, bitrateKbps: 250); {
var f = frames[i];
var dlc = f.DataLen;
if (dlc < 0) dlc = 0; if (dlc > 8) dlc = 8;
// clone rápido (já vem deep do conector, mas garantimos)
var data = dlc > 0 ? (byte[])f.Data.Clone() : Array.Empty<byte>();
// nunca bloquear. se quiser cap, faça:
FramesRecebidos.Enqueue((f.ID, data));
while (FramesRecebidos.Count > 4096) // cap opcional
FramesRecebidos.TryDequeue(out _);
}
UltimoRx = now;
}
catch (Exception ex)
{
MostrarLog($"[CAN] ERRO no callback consumidor: {ex.Message}", 0);
}
}, bitrateKbps: _bitRate, samplePoint: 0.8);
} }
else else
{ {
@ -80,12 +106,15 @@ namespace AgroBase.Services
if (canIniciado || !IsConnected) if (canIniciado || !IsConnected)
{ {
UltimoRx = DateTime.Now;
UltimoTx = DateTime.Now;
tmrEnviarCAN?.Dispose(); tmrEnviarCAN?.Dispose();
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 5); tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 3);
tmrEnviarCAN.Start(); tmrEnviarCAN.Start();
tmrProcessarCAN?.Dispose(); tmrProcessarCAN?.Dispose();
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 10); tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 5);
tmrProcessarCAN.Start(); tmrProcessarCAN.Start();
IsConnected = true; IsConnected = true;
@ -96,20 +125,22 @@ namespace AgroBase.Services
private async Task ProcessarCAN() private async Task ProcessarCAN()
{ {
int mensagensParaProcessar = Math.Min(FramesRecebidos.Count, 10); const int MAX_BATCH = 256; // teto de itens por tick
const int TIME_BUDGET_US = 1000; // ~1 ms de processamento
for (int i = 0; i < mensagensParaProcessar; i++) var sw = System.Diagnostics.Stopwatch.StartNew();
{ int processed = 0;
if (FramesRecebidos.TryDequeue(out (uint, byte[]) frame))
while (processed < MAX_BATCH && FramesRecebidos.TryDequeue(out var frame))
{ {
ProcessarFrameCAN(frame); ProcessarFrameCAN(frame);
} processed++;
else
{ // Para não monopolizar o loop se entrar enxurrada de frames
// A fila ficou vazia antes de atingir o limite if (sw.ElapsedTicks * (1_000_000.0 / System.Diagnostics.Stopwatch.Frequency) > TIME_BUDGET_US)
break; break;
} }
}
LimparMensagensAntigas(); LimparMensagensAntigas();
VerificaSaudeConexao(); VerificaSaudeConexao();
} }
@ -125,10 +156,7 @@ namespace AgroBase.Services
{ {
MostrarLog($"Processando frame: {BitConverter.ToString(data)}", (byte)id); MostrarLog($"Processando frame: {BitConverter.ToString(data)}", (byte)id);
if (_roteadores.TryGetValue((byte)id, out ICanMessageHandler handler)) _roteadores.TryGetValue((byte)id, out ICanMessageHandler handler);
{
}
T_Code dispositivo = handler?.Dispositivo ?? T_Code.Vzo; T_Code dispositivo = handler?.Dispositivo ?? T_Code.Vzo;
byte fCodeRx = data[0]; byte fCodeRx = data[0];
@ -260,35 +288,56 @@ namespace AgroBase.Services
return proximaMsg; return proximaMsg;
} }
private readonly ConcurrentDictionary<byte, DateTime> _cooldownPorId = new ConcurrentDictionary<byte, DateTime>();
private static readonly TimeSpan COOLDOWN_MKS = TimeSpan.FromMilliseconds(6);
private bool PodeEnviarAgora(CanMessage m)
{
// Exemplo: só MKS precisa de respiro
if (m.Dispositivo != T_Code.Mks) return true;
var now = DateTime.UtcNow;
var due = _cooldownPorId.GetOrAdd(m.IdTx, DateTime.MinValue);
if (now < due) return false;
_cooldownPorId[m.IdTx] = now + COOLDOWN_MKS;
return true;
}
private async Task EnviarCAN() private async Task EnviarCAN()
{ {
if (!Connector.IsOpen) return; if (!Connector.IsOpen) return;
CanMessage msg = ObterProximaMensagem(); RefillTokens();
if (msg != null) // pequeno burst controlado para reduzir latência:
const int MAX_BURST = 8;
int sent = 0;
while (_tokens >= 1 && sent < MAX_BURST)
{ {
string frame = CriarFrame(msg); var msg = ObterProximaMensagem();
if (msg == null) break;
if (_ultimoIdEnviado.HasValue && msg.IdTx == _ultimoIdEnviado.Value) //if (!PodeEnviarAgora(msg)) break; // sem token “perdido”: sai do burst e tenta no próximo tick
{
await Task.Delay(5);
}
if (msg.Dispositivo == T_Code.Mks) // Evite delays arbitrários em caminho “quente”
{ // Se precisar espaçar MKS, use cooldown por-ID (abaixo)
await Task.Delay(10); if (!EnviarComando(CriarFrame(msg), msg))
}
if (!EnviarComando(frame, msg))
{ {
MostrarLog("Erro ao enviar frame", msg.IdTx); MostrarLog("Erro ao enviar frame", msg.IdTx);
break; // evita apertar ainda mais se o driver falhar
} }
msg.Enviado = true; msg.Enviado = true;
msg.EnviadoEm = DateTime.Now; msg.EnviadoEm = DateTime.Now;
_ultimoIdEnviado = msg.IdTx; _ultimoIdEnviado = msg.IdTx;
_tokens -= 1;
sent++;
} }
if (sent == 0)
await Task.Delay(1); // cede o scheduler quando não tinha token/mensagem
} }
private string CriarFrame(CanMessage msg) private string CriarFrame(CanMessage msg)
@ -333,7 +382,7 @@ namespace AgroBase.Services
.Where(x => .Where(x =>
(!x.ComResposta && x.Enviado) || (!x.ComResposta && x.Enviado) ||
(x.ComResposta && x.Respondido) || (x.ComResposta && x.Respondido) ||
(x.ComResposta && x.Enviado && !x.Respondido && x.EnviadoEm < agora.AddSeconds(-1)) || (x.ComResposta && x.Enviado && !x.Respondido && x.EnviadoEm < agora.AddSeconds(-2)) ||
(x.Momento < agora.AddSeconds(-30)) (x.Momento < agora.AddSeconds(-30))
) )
.ToList(); .ToList();
@ -346,15 +395,16 @@ namespace AgroBase.Services
private void VerificaSaudeConexao() private void VerificaSaudeConexao()
{ {
if (IsConnected && (UltimoTx - UltimoRx).TotalSeconds > 10) DateTime Agora = DateTime.Now;
if (IsConnected && ((Agora - UltimoTx).TotalSeconds > 10 || (Agora - UltimoRx).TotalSeconds > 10))
{ {
UltimoRx = Agora;
UltimoTx = Agora;
MostrarLog("Muito tempo sem receber novos dados, reiniciando conexao CAN...", 0); MostrarLog("Muito tempo sem receber novos dados, reiniciando conexao CAN...", 0);
try { Connector.Close(); } catch { } try { Connector.Close(); } catch { }
try try
{ {
Inicializar(); Inicializar();
UltimoRx = DateTime.MinValue;
UltimoTx = DateTime.MinValue;
} }
catch { } catch { }
} }
@ -362,7 +412,6 @@ namespace AgroBase.Services
public void Fechar() public void Fechar()
{ {
tmrOuvirCAN?.Dispose();
tmrEnviarCAN?.Dispose(); tmrEnviarCAN?.Dispose();
lock (_LockMensagens) lock (_LockMensagens)
@ -371,7 +420,10 @@ namespace AgroBase.Services
} }
SerialCANBuffer.Clear(); SerialCANBuffer.Clear();
FramesRecebidos = new ConcurrentQueue<(uint, byte[])>(); while (FramesRecebidos.Count > 0)
{
FramesRecebidos.TryDequeue(out var frame);
}
_roteadores.Clear(); _roteadores.Clear();
_ultimoIdEnviado = null; _ultimoIdEnviado = null;
@ -405,5 +457,26 @@ namespace AgroBase.Services
byte parametro = (data.Length > 1) ? data[1] : (byte)0x00; byte parametro = (data.Length > 1) ? data[1] : (byte)0x00;
return $"{Id}_{data[0]}_{parametro}"; return $"{Id}_{data[0]}_{parametro}";
} }
// alvo: ~200 fps (ajuste conforme bitrate/carga)
private const double TARGET_FPS = 200;
private double _tokens = TARGET_FPS;
private const double _capacity = TARGET_FPS;
private const double _refillPerMs = TARGET_FPS / 1000.0;
private DateTime _lastRefill = DateTime.UtcNow;
private void RefillTokens()
{
var now = DateTime.UtcNow;
var dtMs = (now - _lastRefill).TotalMilliseconds;
if (dtMs > 0)
{
_tokens = Math.Min(_capacity, _tokens + dtMs * _refillPerMs);
_lastRefill = now;
}
}
} }
} }

View File

@ -2,6 +2,7 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.IO.Ports; using System.IO.Ports;
@ -44,8 +45,9 @@ namespace AgroBase.Services
private static int rtk_timeout = 60; private static int rtk_timeout = 60;
private static int TempoMin_Ntrip = 10; private static int TempoMin_Ntrip = 10;
private static bool LoopRTK_Ntrip = false; private static bool LoopRTK_Ntrip = false;
public static bool CorrecaoRTK_Ntrip = true; public static bool CorrecaoRTK_Ntrip = false;
public static DateTime UltimoEnvioCorrecaoRTK = DateTime.MinValue; public static DateTime UltimoEnvioCorrecaoRTK = DateTime.MinValue;
private static AsyncTaskTimerModel tmrHeartbeat;
public static void AtualizarPortaCOM(SerialPort Porta) public static void AtualizarPortaCOM(SerialPort Porta)
{ {
@ -75,6 +77,9 @@ namespace AgroBase.Services
{ {
Task.Run(async () => await AplicarCorrecaoRTK_Ntrip()); Task.Run(async () => await AplicarCorrecaoRTK_Ntrip());
} }
tmrHeartbeat?.Dispose();
tmrHeartbeat = new AsyncTaskTimerModel("tmrHeartbeatGps", tmrHeartbeat_Tick, 500);
tmrHeartbeat.Start();
} }
} }
@ -103,7 +108,7 @@ namespace AgroBase.Services
} }
} }
private static async Task ConfigurarModuloRover(string porta_usb = "com3", string porta_entrada = "com2", int comprimento_antena = 130, int tolerancia_antena = 3) private static async Task ConfigurarModuloRover(string porta_usb = "com3", string porta_entrada = "com2", int comprimento_antena = 130, int tolerancia_antena = 5)
{ {
string freq = (1.0 / TaxaAmostragemHz).ToString("0.0").Replace(",", "."); string freq = (1.0 / TaxaAmostragemHz).ToString("0.0").Replace(",", ".");
string[] comandos = { string[] comandos = {
@ -126,6 +131,7 @@ namespace AgroBase.Services
// heading 2 antenas // heading 2 antenas
$"config heading fixlength\r\n", $"config heading fixlength\r\n",
$"config heading tractor\r\n",
$"config heading length {comprimento_antena} {tolerancia_antena}\r\n", $"config heading length {comprimento_antena} {tolerancia_antena}\r\n",
// (se precisar, existe 'config heading offset <azim_off> <pitch_off>') // (se precisar, existe 'config heading offset <azim_off> <pitch_off>')
@ -262,7 +268,7 @@ namespace AgroBase.Services
AtualizarCoordenadasGPS(); AtualizarCoordenadasGPS();
} }
// Coordenadas // Coordenadas
else if (sentenca.StartsWith("$GNGGA")) else if (sentenca.StartsWith("$GNGGA") || sentenca.StartsWith("$GLGGA"))
{ {
ProcessarGNGGA(sentenca); ProcessarGNGGA(sentenca);
AtualizarCoordenadasGPS(); AtualizarCoordenadasGPS();
@ -283,9 +289,10 @@ namespace AgroBase.Services
ProcessarGSV(sentenca); ProcessarGSV(sentenca);
} }
// Orientação real // Orientação real
else if (sentenca.StartsWith("$GNTHS") || sentenca.StartsWith("$GPTHS")) else if (sentenca.StartsWith("$GNTHS") || sentenca.StartsWith("$GPTHS") || sentenca.StartsWith("$GATHS"))
{ {
ProcessarGNTHS(sentenca); ProcessarGNTHS(sentenca);
//AtualizarCoordenadasGPS();
} }
// GLL (GP/GN/GL/GA/BD) // GLL (GP/GN/GL/GA/BD)
else if (sentenca.Length > 6 && sentenca[3] == 'G' && sentenca[4] == 'L' && sentenca[5] == 'L') else if (sentenca.Length > 6 && sentenca[3] == 'G' && sentenca[4] == 'L' && sentenca[5] == 'L')
@ -315,13 +322,17 @@ namespace AgroBase.Services
{ {
string[] parts = sentenca.Split(','); string[] parts = sentenca.Split(',');
PenultimaLeitura.Momento = UltimaLeitura.Momento;
PenultimaLeitura.TimestampPos = UltimaLeitura.TimestampPos.Clone();
PenultimaLeitura.Latitude = UltimaLeitura.Latitude; PenultimaLeitura.Latitude = UltimaLeitura.Latitude;
PenultimaLeitura.Longitude = UltimaLeitura.Longitude; PenultimaLeitura.Longitude = UltimaLeitura.Longitude;
PenultimaLeitura.Altitude = UltimaLeitura.Altitude; PenultimaLeitura.Altitude = UltimaLeitura.Altitude;
PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal; PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal;
PenultimaLeitura.DataHora = UltimaLeitura.DataHora; PenultimaLeitura.DataHora = UltimaLeitura.DataHora;
PenultimaLeitura.NumeroSatelites.valor = UltimaLeitura.NumeroSatelites.valor; PenultimaLeitura.NumeroSatelites = UltimaLeitura.NumeroSatelites;
UltimaLeitura.TimestampPos.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
UltimaLeitura.Momento = DateTime.Now;
if (parts[2] != "" && parts[3] != "") if (parts[2] != "" && parts[3] != "")
{ {
UltimaLeitura.Latitude = GPSUtils.ConvertToDecimalDegrees(parts[2], parts[3], 2); UltimaLeitura.Latitude = GPSUtils.ConvertToDecimalDegrees(parts[2], parts[3], 2);
@ -330,7 +341,7 @@ namespace AgroBase.Services
{ {
UltimaLeitura.Longitude = GPSUtils.ConvertToDecimalDegrees(parts[4], parts[5], 3); UltimaLeitura.Longitude = GPSUtils.ConvertToDecimalDegrees(parts[4], parts[5], 3);
} }
UltimaLeitura.NumeroSatelites.valor = int.TryParse(parts[7], out int numSat) ? numSat : 0; UltimaLeitura.NumeroSatelites = int.TryParse(parts[7], out int numSat) ? numSat : 0;
UltimaLeitura.PrecisaoHorizontal = double.TryParse(parts[8], NumberStyles.Float, CultureInfo.InvariantCulture, out double hdop) ? hdop : 0; UltimaLeitura.PrecisaoHorizontal = double.TryParse(parts[8], NumberStyles.Float, CultureInfo.InvariantCulture, out double hdop) ? hdop : 0;
UltimaLeitura.Altitude = double.TryParse(parts[9], NumberStyles.Float, CultureInfo.InvariantCulture, out double alt) ? alt : 0; UltimaLeitura.Altitude = double.TryParse(parts[9], NumberStyles.Float, CultureInfo.InvariantCulture, out double alt) ? alt : 0;
} }
@ -384,23 +395,25 @@ namespace AgroBase.Services
//Console.WriteLine($"GNGGA: Hora={horaUTC}, Latitude={latitude}, Longitude={longitude}, Qualidade={qualidade}, Satélites={satelitesUsados}, HDOP={hdop}, Altitude={altitude}"); //Console.WriteLine($"GNGGA: Hora={horaUTC}, Latitude={latitude}, Longitude={longitude}, Qualidade={qualidade}, Satélites={satelitesUsados}, HDOP={hdop}, Altitude={altitude}");
PenultimaLeitura.TimestampPos = UltimaLeitura.TimestampPos.Clone();
PenultimaLeitura.Momento = UltimaLeitura.Momento; PenultimaLeitura.Momento = UltimaLeitura.Momento;
PenultimaLeitura.Latitude = UltimaLeitura.Latitude; PenultimaLeitura.Latitude = UltimaLeitura.Latitude;
PenultimaLeitura.Longitude = UltimaLeitura.Longitude; PenultimaLeitura.Longitude = UltimaLeitura.Longitude;
PenultimaLeitura.Altitude = UltimaLeitura.Altitude; PenultimaLeitura.Altitude = UltimaLeitura.Altitude;
PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal; PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal;
PenultimaLeitura.NumeroSatelites.valor = UltimaLeitura.NumeroSatelites.valor; PenultimaLeitura.NumeroSatelites = UltimaLeitura.NumeroSatelites;
PenultimaLeitura.QualidadeFix = UltimaLeitura.QualidadeFix; PenultimaLeitura.QualidadeFix = UltimaLeitura.QualidadeFix;
PenultimaLeitura.DataHora = UltimaLeitura.DataHora; PenultimaLeitura.DataHora = UltimaLeitura.DataHora;
PenultimaLeitura.IdadeCorrecao = UltimaLeitura.IdadeCorrecao; PenultimaLeitura.IdadeCorrecao = UltimaLeitura.IdadeCorrecao;
// Armazenar os valores na última leitura // Armazenar os valores na última leitura
UltimaLeitura.TimestampPos.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
UltimaLeitura.Momento = DateTime.Now; UltimaLeitura.Momento = DateTime.Now;
UltimaLeitura.Latitude = latitude; UltimaLeitura.Latitude = latitude;
UltimaLeitura.Longitude = longitude; UltimaLeitura.Longitude = longitude;
UltimaLeitura.Altitude = altitude; UltimaLeitura.Altitude = altitude;
UltimaLeitura.PrecisaoHorizontal = precisao; UltimaLeitura.PrecisaoHorizontal = precisao;
UltimaLeitura.NumeroSatelites.valor = nsatelites; UltimaLeitura.NumeroSatelites = nsatelites;
UltimaLeitura.QualidadeFix = (TiposCorrecaoGPS)fix; UltimaLeitura.QualidadeFix = (TiposCorrecaoGPS)fix;
UltimaLeitura.IdadeCorrecao = string.IsNullOrEmpty(idadeCorrecaoRaw) ? -1 : idadeCorrecao; UltimaLeitura.IdadeCorrecao = string.IsNullOrEmpty(idadeCorrecaoRaw) ? -1 : idadeCorrecao;
@ -537,6 +550,7 @@ namespace AgroBase.Services
private static void ProcessarGNTHS(string sentenca) private static void ProcessarGNTHS(string sentenca)
{ {
//Console.WriteLine(sentenca);
// Remove o caractere de início '$' e divide os campos // Remove o caractere de início '$' e divide os campos
var campos = sentenca.TrimStart('$').Split(','); var campos = sentenca.TrimStart('$').Split(',');
@ -558,10 +572,12 @@ namespace AgroBase.Services
string status = campos[2].Split('*')[0]; // Campo <Status> string status = campos[2].Split('*')[0]; // Campo <Status>
string checksum = campos[2].Split('*')[1]; // Campo <Checksum> string checksum = campos[2].Split('*')[1]; // Campo <Checksum>
PenultimaLeitura.TimestampOri = UltimaLeitura.TimestampOri.Clone();
PenultimaLeitura.Momento = UltimaLeitura.Momento; PenultimaLeitura.Momento = UltimaLeitura.Momento;
PenultimaLeitura.OrientacaoReal = UltimaLeitura.OrientacaoReal; PenultimaLeitura.OrientacaoReal = UltimaLeitura.OrientacaoReal;
PenultimaLeitura.TipoOrientacao = UltimaLeitura.TipoOrientacao; PenultimaLeitura.TipoOrientacao = UltimaLeitura.TipoOrientacao;
UltimaLeitura.TimestampOri.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
UltimaLeitura.Momento = DateTime.Now; UltimaLeitura.Momento = DateTime.Now;
UltimaLeitura.OrientacaoReal = headingTrue; UltimaLeitura.OrientacaoReal = headingTrue;
UltimaLeitura.TipoOrientacao = status; UltimaLeitura.TipoOrientacao = status;
@ -661,7 +677,7 @@ namespace AgroBase.Services
// Atualiza apenas o que faz sentido complementar // Atualiza apenas o que faz sentido complementar
if (!double.IsInfinity(hdop) && !double.IsNaN(hdop)) UltimaLeitura.PrecisaoHorizontal = hdop; if (!double.IsInfinity(hdop) && !double.IsNaN(hdop)) UltimaLeitura.PrecisaoHorizontal = hdop;
if (satsUsados > 0) UltimaLeitura.NumeroSatelites.valor = Math.Max(UltimaLeitura.NumeroSatelites.valor, satsUsados); if (satsUsados > 0) UltimaLeitura.NumeroSatelites = Math.Max(UltimaLeitura.NumeroSatelites, satsUsados);
// Se você quiser guardar os DOPs: // Se você quiser guardar os DOPs:
UltimaLeitura.PDOP = !double.IsInfinity(pdop) && !double.IsNaN(pdop) ? pdop : UltimaLeitura.PDOP; UltimaLeitura.PDOP = !double.IsInfinity(pdop) && !double.IsNaN(pdop) ? pdop : UltimaLeitura.PDOP;
@ -1083,7 +1099,7 @@ namespace AgroBase.Services
if (Variaveis.OperacaoEmAndamento.Simulando) if (Variaveis.OperacaoEmAndamento.Simulando)
{ {
anguloFinal = UltimaLeitura.OrientacaoMovimento; anguloFinal = UltimaLeitura.OrientacaoReal;
} }
else if (gpsIniciado) else if (gpsIniciado)
{ {
@ -1194,25 +1210,47 @@ namespace AgroBase.Services
return anguloUnificado; return anguloUnificado;
} }
private static async Task tmrHeartbeat_Tick()
{
AtualizaDadosRedis();
}
private static void AtualizaDadosRedis() private static void AtualizaDadosRedis()
{ {
GPSModel posicaoAtual = Variaveis.OperacaoEmAndamento.Simulando ? historicoPosicao.Peek() : UltimaLeitura;
RedisService.AtualizarCampos( RedisService.AtualizarCampos(
RedisService.ModKey(T_Code.Gps), RedisService.ModKey(T_Code.Gps),
("timestamp", UltimaLeitura.Momento),
("conectado", Iniciado), ("conectado", Iniciado),
("freq_base", TaxaAmostragemHz), ("freq_base", TaxaAmostragemHz),
("lat", UltimaLeitura.Latitude), ("lat", posicaoAtual.Latitude),
("lon", UltimaLeitura.Longitude), ("lon", posicaoAtual.Longitude),
("theta", UltimaLeitura.AnguloCarroDefinido), ("theta", posicaoAtual.AnguloCarroDefinido),
("fix", (int)UltimaLeitura.QualidadeFix), ("fix", (int)posicaoAtual.QualidadeFix),
("rtk", UltimaLeitura.IdadeCorrecao < rtk_timeout), ("rtk", posicaoAtual.IdadeCorrecao < rtk_timeout),
("hAcc", UltimaLeitura.PrecisaoCm), ("hAcc", posicaoAtual.PrecisaoCm),
("nSatelites", UltimaLeitura.NumeroSatelites.valor), ("nSatelites", posicaoAtual.NumeroSatelites),
("freq", UltimaLeitura.NumeroSatelites.frequencia), ("freq.posicao", posicaoAtual.TimestampPos.frequencia),
("age", UltimaLeitura.IdadeCorrecao), ("freq.orientacao", posicaoAtual.TimestampOri.frequencia),
("heartbeat", UltimaLeitura.Heartbeat) ("latency.posicao", posicaoAtual.TimestampPos.dt),
("latency.orientacao", posicaoAtual.TimestampOri.dt),
("timestamp.posicao", posicaoAtual.TimestampPos.valor),
("timestamp.orientacao", posicaoAtual.TimestampOri.valor),
("age", posicaoAtual.IdadeCorrecao),
("heartbeat", posicaoAtual.Heartbeat)
); );
} }
public static Queue<GPSModel> historicoPosicao = new Queue<GPSModel>();
public static void AtualizarAtrasoPosicoes(int errosConsiderar = 0)
{
historicoPosicao.Enqueue(UltimaLeitura);
while (historicoPosicao.Count > 1 && (historicoPosicao.Count > errosConsiderar || errosConsiderar == 0))
{
historicoPosicao.Dequeue();
}
}
} }
} }

View File

@ -248,7 +248,7 @@ namespace AgroBase.Services
fix.AddRange(CanPrefix(DadosLoRaParse.GpsQualidade)); fix.AddRange(CanPrefix(DadosLoRaParse.GpsQualidade));
fix.Add((byte)(Gps.Inicializado ? 1 : 0)); // 1 byte fix.Add((byte)(Gps.Inicializado ? 1 : 0)); // 1 byte
fix.Add((byte)Gps.QualidadeFix); // 1 byte fix.Add((byte)Gps.QualidadeFix); // 1 byte
fix.Add((byte)Gps.NumeroSatelites.valor); // 1 byte fix.Add((byte)Gps.NumeroSatelites); // 1 byte
fix.AddRange(CompactarDecimal(Gps.PrecisaoHorizontal)); // 2 bytes fix.AddRange(CompactarDecimal(Gps.PrecisaoHorizontal)); // 2 bytes
List<byte> _lat = new List<byte>(); List<byte> _lat = new List<byte>();

View File

@ -114,33 +114,44 @@ namespace AgroBase.Services.Operadores
return; return;
} }
var _t = _Sensoriamento.Trajetoria;
PontoTrajetoriaModel pontoComparar = _t != null ? (_t.PontoAtual.Aproximando ? _t.PontoAtual : _t.ProximoPonto != null ? _t.ProximoPonto : _t.PontoAtual) : null;
int idx_p_alvo = _t != null ?
(pontoComparar.DistanciaAtual > 1.0 ? pontoComparar.idxPonto :
pontoComparar.idxPonto < _t.QtdPontos - 1 ? pontoComparar.idxPonto + 1 :
pontoComparar.idxPonto) : 0;
RedisService.AtualizarCampos( RedisService.AtualizarCampos(
CtxKey.DadosContexto, CtxKey.DadosContexto,
("Trajetoria", _Sensoriamento.Trajetoria != null ? new ("Trajetoria", _t != null ? new
{ {
status = (int)_Sensoriamento.Trajetoria.StatusCarro, status = (int)_t.StatusCarro,
manobrando = _Sensoriamento.Trajetoria.ManobrandoEntreRuas, manobrando = _t.ManobrandoEntreRuas,
erro_angular = _Sensoriamento.Trajetoria.ErroOrientacaoAngularCombinado, erro_angular = _t.ErroOrientacaoAngularCombinado,
erro_angular_caminho = _Sensoriamento.Trajetoria.ErroOrientacaoAngularCaminho, erro_angular_caminho = _t.ErroOrientacaoAngularCaminho,
erro_angular_proximo_ponto = _Sensoriamento.Trajetoria.ErroOrientacaoAngularProximoPonto, erro_angular_proximo_ponto = _t.ErroOrientacaoAngularProximoPonto,
idx_proximo_ponto = _Sensoriamento.Trajetoria.ProximoPonto.idxPonto, idx_proximo_ponto = _t.ProximoPonto.idxPonto,
idx_ponto_alvo = idx_p_alvo,
CorredorAtual = new CorredorAtual = new
{ {
concluido = _Sensoriamento.Trajetoria.CorredorAtual.Concluido, concluido = _t.CorredorAtual.Concluido,
dentro = _Sensoriamento.Trajetoria.CorredorAtual.Dentro, dentro = _t.CorredorAtual.Dentro,
ultimo = _Sensoriamento.Trajetoria.CorredorAtual.Ultimo, ultimo = _t.CorredorAtual.Ultimo,
largura = _Sensoriamento.Trajetoria.CorredorAtual.Largura, largura = _t.CorredorAtual.Largura,
progresso = _Sensoriamento.Trajetoria.CorredorAtual.Progresso, progresso = _t.CorredorAtual.Progresso,
dist_esq = _Sensoriamento.Trajetoria.DistanciaEsquerda, dist_esq = _t.DistanciaEsquerda,
dist_dir = _Sensoriamento.Trajetoria.DistanciaDireita, dist_dir = _t.DistanciaDireita,
angulo = _Sensoriamento.Trajetoria.AnguloCaminho, angulo = _t.AnguloMedioCorredor,
}, },
} : null } : null
), ),
("Gerais", new ("Gerais", new
{ {
velocidade_ms = _Sensoriamento.Movimentacao?.VelocidadeMedia ?? 0, //velocidade_ms = _Sensoriamento.Movimentacao?.VelocidadeMedia ?? 0,
velocidade_ms = FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.Controle.PercentualVelocidadeSP),
ervas_no_radar = _Sensoriamento.Atuador?.ErvasNoRadar ?? false, ervas_no_radar = _Sensoriamento.Atuador?.ErvasNoRadar ?? false,
percentual_ervas_no_radar = _Sensoriamento.Atuador?.PercentualErvasNoRadar ?? 0,
percentual_reservatorio = _Sensoriamento.Atuador?.PercentualReservatorio ?? 0,
percentual_bateria = _Sensoriamento.Bateria?.PorcentagemBateria ?? 0,
} }
) )
); );
@ -423,7 +434,7 @@ namespace AgroBase.Services.Operadores
int id = sensor.ID_Num; int id = sensor.ID_Num;
string basePath = $"sensores.{tipo}.{id}"; string basePath = $"sensores.{tipo}.{id}";
var saude = RedisService.GetField<object>(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); var saude = RedisService.GetField<object>(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude");
var dados = sensor.ValoresLeituras var dados = sensor.ValoresLeituras
.Where(y => y.analise_health) .Where(y => y.analise_health)
@ -462,7 +473,7 @@ namespace AgroBase.Services.Operadores
int id = bico.ID_Num; int id = bico.ID_Num;
string basePath = $"bicos.{tipo}.{id}"; string basePath = $"bicos.{tipo}.{id}";
var saude = RedisService.GetField<object>(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); var saude = RedisService.GetField<object>(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude");
var dados = bico.ValoresLeituras var dados = bico.ValoresLeituras
.Where(y => y.analise_health) .Where(y => y.analise_health)
@ -501,7 +512,7 @@ namespace AgroBase.Services.Operadores
int id = bomba.ID_Num; int id = bomba.ID_Num;
string basePath = $"bombas.{tipo}.{id}"; string basePath = $"bombas.{tipo}.{id}";
var saude = RedisService.GetField<object>(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); var saude = RedisService.GetField<object>(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude");
var dados = bomba.ValoresLeituras var dados = bomba.ValoresLeituras
.Where(y => y.analise_health) .Where(y => y.analise_health)

View File

@ -152,6 +152,8 @@ namespace AgroBase.Services.Operadores
catch { /* opcional: logar erro */ } catch { /* opcional: logar erro */ }
} }
DadosLeitura.Status = HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == Enums.T_Code.Snr)?.status ?? Enums.StatusModulo.Desconectado;
// VisualWorker // VisualWorker
var json = RedisService.Get(CtxKey.DadosVisualWorker); var json = RedisService.Get(CtxKey.DadosVisualWorker);
if (string.IsNullOrWhiteSpace(json)) return; if (string.IsNullOrWhiteSpace(json)) return;

View File

@ -186,6 +186,7 @@ namespace AgroBase.Services.Operadores
RedisService.AtualizarCampos( RedisService.AtualizarCampos(
CtxKey.DadosWeedWorker, CtxKey.DadosWeedWorker,
("analise.ervas_no_radar", false), ("analise.ervas_no_radar", false),
("analise.percentual_ervas_no_radar", 0),
("analise.controle", new Dictionary<string, bool>(Variaveis.OperacaoEmAndamento.DispAtu.Dados.BicosPulverizadores.ToDictionary(x => x.Posicao.ToString(), x => x.ComandoAtuar))), ("analise.controle", new Dictionary<string, bool>(Variaveis.OperacaoEmAndamento.DispAtu.Dados.BicosPulverizadores.ToDictionary(x => x.Posicao.ToString(), x => x.ComandoAtuar))),
("analise.estatisticas", new WeedWorkerAnaliseEstatisticasModel()) ("analise.estatisticas", new WeedWorkerAnaliseEstatisticasModel())
); );

View File

@ -145,7 +145,7 @@ namespace AgroBase.Services
public static void LimparDadosIniciais() public static void LimparDadosIniciais()
{ {
Set(CtxKey.DadosCameras, JsonConvert.SerializeObject(new Dictionary<string, CameraWorkerItemModel>())); //Set(CtxKey.DadosCameras, JsonConvert.SerializeObject(new Dictionary<string, CameraWorkerItemModel>()));
AtualizarCampos(ModKey(T_Code.Gps), ("correcao_ntrip", false)); AtualizarCampos(ModKey(T_Code.Gps), ("correcao_ntrip", false));
DefinirEquipamentoDesconectado(); DefinirEquipamentoDesconectado();
} }

View File

@ -240,7 +240,7 @@ namespace AgroBase.Services
await ProcurarDispositivosCAN(); await ProcurarDispositivosCAN();
var DispositivosRemovidos = dispositivosMapeados.Where(x => x.Tipo == TipoConexao.PortaCOM && !Portas.Contains(x.Endereco)).ToList(); var DispositivosRemovidos = dispositivosMapeados.Where(x => x.Tipo == TipoConexao.PortaCOM && (!Portas.Contains(x.Endereco) || x.Status == StatusModulo.Desconectado)).ToList();
foreach (var Dispositivo in DispositivosRemovidos) foreach (var Dispositivo in DispositivosRemovidos)
{ {
DispositivosMapeados.Remove(Dispositivo); DispositivosMapeados.Remove(Dispositivo);

View File

@ -1,5 +1,6 @@
using AgroBase.Models; using AgroBase.Models;
using System; using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,15 +10,39 @@ public class WaveshareCANFDService
private const uint DEVICE_INDEX = 0; private const uint DEVICE_INDEX = 0;
private readonly uint channelIndex; private readonly uint channelIndex;
private Action<VCI_CAN_OBJ> callback;
private AsyncTaskTimerModel tmrLeitura; private AsyncTaskTimerModel tmrLeitura;
private AsyncTaskTimerModel tmrWatchdogRx;
private AsyncTaskTimerModel tmrWatchdogSaude;
private readonly int delayTickTimer = 1; private readonly int delayTickTimer = 1;
private bool isOpen = false; private bool isOpen = false;
private bool _rxBusy = false; private volatile bool _rxBusy = false;
private DateTime _ultimoRxTick = DateTime.MinValue;
private DateTime _ultimoRxFrame = DateTime.MinValue;
private int _rxRecoveryStep = 0; // 0=ok,1=soft,2=medium,3=hard
private DateTime _ultimoRecovery = DateTime.MinValue;
// buffer reutilizável para receber da DLL
private readonly VCI_CAN_OBJ[] _rxBuf = InitBuf(256); // 100 também funciona; 256 dá folga
private static VCI_CAN_OBJ[] InitBuf(int n)
{
var arr = new VCI_CAN_OBJ[n];
for (int i = 0; i < n; i++)
arr[i].Data = new byte[8];
return arr;
}
private FramesBatchHandler _onFramesBatch;
// versão do callback por lote com contagem
public delegate void FramesBatchHandler(VCI_CAN_OBJ[] frames);
public bool IsOpen => isOpen; public bool IsOpen => isOpen;
public int Canal => ((int)channelIndex + 1); public int Canal => ((int)channelIndex + 1);
private int _lastKbps = 500;
private double _lastSp = 0.875;
public WaveshareCANFDService(uint channelIndex) public WaveshareCANFDService(uint channelIndex)
{ {
this.channelIndex = channelIndex; this.channelIndex = channelIndex;
@ -62,6 +87,8 @@ public class WaveshareCANFDService
[DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)] [DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VCI_StartCAN(uint DeviceType, uint DeviceInd, uint CANInd); public static extern uint VCI_StartCAN(uint DeviceType, uint DeviceInd, uint CANInd);
[DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VCI_ClearBuffer(uint DeviceType, uint DeviceInd, uint CANInd);
[DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)] [DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VCI_Transmit(uint DeviceType, uint DeviceInd, uint CANInd, ref VCI_CAN_OBJ pSend, uint Len); public static extern uint VCI_Transmit(uint DeviceType, uint DeviceInd, uint CANInd, ref VCI_CAN_OBJ pSend, uint Len);
@ -72,38 +99,33 @@ public class WaveshareCANFDService
[DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)] [DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VCI_SetReference(uint DeviceType, uint DeviceInd, uint CANInd, uint RefType, ref uint pData); public static extern uint VCI_SetReference(uint DeviceType, uint DeviceInd, uint CANInd, uint RefType, ref uint pData);
public bool Open(Action<VCI_CAN_OBJ> onReceive, int bitrateKbps = 500, double samplePoint = 0.875)
public bool Open(FramesBatchHandler onFramesBatch, int bitrateKbps = 500, double samplePoint = 0.875)
{ {
callback = onReceive; _onFramesBatch = onFramesBatch;
_lastKbps = bitrateKbps;
_lastSp = samplePoint;
if (VCI_OpenDevice(DEVICE_TYPE, DEVICE_INDEX, 0) != 1) return false; if (VCI_OpenDevice(DEVICE_TYPE, DEVICE_INDEX, 0) != 1) return false;
if (VCI_OpenDevice(DEVICE_TYPE, DEVICE_INDEX, 0) != 1) return false; if (!InitAndStartCAN()) return false;
// >>> SET BAUD PARA CAN-FD (ignora Timing0/1)
uint baudCode = MapToFdBaudCode(bitrateKbps);
if (VCI_SetReference(DEVICE_TYPE, DEVICE_INDEX, channelIndex, 0 /*RefType: baud*/, ref baudCode) != 1)
return false;
// o resto pode ficar igual; Timing0/1 serão ignorados pela DLL FD, então ponha 0 para clareza
var config = new VCI_INIT_CONFIG
{
AccCode = 0x00000000,
AccMask = 0xFFFFFFFF, // FD: máscara é ignorada segundo manual; sem problemas
Reserved = 0,
Filter = 0, // filtro padrão
Timing0 = 0, // ignorado em FD
Timing1 = 0, // ignorado em FD
Mode = 0
};
if (VCI_InitCAN(DEVICE_TYPE, DEVICE_INDEX, channelIndex, ref config) != 1) return false;
if (VCI_StartCAN(DEVICE_TYPE, DEVICE_INDEX, channelIndex) != 1) return false;
tmrLeitura?.Dispose(); tmrLeitura?.Dispose();
tmrLeitura = new AsyncTaskTimerModel($"tmrLeitura_CAN_{Canal}", tmrLeitura_Tick, delayTickTimer); tmrLeitura = new AsyncTaskTimerModel($"tmrLeitura_CAN_{Canal}", tmrLeitura_Tick, delayTickTimer);
tmrLeitura.Start(); tmrLeitura.Start();
tmrWatchdogRx?.Dispose();
tmrWatchdogRx = new AsyncTaskTimerModel($"tmrWatchdogRx_CAN_{Canal}", WatchdogRx, 500);
tmrWatchdogRx.Start();
tmrWatchdogSaude?.Dispose();
tmrWatchdogSaude = new AsyncTaskTimerModel($"tmrWatchdogSaude_CAN_{Canal}", WatchdogSaude, 100);
tmrWatchdogSaude.Start();
isOpen = true; isOpen = true;
Console.WriteLine($"[CAN-FD] Canal {Canal} aberto @ {bitrateKbps} kbps, SP≈{samplePoint * 100:0.#}%"); Console.WriteLine($"[CAN-FD] Canal {Canal} aberto @ {bitrateKbps} kbps, SP≈{samplePoint * 100:0.#}%");
return true; return true;
@ -154,27 +176,56 @@ public class WaveshareCANFDService
_rxBusy = true; _rxBusy = true;
try try
{ {
// (re)aloca buffer local por tick para evitar lixo compartilhado _ultimoRxTick = DateTime.Now;
var local = new VCI_CAN_OBJ[100]; int emptyReads = 0;
for (int i = 0; i < local.Length; i++)
local[i].Data = new byte[8];
var count = VCI_Receive(DEVICE_TYPE, DEVICE_INDEX, channelIndex, local, 100, 100); const int MAX_PER_TICK = 512;
if (count > 0) var outList = new List<VCI_CAN_OBJ>(Math.Min(MAX_PER_TICK, _rxBuf.Length));
int total = 0;
while (total < MAX_PER_TICK)
{ {
for (int i = 0; i < count; i++) int got = (int)VCI_Receive(DEVICE_TYPE, DEVICE_INDEX, channelIndex, _rxBuf, (uint)_rxBuf.Length, 0);
if (got == 0)
{ {
var frame = local[i]; emptyReads++;
var data = new byte[frame.DataLen]; break; // sem dados agora; libera CPU
Array.Copy(frame.Data, data, frame.DataLen); }
// 🔧 substitui o Data pelo array "desanexado" do buffer // chegou algo
frame.Data = data; _ultimoRxFrame = DateTime.Now;
_rxRecoveryStep = 0; // reset da escada de recovery, está saudável
// entrega frame "isolado" ao callback for (int i = 0; i < got && total < MAX_PER_TICK; i++, total++)
callback?.Invoke(frame); {
var src = _rxBuf[i];
var dlc = src.DataLen;
if (dlc < 0) dlc = 0;
if (dlc > 8) dlc = 8;
var dst = src;
var data = dlc > 0 ? new byte[dlc] : Array.Empty<byte>();
if (dlc > 0) Array.Copy(src.Data, data, dlc);
dst.Data = data;
outList.Add(dst);
} }
} }
if (outList.Count > 0)
{
try { _onFramesBatch?.Invoke(outList.ToArray()); }
catch (Exception ex)
{
// não deixe matar o RX
Console.WriteLine($"[CAN-FD] ERRO no callback de frames: {ex.Message}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[CAN-FD] ERRO no tmrLeitura_Tick: {ex}");
// opcional: marcar para reabrir canal no próximo health
} }
finally finally
{ {
@ -182,12 +233,111 @@ public class WaveshareCANFDService
} }
} }
private async Task WatchdogRx()
{
var semTick = (DateTime.Now - _ultimoRxTick).TotalSeconds;
if (semTick > 1)
{
Console.WriteLine("[CAN-FD] Watchdog: RX sem tick há >1s. Reiniciando leitura...");
try
{ // reinicialize apenas a tarefa/timer de leitura
// (seu AsyncTaskTimerModel deve permitir Stop/Start)
tmrLeitura?.Stop();
_rxBusy = false; // forçar liberação
tmrLeitura?.Start();
}
catch { }
}
}
private async Task WatchdogSaude()
{
var now = DateTime.Now;
// 2) watchdog de SAÚDE DO RX (frames reais)
var semRx = (now - _ultimoRxFrame).TotalMilliseconds;
// thresholds (ajuste conforme tráfego)
if (semRx > 1000 && _rxRecoveryStep == 0 && (now - _ultimoRecovery).TotalMilliseconds > 300)
{
_rxRecoveryStep = 1; _ultimoRecovery = now;
Console.WriteLine("[CAN-FD] RX sem frames >1s. Soft-kick: ClearBuffer + StartCAN.");
try
{
VCI_ClearBuffer(DEVICE_TYPE, DEVICE_INDEX, channelIndex);
VCI_StartCAN(DEVICE_TYPE, DEVICE_INDEX, channelIndex);
}
catch (Exception ex) { Console.WriteLine($"[CAN-FD] Soft-kick falhou: {ex.Message}"); }
return;
}
if (semRx > 2000 && _rxRecoveryStep <= 1 && (now - _ultimoRecovery).TotalMilliseconds > 500)
{
_rxRecoveryStep = 2; _ultimoRecovery = now;
Console.WriteLine("[CAN-FD] RX sem frames >2s. Medium reset: ResetCAN + InitCAN + StartCAN.");
try
{
if (!InitAndStartCAN()) Console.WriteLine("[CAN-FD] Medium reset: Init/Start falhou!");
}
catch (Exception ex) { Console.WriteLine($"[CAN-FD] Medium reset falhou: {ex.Message}"); }
return;
}
if (semRx > 5000 && _rxRecoveryStep <= 2 && (now - _ultimoRecovery).TotalMilliseconds > 1000)
{
_rxRecoveryStep = 3; _ultimoRecovery = now;
Console.WriteLine("[CAN-FD] RX sem frames >5s. HARD reopen: CloseDevice/OpenDevice/Init/Start.");
try
{
HardReopen(); // feche e reabra igual ao seu Open()
}
catch (Exception ex) { Console.WriteLine($"[CAN-FD] Hard reopen falhou: {ex.Message}"); }
return;
}
}
private bool InitAndStartCAN()
{
// reconfigura baud FD
uint baud = MapToFdBaudCode(_lastKbps);
if (VCI_SetReference(DEVICE_TYPE, DEVICE_INDEX, channelIndex, 0, ref baud) != 1)
return false;
var cfg = new VCI_INIT_CONFIG
{
AccCode = 0,
AccMask = 0xFFFFFFFF,
Reserved = 0,
Filter = 0,
Timing0 = 0,
Timing1 = 0,
Mode = 0
};
if (VCI_InitCAN(DEVICE_TYPE, DEVICE_INDEX, channelIndex, ref cfg) != 1)
return false;
return VCI_StartCAN(DEVICE_TYPE, DEVICE_INDEX, channelIndex) == 1;
}
private bool HardReopen()
{
try { VCI_CloseDevice(DEVICE_TYPE, DEVICE_INDEX); } catch { }
// reabre device
if (VCI_OpenDevice(DEVICE_TYPE, DEVICE_INDEX, 0) != 1) return false;
// reinit canal com os últimos parâmetros
return InitAndStartCAN();
}
public void Close() public void Close()
{ {
if (!isOpen) return; if (!isOpen) return;
tmrLeitura?.Dispose(); tmrLeitura?.Dispose();
tmrWatchdogRx?.Dispose();
tmrWatchdogSaude?.Dispose();
VCI_CloseDevice(DEVICE_TYPE, DEVICE_INDEX); VCI_CloseDevice(DEVICE_TYPE, DEVICE_INDEX);
isOpen = false; isOpen = false;
Console.WriteLine($"[CAN-FD] Canal {Canal} fechado"); Console.WriteLine($"[CAN-FD] Canal {Canal} fechado");
} }
} }

View File

@ -1,12 +1,5 @@
{ {
"epochs": [ { "epochs": [ {
"calculation_time": "13398455528662484",
"config_version": 0,
"model_version": "0",
"padded_top_topics_start_index": 0,
"taxonomy_version": 0,
"top_topics_and_observing_domains": [ ]
}, {
"calculation_time": "13399153521058315", "calculation_time": "13399153521058315",
"config_version": 0, "config_version": 0,
"model_version": "0", "model_version": "0",
@ -27,7 +20,14 @@
"padded_top_topics_start_index": 0, "padded_top_topics_start_index": 0,
"taxonomy_version": 0, "taxonomy_version": 0,
"top_topics_and_observing_domains": [ ] "top_topics_and_observing_domains": [ ]
}, {
"calculation_time": "13401214743883964",
"config_version": 0,
"model_version": "0",
"padded_top_topics_start_index": 0,
"taxonomy_version": 0,
"top_topics_and_observing_domains": [ ]
} ], } ],
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642", "hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
"next_scheduled_calculation_time": "13401199940855097" "next_scheduled_calculation_time": "13401819543884442"
} }

View File

@ -1,3 +1,3 @@
2025/08/25-17:06:11.375 d6c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001 2025/09/02-08:53:17.510 23f4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
2025/08/25-17:06:11.381 d6c Recovering log #3 2025/09/02-08:53:17.517 23f4 Recovering log #3
2025/08/25-17:06:11.384 d6c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log 2025/09/02-08:53:17.520 23f4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log

View File

@ -1,3 +1,3 @@
2025/08/25-17:05:24.176 da4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001 2025/09/02-08:47:11.765 3b04 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
2025/08/25-17:05:24.181 da4 Recovering log #3 2025/09/02-08:47:11.773 3b04 Recovering log #3
2025/08/25-17:05:24.184 da4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log 2025/09/02-08:47:11.777 3b04 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log

View File

@ -1 +1 @@
{"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13400712371911326","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":42895},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:60c:f100:fd1a:acec:81f9:ab34","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":"13401373997903613","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":7779},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:6a8:5d00:c40:d68b:d066:c8cf","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}}

View File

@ -1 +1 @@
{"sts":[{"expiry":1787685962.878582,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1756149962.878585}],"version":2} {"sts":[{"expiry":1788349633.320866,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1756813633.320875}],"version":2}

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
2025/08/25-17:07:32.655 d6c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001 2025/09/02-09:10:40.010 23f4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
2025/08/25-17:07:32.656 d6c Recovering log #3 2025/09/02-09:10:40.012 23f4 Recovering log #3
2025/08/25-17:07:32.661 d6c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log 2025/09/02-09:10:40.015 23f4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log

View File

@ -1,3 +1,3 @@
2025/08/25-17:05:59.944 da4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001 2025/09/02-08:53:00.810 3b04 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
2025/08/25-17:05:59.945 da4 Recovering log #3 2025/09/02-08:53:00.812 3b04 Recovering log #3
2025/08/25-17:05:59.949 da4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log 2025/09/02-08:53:00.817 3b04 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log

View File

@ -1,3 +1,3 @@
2025/08/25-17:06:11.307 51e4 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001 2025/09/02-08:53:17.431 67f8 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
2025/08/25-17:06:11.309 51e4 Recovering log #7 2025/09/02-08:53:17.433 67f8 Recovering log #7
2025/08/25-17:06:11.309 51e4 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log 2025/09/02-08:53:17.433 67f8 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log

View File

@ -1,3 +1,3 @@
2025/08/25-17:05:24.104 698c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001 2025/09/02-08:47:11.681 4a8c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
2025/08/25-17:05:24.106 698c Recovering log #7 2025/09/02-08:47:11.683 4a8c Recovering log #7
2025/08/25-17:05:24.106 698c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log 2025/09/02-08:47:11.683 4a8c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log

View File

@ -1 +1 @@
139.0.3405.111 139.0.3405.125

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1,221 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Id":"1","Name":"25_08_2025_16_29_49_MapaGPS","Length":45.0669749673391,"Dist1":0.0,"Dist2":0.0},"geometry":{"id":null,"type":"LineString","coordinates":[[-47.395187119333336,-22.172544759833333],[-47.3951871205,-22.172544750333333],[-47.395187123666666,-22.17254474],[-47.395187119333336,-22.172544773],[-47.39518710983333,-22.172544767666668],[-47.3951870925,-22.172544773333332],[-47.3951871135,-22.1725447695],[-47.395187109,-22.172544759],[-47.395187124333333,-22.172544775333332],[-47.395187104,-22.172544765333335],[-47.395187114,-22.17254478],[-47.395187119,-22.1725447645],[-47.395187111166663,-22.172544784],[-47.39518645,-22.172549661833333],[-47.39518665166667,-22.172551579833332],[-47.395191283666669,-22.172556917166666],[-47.395194691166665,-22.172557394166667],[-47.395197058166666,-22.1725579445],[-47.39520246033333,-22.172560708833334],[-47.395205430833336,-22.172563393833332],[-47.3952061165,-22.172569330666665],[-47.395204892333332,-22.172570874],[-47.395202153,-22.172576186166665],[-47.3952015505,-22.172579329333335],[-47.395201410666665,-22.172581696333335],[-47.395201276666668,-22.172586928833333],[-47.395200646,-22.172589796],[-47.395201421833335,-22.172594937166668],[-47.395201952333331,-22.172597846666665],[-47.3952035565,-22.172603020166665],[-47.395204372,-22.172605900833332],[-47.3952052305,-22.172607986333333],[-47.3952063055,-22.1726129495],[-47.395207043666666,-22.172615775833332],[-47.3952076955,-22.172621029],[-47.395208046166665,-22.17262326],[-47.395208260333334,-22.172629409],[-47.3952083685,-22.172631642666666],[-47.3952084535,-22.1726344725],[-47.3952091315,-22.172639615666668],[-47.39520933033333,-22.172642971166667],[-47.395209947833337,-22.172648214166667],[-47.395210410333334,-22.172650344333334],[-47.395210539166669,-22.1726554185],[-47.395211094833336,-22.1726582835],[-47.3952113955,-22.172663612],[-47.395211805333332,-22.172665979],[-47.395212764166665,-22.172671918666666],[-47.395213434666665,-22.172674605333334],[-47.3952138935,-22.172676825166668],[-47.395214831666664,-22.172681917166667],[-47.395215301166665,-22.172684613166666],[-47.395216344666665,-22.172689754166665],[-47.395216705333333,-22.172692151666666],[-47.3952172425,-22.172695494833334],[-47.395217986666665,-22.172701318666668],[-47.395218297333336,-22.1727032815],[-47.395218085,-22.1727083455],[-47.395217370666664,-22.172711332],[-47.395221153,-22.172715260166665],[-47.395222875166667,-22.172716722166665],[-47.3952262205,-22.172721511833334],[-47.395226792166667,-22.1727251945],[-47.3952264485,-22.172728077666665],[-47.395221607833335,-22.1727346585],[-47.395217703833332,-22.172736784333335],[-47.395209911666669,-22.172737078833332],[-47.395206829833334,-22.172735761166667],[-47.395201955833336,-22.172729872333335],[-47.395201161833334,-22.172725887666665],[-47.395202950666665,-22.172722859166665],[-47.395206459,-22.1727181785],[-47.395209633,-22.172715328833334],[-47.395214860166668,-22.172716129],[-47.395217123166667,-22.172716103333332],[-47.395223301166666,-22.172714955333333],[-47.395226070333337,-22.172712866333335],[-47.395227983666665,-22.172710646166667],[-47.395229317,-22.172702757833335],[-47.395227237833332,-22.172699078333334],[-47.3952201425,-22.172694863666667],[-47.395216778,-22.17269502],[-47.395211916166666,-22.172697834666668],[-47.3952096905,-22.172699738166667],[-47.395207431,-22.172700759833333],[-47.395201152333335,-22.1727031105],[-47.395196381333335,-22.172702974833335],[-47.39519378,-22.172702100166667],[-47.395188575333336,-22.172697375166667],[-47.395187179,-22.172693494333334],[-47.3951886055,-22.172686481833335],[-47.395190795666664,-22.1726839655],[-47.395197941333336,-22.1726811135],[-47.395201979333336,-22.172681057],[-47.395203451833332,-22.172682550166666],[-47.395207649166665,-22.172685885666667],[-47.3952109425,-22.17268669],[-47.3952134685,-22.1726865585],[-47.395219690666664,-22.172683575],[-47.395223081166669,-22.1726776265],[-47.3952225125,-22.172672815666665],[-47.395220510166666,-22.172669935333332],[-47.395214666666668,-22.172667577333332],[-47.395210875833335,-22.172667172],[-47.395205466,-22.172665870166668],[-47.3952020855,-22.172664815333334],[-47.395198556666664,-22.172663721],[-47.395193420333335,-22.1726622985],[-47.395193420333335,-22.1726622985]]}}]} {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"Id": "1",
"Name": "CidadeJardimTerreno2",
"Length": 14.558011415731592,
"Dist1": 14.558011415731592,
"Dist2": 0.0
},
"geometry": {
"id": null,
"type": "LineString",
"coordinates": [
[
-47.395205344,
-22.172559531333334
],
[
-47.395212610166666,
-22.172614638833334
],
[
-47.395219157,
-22.172656417833334
],
[
-47.395223544833335,
-22.1726892105
],
[
-47.39522414098443,
-22.17269369161165
],
[
-47.395225326538004,
-22.172702654611555
]
]
}
},
{
"type": "Feature",
"properties": {
"Id": "2",
"Name": "CidadeJardimTerreno2",
"Length": 14.771318274761821,
"Dist1": 14.558011415731592,
"Dist2": 0.0
},
"geometry": {
"id": null,
"type": "LineString",
"coordinates": [
[
-47.39521090233333,
-22.172708800833334
],
[
-47.395206943666665,
-22.172679811
],
[
-47.395202420666664,
-22.1726491015
],
[
-47.395198865666664,
-22.172615782833333
],
[
-47.395193255833334,
-22.172577132833332
],
[
-47.395192610024395,
-22.17257265769541
],
[
-47.395191325698136,
-22.172563706509337
]
]
}
},
{
"type": "Feature",
"properties": {
"Id": "3",
"Name": "CidadeJardimTerreno2",
"Length": 14.827915524386164,
"Dist1": 14.558011415731592,
"Dist2": 0.0
},
"geometry": {
"id": null,
"type": "LineString",
"coordinates": [
[
-47.3951762925,
-22.172561603
],
[
-47.395180824166665,
-22.172591686166665
],
[
-47.395185745333336,
-22.172623080166666
],
[
-47.395190433,
-22.172656367166667
],
[
-47.395195199,
-22.172693641833334
],
[
-47.39519576904436,
-22.172698125891035
],
[
-47.39519690267159,
-22.172707094716973
]
]
}
},
{
"type": "Feature",
"properties": {
"Id": "4",
"Name": "CidadeJardimTerreno2",
"Length": 14.785159385903514,
"Dist1": 14.558011415731592,
"Dist2": 0.0
},
"geometry": {
"id": null,
"type": "LineString",
"coordinates": [
[
-47.39518293216667,
-22.1727127985
],
[
-47.39517819266667,
-22.172680514833335
],
[
-47.3951732595,
-22.172646752833334
],
[
-47.39516880516667,
-22.1726149155
],
[
-47.39516381233334,
-22.172581167166665
],
[
-47.39516315429932,
-22.172576693573966
],
[
-47.39516184565584,
-22.172567745443878
]
]
}
},
{
"type": "Feature",
"properties": {
"Id": "5",
"Name": "CidadeJardimTerreno2",
"Length": 14.80117692191233,
"Dist1": 14.558011415731592,
"Dist2": 0.0
},
"geometry": {
"id": null,
"type": "LineString",
"coordinates": [
[
-47.395147317833334,
-22.172566031
],
[
-47.395151503166666,
-22.172595452333333
],
[
-47.3951561855,
-22.172628011166665
],
[
-47.39516159866667,
-22.172663757333332
],
[
-47.39516672716667,
-22.172697770833334
],
[
-47.395167397572706,
-22.172702242832194
],
[
-47.39516873082615,
-22.17271118781009
]
]
}
}
]
}

View File

@ -17,7 +17,7 @@
<meta name="viewport" content="width=device-width, <meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style> <style>
#map_b2cfb7c0b8cd21733288866dbc6da89b { #map_b38d811fa61af8b65eff35dfc9035850 {
position: relative; position: relative;
width: 100.0%; width: 100.0%;
height: 100.0%; height: 100.0%;
@ -54,14 +54,14 @@
<body> <body>
<div class="folium-map" id="map_b2cfb7c0b8cd21733288866dbc6da89b" ></div> <div class="folium-map" id="map_b38d811fa61af8b65eff35dfc9035850" ></div>
</body> </body>
<script> <script>
var map_b2cfb7c0b8cd21733288866dbc6da89b = L.map( var map_b38d811fa61af8b65eff35dfc9035850 = L.map(
"map_b2cfb7c0b8cd21733288866dbc6da89b", "map_b38d811fa61af8b65eff35dfc9035850",
{ {
center: [0.0, 0.0], center: [0.0, 0.0],
crs: L.CRS.EPSG3857, crs: L.CRS.EPSG3857,
@ -78,7 +78,7 @@
var tile_layer_bab172d3f76c66b031f76d6ae9baaaaf = L.tileLayer( var tile_layer_ad727f4f6e04926dedef75d899276bfb = L.tileLayer(
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
{ {
"minZoom": 0, "minZoom": 0,
@ -95,7 +95,7 @@
); );
tile_layer_bab172d3f76c66b031f76d6ae9baaaaf.addTo(map_b2cfb7c0b8cd21733288866dbc6da89b); tile_layer_ad727f4f6e04926dedef75d899276bfb.addTo(map_b38d811fa61af8b65eff35dfc9035850);
</script> </script>
@ -116,7 +116,7 @@
} }
trajeto_json_add({"features": []}); trajeto_json_add({"features": []});
trajeto_json.addTo(map_b2cfb7c0b8cd21733288866dbc6da89b); trajeto_json.addTo(map_b38d811fa61af8b65eff35dfc9035850);
function adicionarGeometria(novaGeometria) { function adicionarGeometria(novaGeometria) {
trajeto_json.addData(novaGeometria); trajeto_json.addData(novaGeometria);
@ -179,9 +179,9 @@
var marcadorEquipamento = L.marker([0, 0], { var marcadorEquipamento = L.marker([0, 0], {
icon: customIcon icon: customIcon
}).addTo(map_b2cfb7c0b8cd21733288866dbc6da89b); }).addTo(map_b38d811fa61af8b65eff35dfc9035850);
var marcadorBase = L.marker([0, 0], {}).addTo(map_b2cfb7c0b8cd21733288866dbc6da89b); var marcadorBase = L.marker([0, 0], {}).addTo(map_b38d811fa61af8b65eff35dfc9035850);
var icon = L.AwesomeMarkers.icon( var icon = L.AwesomeMarkers.icon(
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"} {"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
); );
@ -246,7 +246,7 @@
} }
if (foco) { if (foco) {
map_b2cfb7c0b8cd21733288866dbc6da89b.setView(novaPosicao, map_b2cfb7c0b8cd21733288866dbc6da89b.getZoom()); map_b38d811fa61af8b65eff35dfc9035850.setView(novaPosicao, map_b38d811fa61af8b65eff35dfc9035850.getZoom());
} }
} }
@ -268,7 +268,7 @@
marcadorDinamico.setRotationAngle(angulo); marcadorDinamico.setRotationAngle(angulo);
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]); adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
map_b2cfb7c0b8cd21733288866dbc6da89b.setView(novaPosicao, map_b2cfb7c0b8cd21733288866dbc6da89b.getZoom());*/ map_b38d811fa61af8b65eff35dfc9035850.setView(novaPosicao, map_b38d811fa61af8b65eff35dfc9035850.getZoom());*/
}); });
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) { function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {

View File

@ -17,7 +17,7 @@
<meta name="viewport" content="width=device-width, <meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style> <style>
#map_b21baaed2057e8bbe47af17d68ecedd6 { #map_21ca758b860567b95d146fc0853c971f {
position: relative; position: relative;
width: 100.0%; width: 100.0%;
height: 100.0%; height: 100.0%;
@ -54,16 +54,16 @@
<body> <body>
<div class="folium-map" id="map_b21baaed2057e8bbe47af17d68ecedd6" ></div> <div class="folium-map" id="map_21ca758b860567b95d146fc0853c971f" ></div>
</body> </body>
<script> <script>
var map_b21baaed2057e8bbe47af17d68ecedd6 = L.map( var map_21ca758b860567b95d146fc0853c971f = L.map(
"map_b21baaed2057e8bbe47af17d68ecedd6", "map_21ca758b860567b95d146fc0853c971f",
{ {
center: [-22.172640909416664, -47.3952078835], center: [-22.172636164916668, -47.395186322185666],
crs: L.CRS.EPSG3857, crs: L.CRS.EPSG3857,
...{ ...{
"zoom": 12, "zoom": 12,
@ -78,7 +78,7 @@
var tile_layer_faa254f73950244990d85d26fa7337d6 = L.tileLayer( var tile_layer_aa19b16e8f9ad64dd51817e34341c533 = L.tileLayer(
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
{ {
"minZoom": 0, "minZoom": 0,
@ -95,7 +95,7 @@
); );
tile_layer_faa254f73950244990d85d26fa7337d6.addTo(map_b21baaed2057e8bbe47af17d68ecedd6); tile_layer_aa19b16e8f9ad64dd51817e34341c533.addTo(map_21ca758b860567b95d146fc0853c971f);
@ -111,7 +111,7 @@
}*/ }*/
}); });
} }
function geo_json_99cfc632ca5e92adc9931d372fcc1b88_onEachFeature(feature, layer) { function geo_json_40d1c4f07ee5691fa37b3e9a1cd24536_onEachFeature(feature, layer) {
layer.on({ layer.on({
@ -148,23 +148,23 @@
}*/ }*/
}); });
}; };
var geo_json_99cfc632ca5e92adc9931d372fcc1b88 = L.geoJson(null, { var geo_json_40d1c4f07ee5691fa37b3e9a1cd24536 = L.geoJson(null, {
onEachFeature: geo_json_99cfc632ca5e92adc9931d372fcc1b88_onEachFeature, onEachFeature: geo_json_40d1c4f07ee5691fa37b3e9a1cd24536_onEachFeature,
...{ ...{
} }
}); });
function geo_json_99cfc632ca5e92adc9931d372fcc1b88_add (data) { function geo_json_40d1c4f07ee5691fa37b3e9a1cd24536_add (data) {
geo_json_99cfc632ca5e92adc9931d372fcc1b88 geo_json_40d1c4f07ee5691fa37b3e9a1cd24536
.addData(data); .addData(data);
} }
geo_json_99cfc632ca5e92adc9931d372fcc1b88_add({"features": [{"geometry": {"coordinates": [[-47.395187119333336, -22.172544759833333], [-47.3951871205, -22.172544750333333], [-47.395187123666666, -22.17254474], [-47.395187119333336, -22.172544773], [-47.39518710983333, -22.17254476766667], [-47.3951870925, -22.172544773333332], [-47.3951871135, -22.1725447695], [-47.395187109, -22.172544759], [-47.39518712433333, -22.172544775333332], [-47.395187104, -22.172544765333335], [-47.395187114, -22.17254478], [-47.395187119, -22.1725447645], [-47.39518711116666, -22.172544784], [-47.39518645, -22.172549661833333], [-47.39518665166667, -22.172551579833332], [-47.39519128366667, -22.172556917166666], [-47.395194691166665, -22.172557394166667], [-47.395197058166666, -22.1725579445], [-47.39520246033333, -22.172560708833334], [-47.395205430833336, -22.172563393833332], [-47.3952061165, -22.172569330666665], [-47.39520489233333, -22.172570874], [-47.395202153, -22.172576186166665], [-47.3952015505, -22.172579329333335], [-47.395201410666665, -22.172581696333335], [-47.39520127666667, -22.172586928833333], [-47.395200646, -22.172589796], [-47.395201421833335, -22.172594937166668], [-47.39520195233333, -22.172597846666665], [-47.3952035565, -22.172603020166665], [-47.395204372, -22.172605900833332], [-47.3952052305, -22.172607986333333], [-47.3952063055, -22.1726129495], [-47.395207043666666, -22.172615775833332], [-47.3952076955, -22.172621029], [-47.395208046166665, -22.17262326], [-47.395208260333334, -22.172629409], [-47.3952083685, -22.172631642666666], [-47.3952084535, -22.1726344725], [-47.3952091315, -22.172639615666668], [-47.39520933033333, -22.172642971166667], [-47.39520994783334, -22.172648214166667], [-47.395210410333334, -22.172650344333334], [-47.39521053916667, -22.1726554185], [-47.395211094833336, -22.1726582835], [-47.3952113955, -22.172663612], [-47.39521180533333, -22.172665979], [-47.395212764166665, -22.172671918666666], [-47.395213434666665, -22.172674605333334], [-47.3952138935, -22.17267682516667], [-47.395214831666664, -22.172681917166667], [-47.395215301166665, -22.172684613166666], [-47.395216344666665, -22.172689754166665], [-47.39521670533333, -22.172692151666666], [-47.3952172425, -22.172695494833334], [-47.395217986666665, -22.172701318666668], [-47.39521829733334, -22.1727032815], [-47.395218085, -22.1727083455], [-47.395217370666664, -22.172711332], [-47.395221153, -22.172715260166665], [-47.39522287516667, -22.172716722166665], [-47.3952262205, -22.172721511833334], [-47.39522679216667, -22.1727251945], [-47.3952264485, -22.172728077666665], [-47.395221607833335, -22.1727346585], [-47.39521770383333, -22.172736784333335], [-47.39520991166667, -22.172737078833332], [-47.395206829833334, -22.172735761166667], [-47.395201955833336, -22.172729872333335], [-47.395201161833334, -22.172725887666665], [-47.395202950666665, -22.172722859166665], [-47.395206459, -22.1727181785], [-47.395209633, -22.172715328833334], [-47.39521486016667, -22.172716129], [-47.39521712316667, -22.172716103333332], [-47.395223301166666, -22.172714955333333], [-47.39522607033334, -22.172712866333335], [-47.395227983666665, -22.172710646166667], [-47.395229317, -22.172702757833335], [-47.39522723783333, -22.172699078333334], [-47.3952201425, -22.172694863666667], [-47.395216778, -22.17269502], [-47.395211916166666, -22.172697834666668], [-47.3952096905, -22.172699738166667], [-47.395207431, -22.172700759833333], [-47.395201152333335, -22.1727031105], [-47.395196381333335, -22.172702974833335], [-47.39519378, -22.172702100166667], [-47.395188575333336, -22.172697375166667], [-47.395187179, -22.172693494333334], [-47.3951886055, -22.172686481833335], [-47.395190795666664, -22.1726839655], [-47.395197941333336, -22.1726811135], [-47.395201979333336, -22.172681057], [-47.39520345183333, -22.172682550166666], [-47.395207649166665, -22.172685885666667], [-47.3952109425, -22.17268669], [-47.3952134685, -22.1726865585], [-47.395219690666664, -22.172683575], [-47.39522308116667, -22.1726776265], [-47.3952225125, -22.172672815666665], [-47.395220510166666, -22.17266993533333], [-47.39521466666667, -22.172667577333332], [-47.395210875833335, -22.172667172], [-47.395205466, -22.172665870166668], [-47.3952020855, -22.172664815333334], [-47.395198556666664, -22.172663721], [-47.395193420333335, -22.1726622985], [-47.395193420333335, -22.1726622985]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 0.0, "Dist2": 0.0, "Id": "1", "Length": 45.0669749673391, "Name": "25_08_2025_16_29_49_MapaGPS"}, "type": "Feature"}], "type": "FeatureCollection"}); geo_json_40d1c4f07ee5691fa37b3e9a1cd24536_add({"features": [{"geometry": {"coordinates": [[-47.395205344, -22.172559531333334], [-47.395212610166666, -22.172614638833334], [-47.395219157, -22.172656417833334], [-47.395223544833335, -22.1726892105], [-47.39522414098443, -22.17269369161165], [-47.395225326538004, -22.172702654611555]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "1", "Length": 14.558011415731592, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39521090233333, -22.172708800833334], [-47.395206943666665, -22.172679811], [-47.395202420666664, -22.1726491015], [-47.395198865666664, -22.172615782833333], [-47.395193255833334, -22.172577132833332], [-47.395192610024395, -22.17257265769541], [-47.395191325698136, -22.172563706509337]], "id": null, "type": "LineString"}, "id": 1, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "2", "Length": 14.771318274761821, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.3951762925, -22.172561603], [-47.395180824166665, -22.172591686166665], [-47.395185745333336, -22.172623080166666], [-47.395190433, -22.172656367166667], [-47.395195199, -22.172693641833334], [-47.39519576904436, -22.172698125891035], [-47.39519690267159, -22.172707094716973]], "id": null, "type": "LineString"}, "id": 2, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "3", "Length": 14.827915524386164, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39518293216667, -22.1727127985], [-47.39517819266667, -22.172680514833335], [-47.3951732595, -22.172646752833334], [-47.39516880516667, -22.1726149155], [-47.39516381233334, -22.172581167166665], [-47.39516315429932, -22.172576693573966], [-47.39516184565584, -22.172567745443878]], "id": null, "type": "LineString"}, "id": 3, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "4", "Length": 14.785159385903514, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.395147317833334, -22.172566031], [-47.395151503166666, -22.172595452333333], [-47.3951561855, -22.172628011166665], [-47.39516159866667, -22.172663757333332], [-47.39516672716667, -22.172697770833334], [-47.395167397572706, -22.172702242832194], [-47.39516873082615, -22.17271118781009]], "id": null, "type": "LineString"}, "id": 4, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "5", "Length": 14.80117692191233, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}], "type": "FeatureCollection"});
geo_json_99cfc632ca5e92adc9931d372fcc1b88.setStyle(function(feature) {return feature.properties.style;}); geo_json_40d1c4f07ee5691fa37b3e9a1cd24536.setStyle(function(feature) {return feature.properties.style;});
geo_json_99cfc632ca5e92adc9931d372fcc1b88.addTo(map_b21baaed2057e8bbe47af17d68ecedd6); geo_json_40d1c4f07ee5691fa37b3e9a1cd24536.addTo(map_21ca758b860567b95d146fc0853c971f);
</script> </script>
@ -185,7 +185,7 @@
} }
trajeto_json_add({"features": []}); trajeto_json_add({"features": []});
trajeto_json.addTo(map_b21baaed2057e8bbe47af17d68ecedd6); trajeto_json.addTo(map_21ca758b860567b95d146fc0853c971f);
function adicionarGeometria(novaGeometria) { function adicionarGeometria(novaGeometria) {
trajeto_json.addData(novaGeometria); trajeto_json.addData(novaGeometria);
@ -243,7 +243,7 @@
} }
trajeto_dinamico_json_add({"features": []}); trajeto_dinamico_json_add({"features": []});
trajeto_dinamico_json.addTo(map_b21baaed2057e8bbe47af17d68ecedd6); trajeto_dinamico_json.addTo(map_21ca758b860567b95d146fc0853c971f);
function adicionarGeometriaDinamica(novaGeometria) { function adicionarGeometriaDinamica(novaGeometria) {
trajeto_dinamico_json.addData(novaGeometria); trajeto_dinamico_json.addData(novaGeometria);
@ -296,9 +296,9 @@
var marcadorEquipamento = L.marker([0, 0], { var marcadorEquipamento = L.marker([0, 0], {
icon: customIcon icon: customIcon
}).addTo(map_b21baaed2057e8bbe47af17d68ecedd6); }).addTo(map_21ca758b860567b95d146fc0853c971f);
var marcadorBase = L.marker([0, 0], {}).addTo(map_b21baaed2057e8bbe47af17d68ecedd6); var marcadorBase = L.marker([0, 0], {}).addTo(map_21ca758b860567b95d146fc0853c971f);
var icon = L.AwesomeMarkers.icon( var icon = L.AwesomeMarkers.icon(
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"} {"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
); );
@ -380,7 +380,7 @@
} }
if (foco) { if (foco) {
map_b21baaed2057e8bbe47af17d68ecedd6.setView(novaPosicao, map_b21baaed2057e8bbe47af17d68ecedd6.getZoom()); map_21ca758b860567b95d146fc0853c971f.setView(novaPosicao, map_21ca758b860567b95d146fc0853c971f.getZoom());
} }
} }
@ -397,7 +397,7 @@
function atualizarSelecaoRuas(selecionadas) { function atualizarSelecaoRuas(selecionadas) {
selecionadas = JSON.parse(selecionadas); selecionadas = JSON.parse(selecionadas);
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : []; RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
geo_json_99cfc632ca5e92adc9931d372fcc1b88.eachLayer(function (layer) { geo_json_40d1c4f07ee5691fa37b3e9a1cd24536.eachLayer(function (layer) {
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) { if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {
layer.setStyle({ color: 'blue' }); layer.setStyle({ color: 'blue' });
} else { } else {

View File

@ -12,18 +12,6 @@ class CameraOak:
self.modelo_ia_seg = modelo_ia_seg self.modelo_ia_seg = modelo_ia_seg
self.modelo_ia_det = modelo_ia_det self.modelo_ia_det = modelo_ia_det
disp_list = dai.Device.getAllAvailableDevices()
disp_info = next((d for d in disp_list if d.getMxId() == mx_id), None)
if not disp_info:
return
#raise RuntimeError(f"Dispositivo com mxid {mx_id} não encontrado")
self.dev_info = disp_info
#self.dev_info = dai.DeviceInfo(mx_id)
self.mx_id = self.dev_info.getMxId()
self.versao = self.dev_info.name
self.dispositivo = T_Code.Vzo self.dispositivo = T_Code.Vzo
self.modelo = "Desconhecido" self.modelo = "Desconhecido"
self.ultima_saude = {} self.ultima_saude = {}
@ -37,6 +25,19 @@ class CameraOak:
self.rodando = False self.rodando = False
self.iniciado = False self.iniciado = False
disp_list = dai.Device.getAllAvailableDevices()
disp_info = next((d for d in disp_list if d.getMxId() == mx_id), None)
if not disp_info:
return
#raise RuntimeError(f"Dispositivo com mxid {mx_id} não encontrado")
self.dev_info = disp_info
#self.dev_info = dai.DeviceInfo(mx_id)
self.mx_id = self.dev_info.getMxId()
self.versao = self.dev_info.name
_camera = ContextoGlobalRedis.get_camera(self.mx_id) or {} _camera = ContextoGlobalRedis.get_camera(self.mx_id) or {}
_camera["mx_id"] = self.mx_id _camera["mx_id"] = self.mx_id
_camera["versao"] = self.versao _camera["versao"] = self.versao
@ -88,7 +89,7 @@ class CameraOak:
self.q_seg = self.device.getOutputQueue(name="seg", maxSize=1, blocking=False) self.q_seg = self.device.getOutputQueue(name="seg", maxSize=1, blocking=False)
if self.modelo_ia_det is not None: if self.modelo_ia_det is not None:
self.q_det = self.device.getOutputQueue(name="det", maxSize=4, blocking=False) self.q_det = self.device.getOutputQueue(name="det", maxSize=1, blocking=False)
if self.dispositivo == T_Code.Snr: if self.dispositivo == T_Code.Snr:
dadosSnr = ContextoGlobalRedis.get_operacao().get("Snr", {}) dadosSnr = ContextoGlobalRedis.get_operacao().get("Snr", {})
@ -204,27 +205,24 @@ class CameraOak:
script = pipeline.create(dai.node.Script) script = pipeline.create(dai.node.Script)
if self.modelo_ia_seg is not None or self.modelo_ia_det is not None: if self.modelo_ia_seg is not None or self.modelo_ia_det is not None:
try: try:
N_seg = (self.modelo_ia_seg or {}).get("det_every_n", 1) N_seg = (self.modelo_ia_seg or {}).get("seg_every_n", 1)
N_det = (self.modelo_ia_det or {}).get("det_every_n", 1) N_det = (self.modelo_ia_det or {}).get("det_every_n", 1)
script.setProcessor(dai.ProcessorType.LEON_CSS) script.setProcessor(dai.ProcessorType.LEON_CSS)
script.setScript(f""" script.setScript(f"""
from time import monotonic
i = 0 i = 0
while True: while True:
f = node.io['in'].get() f = node.io['in'].get()
""" send_seg = {1 if self.modelo_ia_seg else 0} and (i % {N_seg} == 0)
+ send_det = {1 if self.modelo_ia_det else 0} and (i % {N_det} == 0)
f"""
if i % {N_seg} == 0: if send_seg and send_det:
node.io['toSeg'].send(f) # move o original
node.io['toDet'].send(f) # clone para a segunda rota
elif send_seg:
node.io['toSeg'].send(f) node.io['toSeg'].send(f)
""" if self.modelo_ia_seg is not None else "" elif send_det:
+
f"""
if i % {N_det} == 0:
node.io['toDet'].send(f) node.io['toDet'].send(f)
""" if self.modelo_ia_det is not None else "" # se nenhum for enviar, apenas descarta 'f'
+
f"""
i += 1 i += 1
""") """)
cam.video.link(script.inputs['in']) cam.video.link(script.inputs['in'])
@ -299,6 +297,7 @@ class CameraOak:
det.out.link(xout_det.input) det.out.link(xout_det.input)
script.outputs['toDet'].link(manip_det.inputImage) script.outputs['toDet'].link(manip_det.inputImage)
#cam.video.link(manip_det.inputImage)
# (opcional) passthrough para sincronizar timestamp/frame com a detecção # (opcional) passthrough para sincronizar timestamp/frame com a detecção
# xout_det_img = pipeline.createXLinkOut() # xout_det_img = pipeline.createXLinkOut()

View File

@ -31,21 +31,26 @@ class CameraManager:
else: else:
cam_existente["timestamp"] = self._ultimo_scan cam_existente["timestamp"] = self._ultimo_scan
ContextoGlobalRedis.set(CtxKey.DadosCameras, cameras_mapeadas) ids_para_remover = []
for cam_id, val in cameras_mapeadas.items():
for cam_id in cameras_mapeadas.keys():
cam = ContextoGlobalRedis.get_camera(cam_id) cam = ContextoGlobalRedis.get_camera(cam_id)
if cam is not None: if cam is not None and cam.get("saude", {}).get("status", StatusModulo.DESCONECTADO.value) != StatusModulo.DESCONECTADO.value:
if cam.get("dispositivo", T_Code.Vzo.value) == T_Code.Snr.value: if cam.get("dispositivo", T_Code.Vzo.value) == T_Code.Snr.value:
ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.AtualizarSaudeCamera.value } ) ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.AtualizarSaudeCamera.value } )
elif cam.get("dispositivo", T_Code.Vzo.value) == T_Code.Cam.value: elif cam.get("dispositivo", T_Code.Vzo.value) == T_Code.Cam.value:
ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerRx, { "cmd": WeedWorkerCommandType.AtualizarSaudeCamera.value } ) ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerRx, { "cmd": WeedWorkerCommandType.AtualizarSaudeCamera.value } )
elif cam is not None and val.get("timestamp", 0) < self._ultimo_scan:
ids_para_remover.append(cam_id)
for cam_id in ids_para_remover:
cameras_mapeadas.pop(cam_id, None)
ContextoGlobalRedis.set(CtxKey.DadosCameras, cameras_mapeadas)
visual_worker_camera_id = ContextoGlobalRedis.get_equipamento().get("camera_caminho_id") visual_worker_camera_id = ContextoGlobalRedis.get_equipamento().get("camera_caminho_id")
if visual_worker_camera_id is not None and visual_worker_camera_id in dispositivos_serializados: if visual_worker_camera_id is not None and visual_worker_camera_id in cameras_mapeadas:
ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.IniciarCameraManager.value, "params": visual_worker_camera_id } ) ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.IniciarCameraManager.value, "params": visual_worker_camera_id } )
weed_worker_camera_id = ContextoGlobalRedis.get_equipamento().get("camera_solo_id") weed_worker_camera_id = ContextoGlobalRedis.get_equipamento().get("camera_solo_id")
if weed_worker_camera_id is not None and weed_worker_camera_id in dispositivos_serializados: if weed_worker_camera_id is not None and weed_worker_camera_id in cameras_mapeadas:
ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerRx, { "cmd": WeedWorkerCommandType.IniciarCameraManager.value, "params": weed_worker_camera_id } ) ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerRx, { "cmd": WeedWorkerCommandType.IniciarCameraManager.value, "params": weed_worker_camera_id } )

View File

@ -30,20 +30,25 @@ class ModuloAtuador(ModuloDiagnosticoBase):
FREQ_MIN = FREQ_BASE * 0.5 FREQ_MIN = FREQ_BASE * 0.5
SAUDE_MIN_ALERTA = 80 SAUDE_MIN_ALERTA = 80
sensor_massa_ativado = False
for tipo, sensores in dados_atu.get("sensores", {}).items(): for tipo, sensores in dados_atu.get("sensores", {}).items():
for bico_id, dados_bico in sensores.items(): for sensor_id, dados_sensor in sensores.items():
saude = 100 saude = 100
motivos = [] motivos = []
condicoes_operacionais = [] condicoes_operacionais = []
aferir = dados_bico.get("aferir", False) aferir = dados_sensor.get("aferir", False)
padroes = self.padroes_sensores.get(tipo, {}).get(bico_id, {}) padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
conectado = dados_bico.get("conectado", False) conectado = dados_sensor.get("conectado", False)
mandatorio = dados_bico.get("mandatorio", False) mandatorio = dados_sensor.get("mandatorio", False)
if dados_sensor.get("label") == "MASRS" and conectado and mandatorio and aferir: sensor_massa_ativado = True
if not conectado: if not conectado:
saude = 0 saude = 0
motivos.append(f"desconectado") motivos.append(f"desconectado")
else: else:
for campo in dados_bico.get("dados", []): for campo in dados_sensor.get("dados", []):
label = campo.get("label") label = campo.get("label")
if label is None: if label is None:
continue continue
@ -69,10 +74,10 @@ class ModuloAtuador(ModuloDiagnosticoBase):
"descricao": f"{label} fora do padrão" "descricao": f"{label} fora do padrão"
}) })
saude = max(0, saude) saude = max(0, saude)
chave = f"{S_Code(int(tipo)).name}_{bico_id}" chave = f"{S_Code(int(tipo)).name}_{sensor_id}"
saude_mod = { saude_mod = {
"id": bico_id, "id": sensor_id,
"label": dados_bico.get("label"), "label": dados_sensor.get("label"),
"status": (StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA).value, "status": (StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA).value,
"saude": saude, "saude": saude,
"motivos": motivos, "motivos": motivos,
@ -84,7 +89,7 @@ class ModuloAtuador(ModuloDiagnosticoBase):
total_ativos += 1 total_ativos += 1
saude_total += saude saude_total += saude
motivos_gerais.extend([f"{chave}: {m}" for m in motivos]) motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
dados_bico["saude"] = saude_mod dados_sensor["saude"] = saude_mod
for tipo, bicos in dados_atu.get("bicos", {}).items(): for tipo, bicos in dados_atu.get("bicos", {}).items():
for bico_id, dados_bico in bicos.items(): for bico_id, dados_bico in bicos.items():
@ -198,7 +203,34 @@ class ModuloAtuador(ModuloDiagnosticoBase):
motivos_gerais.extend([f"{chave}: {m}" for m in motivos]) motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
dados_bomba["saude"] = saude_mod dados_bomba["saude"] = saude_mod
saude_final = (saude_total // total_ativos) if total_ativos > 0 else 100 cond_op_mod = []
penalidade_percentual_herbicida = 0
if sensor_massa_ativado:
percent_min = ContextoGlobalRedis.get_equipamento().get("percentual_reservatorio_min", 5.0)
percent_atual = ContextoGlobalRedis.get_contexto().get("Gerais", {}).get("percentual_reservatorio", 0.0)
if percent_atual < percent_min:
penalidade_percentual_herbicida = 50
cond_op_mod.append({
"valor": percent_atual,
"severidade": 100,
"descricao": f"Nível do reservatório de herbicida abaixo do limite mínimo!"
})
elif percent_atual < 20.0:
penalidade_percentual_herbicida = 21
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": f"Nível do reservatório de herbicida baixo!"
})
else:
cond_op_mod.append({
"valor": 0,
"severidade": 100,
"descricao": f"Sensor do nível do reservatório desconectado!"
})
saude_final = (saude_total // total_ativos) if total_ativos > 0 else 0
saude_final = saude_final - penalidade_percentual_herbicida
status = StatusModulo.OPERANTE status = StatusModulo.OPERANTE
if not mod_conectado: if not mod_conectado:
@ -211,9 +243,10 @@ class ModuloAtuador(ModuloDiagnosticoBase):
saude_geral = { saude_geral = {
"conectado": mod_conectado, "conectado": mod_conectado,
"status": status.value, "status": status.value,
"saude": saude_final, "saude": max(0, saude_final),
"motivos": motivos_gerais, "motivos": motivos_gerais,
"saude_individual": saude_individual "saude_individual": saude_individual,
"condicoes_operacionais": cond_op_mod
} }
ContextoGlobalRedis.atualizar_ctx_dict( ContextoGlobalRedis.atualizar_ctx_dict(

View File

@ -1,3 +1,4 @@
import math
import time import time
from shared.enums import T_Code, StatusModulo from shared.enums import T_Code, StatusModulo
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
@ -17,7 +18,8 @@ class ModuloGPS(ModuloDiagnosticoBase):
SAUDE_MIN_ALERTA = 80 SAUDE_MIN_ALERTA = 80
conectado = modulo.get("conectado", False) conectado = modulo.get("conectado", False)
freq_hz = modulo.get("freq", 0.0) freq_pos = modulo.get("freq", {}).get("posicao", 0.0)
freq_ori = modulo.get("freq", {}).get("orientacao", 0.0)
fix = modulo.get("fix", 0) fix = modulo.get("fix", 0)
rtk = modulo.get("rtk", False) rtk = modulo.get("rtk", False)
hAcc = modulo.get("hAcc", 0) hAcc = modulo.get("hAcc", 0)
@ -30,16 +32,26 @@ class ModuloGPS(ModuloDiagnosticoBase):
if not conectado: if not conectado:
motivos.append("GPS desconectado") motivos.append("GPS desconectado")
saude = 0 saude = 0
elif freq_hz <= FREQ_MIN: elif freq_pos <= FREQ_MIN:
motivos.append("Sem resposta do GPS") motivos.append("Sem resposta de coordenadas")
saude = 0
elif freq_ori <= FREQ_MIN:
motivos.append("Sem resposta de orientacao")
saude = 0 saude = 0
else: else:
f = min(freq_hz, FREQ_BASE) f_pos = min(freq_pos, FREQ_BASE)
erro = (FREQ_BASE - f) / FREQ_BASE erro_pos = (FREQ_BASE - f_pos) / FREQ_BASE
p_freq = min(int(erro * 100), 40) p_freq_pos = min(int(erro_pos * 100), 40)
saude -= abs(p_freq) saude -= abs(p_freq_pos)
if abs(p_freq) > 0: if abs(p_freq_pos) > 0:
motivos.append(f"Frequência baixa: {f:.2f} Hz (penalidade {p_freq}%)") motivos.append(f"Frequência de coordenadas baixa: {f_pos:.2f} Hz (penalidade {p_freq_pos}%)")
f_ori = min(freq_ori, FREQ_BASE)
erro_ori = (FREQ_BASE - f_ori) / FREQ_BASE
p_freq_ori = min(int(erro_ori * 100), 40)
saude -= abs(p_freq_ori)
if abs(p_freq_ori) > 0:
motivos.append(f"Frequência de orientacao baixa: {f_ori:.2f} Hz (penalidade {p_freq_ori}%)")
# 🔹 Fix # 🔹 Fix
p_fix = self._penalidade_fix(fix) p_fix = self._penalidade_fix(fix)
@ -235,11 +247,7 @@ class ModuloGPS(ModuloDiagnosticoBase):
if h < 200: return 20 if h < 200: return 20
return 30 return 30
def _penalidade_age(self, age) -> int: def _penalidade_age(self, age: float) -> float:
if age < 0: return 35 if age < 0:
if age < 0.5: return 0
if age < 2.0: return 5
if age < 5.0: return 10
if age < 10.0: return 18
if age < 15.0: return 25
return 35 return 35
return round(min(35, 35 * (1 - math.exp(-age / 8))), 2)

View File

@ -29,6 +29,8 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
FREQ_MIN = FREQ_BASE * 0.5 FREQ_MIN = FREQ_BASE * 0.5
SAUDE_MIN_ALERTA = 80 SAUDE_MIN_ALERTA = 80
sensor_bateria_ativado = False
for tipo, sensores in dados_sen.get("sensores", {}).items(): for tipo, sensores in dados_sen.get("sensores", {}).items():
for sensor_id, dados_sensor in sensores.items(): for sensor_id, dados_sensor in sensores.items():
saude = 100 saude = 100
@ -38,6 +40,9 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {}) padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
conectado = dados_sensor.get("conectado", False) conectado = dados_sensor.get("conectado", False)
mandatorio = dados_sensor.get("mandatorio", False) mandatorio = dados_sensor.get("mandatorio", False)
if dados_sensor.get("label") == "AB36V" and conectado and mandatorio and aferir: sensor_bateria_ativado = True
if not conectado: if not conectado:
saude = 0 saude = 0
motivos.append(f"desconectado") motivos.append(f"desconectado")
@ -138,7 +143,34 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
motivos_gerais.extend([f"{chave}: {m}" for m in motivos]) motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
dados_servo["saude"] = saude_mod dados_servo["saude"] = saude_mod
saude_final = (saude_total // total_ativos) if total_ativos > 0 else 100 cond_op_mod = []
penalidade_percentual_bateria = 0
if sensor_bateria_ativado:
percent_min = ContextoGlobalRedis.get_equipamento().get("percentual_tensao_bateria_min", 5.0)
percent_atual = ContextoGlobalRedis.get_contexto().get("Gerais", {}).get("percentual_bateria", 0.0)
if percent_atual < percent_min:
penalidade_percentual_bateria = 70
cond_op_mod.append({
"valor": percent_atual,
"severidade": 100,
"descricao": f"Nível da bateria abaixo do limite mínimo!"
})
elif percent_atual < 25.0:
penalidade_percentual_bateria = 25
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": f"Nível da bateria baixo!"
})
else:
cond_op_mod.append({
"valor": 0,
"severidade": 100,
"descricao": f"Sensor de nível da bateria desconectado!"
})
saude_final = (saude_total // total_ativos) if total_ativos > 0 else 0
saude_final = saude_final - penalidade_percentual_bateria
status = StatusModulo.OPERANTE status = StatusModulo.OPERANTE
if not mod_conectado: if not mod_conectado:
@ -151,9 +183,10 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
saude_geral = { saude_geral = {
"conectado": mod_conectado, "conectado": mod_conectado,
"status": status.value, "status": status.value,
"saude": saude_final, "saude": max(0, saude_final),
"motivos": motivos_gerais, "motivos": motivos_gerais,
"saude_individual": saude_individual "saude_individual": saude_individual,
"condicoes_operacionais": cond_op_mod
} }
ContextoGlobalRedis.atualizar_ctx_dict( ContextoGlobalRedis.atualizar_ctx_dict(

View File

@ -24,7 +24,7 @@ def main():
fila = WorkerFilaMensagens("controle", fila_callback, 1, True, 100) fila = WorkerFilaMensagens("controle", fila_callback, 1, True, 100)
loop_fixo = True loop_fixo = True
frequencia_loop = 5 # hz frequencia_loop = 15 # hz
def loop_ativo(): def loop_ativo():
t0 = time.time() t0 = time.time()

View File

@ -7,7 +7,7 @@ from manager_worker.modulos.mpc import get_mpc, comando_parado
from manager_worker.modulos.pid import PIDAdaptativo from manager_worker.modulos.pid import PIDAdaptativo
from visual_worker.processamento.costmap_fuser import unpack_snapshot from visual_worker.processamento.costmap_fuser import unpack_snapshot
def definir_comando(pid: PIDAdaptativo): def definir_comando(pid: PIDAdaptativo, envio_necessario: bool):
try: try:
_operacao = ContextoGlobalRedis.get_operacao() _operacao = ContextoGlobalRedis.get_operacao()
_tipo_controle = TiposControladorDirecional(_operacao.get("Dir", {}).get("tipo_controle", TiposControladorDirecional.MPC.value)) _tipo_controle = TiposControladorDirecional(_operacao.get("Dir", {}).get("tipo_controle", TiposControladorDirecional.MPC.value))
@ -47,6 +47,13 @@ def definir_comando(pid: PIDAdaptativo):
pass_locked=_gps_passos_travados pass_locked=_gps_passos_travados
) )
latencia = time.perf_counter() - _gps.get("timestamp", {}).get("posicao", 0.0)
if not _gps_pos_atualizada and not envio_necessario:
return _comando_anterior(latencia, False)
Vmin = 0.1 # if _trajetoria.get("status", StatusCarroMapa.Parado.value) == StatusCarroMapa.CaminhandoRua.value else 0.8
contexto = { contexto = {
"Operacao": { "Operacao": {
"Status": _operacao.get("status"), "Status": _operacao.get("status"),
@ -56,16 +63,18 @@ def definir_comando(pid: PIDAdaptativo):
"Latitude": _gps.get("lat", 0), "Latitude": _gps.get("lat", 0),
"Longitude": _gps.get("lon", 0), "Longitude": _gps.get("lon", 0),
"AnguloCarro": _gps.get("theta", 0), "AnguloCarro": _gps.get("theta", 0),
"PassosAtraso": _gps_passos_travados "PassosAtraso": _gps_passos_travados,
"Timestamp": _gps.get("timestamp", {}).get("posicao", 0.0)
}, },
"Carro": { "Carro": {
"Velocidade": max(0.01, _contexto.get("Gerais", {}).get("velocidade_ms", 0.0)), "Velocidade": max(Vmin, _contexto.get("Gerais", {}).get("velocidade_ms", 0.0)),
"Status": _trajetoria.get("status", StatusCarroMapa.Parado.value), "Status": _trajetoria.get("status", StatusCarroMapa.Parado.value),
"ManobrandoEntreRuas": _trajetoria.get("manobrando", False), "ManobrandoEntreRuas": _trajetoria.get("manobrando", False),
"DentroCorredor": _trajetoria.get("CorredorAtual", {}).get("dentro", False), "DentroCorredor": _trajetoria.get("CorredorAtual", {}).get("dentro", False),
"DistanciaEsquerda": _trajetoria.get("CorredorAtual", {}).get("dist_esq", 0.0), "DistanciaEsquerda": _trajetoria.get("CorredorAtual", {}).get("dist_esq", 0.0),
"DistanciaDireita": _trajetoria.get("CorredorAtual", {}).get("dist_dir", 0.0), "DistanciaDireita": _trajetoria.get("CorredorAtual", {}).get("dist_dir", 0.0),
"IdxProximoPonto": _trajetoria.get("idx_proximo_ponto", 0), "IdxProximoPonto": _trajetoria.get("idx_proximo_ponto", 0),
"IdxPontoAlvo": _trajetoria.get("idx_ponto_alvo", 0),
"AnguloCaminho": _trajetoria.get("CorredorAtual", {}).get("angulo", 0.0), "AnguloCaminho": _trajetoria.get("CorredorAtual", {}).get("angulo", 0.0),
"ErroAngular": _trajetoria.get("erro_angular", 0), "ErroAngular": _trajetoria.get("erro_angular", 0),
"ErroAngularCaminho": _trajetoria.get("erro_angular_caminho", 0), "ErroAngularCaminho": _trajetoria.get("erro_angular_caminho", 0),
@ -93,7 +102,7 @@ def definir_comando(pid: PIDAdaptativo):
}, },
"RegrasAtivas": { "RegrasAtivas": {
"matriz_custo": False, "matriz_custo": False,
"deteccao_obstaculos": True, "deteccao_obstaculos": False,
}, },
"Equipamento": { "Equipamento": {
"largura": _equipamento.get("largura", 0.85), "largura": _equipamento.get("largura", 0.85),
@ -108,8 +117,8 @@ def definir_comando(pid: PIDAdaptativo):
if _tipo_controle == TiposControladorDirecional.MPC: if _tipo_controle == TiposControladorDirecional.MPC:
comando = _regras_taticas(contexto) comando = _regras_taticas(contexto)
if (comando["comando_definido"]): if (comando.get("comando_definido", False)):
return comando["angulo"], comando["tipo"], comando["simulacao"], comando["parada_necessaria"], False return _montar_comando_retorno(comando, latencia=latencia)
else: else:
op_modo = ModoOperacao(_operacao.get("modo", ModoOperacao.NaoDefinido.value)) op_modo = ModoOperacao(_operacao.get("modo", ModoOperacao.NaoDefinido.value))
if op_modo == ModoOperacao.MapaGPS: if op_modo == ModoOperacao.MapaGPS:
@ -117,8 +126,8 @@ def definir_comando(pid: PIDAdaptativo):
elif op_modo == ModoOperacao.MapeamentoVisual: elif op_modo == ModoOperacao.MapeamentoVisual:
return _comando_mapeamento_visual(contexto) return _comando_mapeamento_visual(contexto)
else: else:
comando = _comando_direcional_parado() comando = _comando_direcional_parado(True)
return comando["angulo"], comando["tipo"], comando["simulacao"], comando["parada_necessaria"], False return _montar_comando_retorno(comando, latencia=latencia)
elif _tipo_controle == TiposControladorDirecional.PID: elif _tipo_controle == TiposControladorDirecional.PID:
_controle = ContextoGlobalRedis.get_controle() _controle = ContextoGlobalRedis.get_controle()
@ -145,7 +154,17 @@ def definir_comando(pid: PIDAdaptativo):
tipo_movimento = TipoMovimentoDirecional.MovimentoArco if usar_arco else TipoMovimentoDirecional.RodasDianteiras tipo_movimento = TipoMovimentoDirecional.MovimentoArco if usar_arco else TipoMovimentoDirecional.RodasDianteiras
angulo = round(pid.atualizar(erro_angular), 2) angulo = round(pid.atualizar(erro_angular), 2)
print(f"Angulo: {angulo}, Tipo Movimento: {tipo_movimento.name}, Erro Angular: {erro_angular:.2f}") print(f"Angulo: {angulo}, Tipo Movimento: {tipo_movimento.name}, Erro Angular: {erro_angular:.2f}")
return angulo, tipo_movimento, [], False _cmd = {
"comando_definido": True,
"enviar_comando": True,
"parada_necessaria": True,
"erro": False,
"latencia": 0.0,
"angulo": angulo,
"tipo": tipo_movimento.value,
"simulacao": []
}
return _montar_comando_retorno(_cmd)
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao definir comando direcional: {e}") mostrar_log(f"❌ Erro ao definir comando direcional: {e}")
@ -153,13 +172,17 @@ def definir_comando(pid: PIDAdaptativo):
def _comando_direcional_parado(definido: bool): def _comando_direcional_parado(definido: bool):
try: try:
angulo, tipo = comando_parado() angulo, tipo = comando_parado()
return { _cmd = {
"comando_definido": definido,
"enviar_comando": True,
"parada_necessaria": True,
"erro": False,
"latencia": 0.0,
"angulo": angulo, "angulo": angulo,
"tipo": tipo.value, "tipo": tipo.value,
"simulacao": [], "simulacao": []
"parada_necessaria": True,
"comando_definido": definido
} }
return _cmd
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao montar comando direcional parado: {e}") mostrar_log(f"❌ Erro ao montar comando direcional parado: {e}")
@ -247,7 +270,7 @@ def _comando_mapa_gps_mpc(contexto):
#input("⏸️ Pressione Enter para continuar...") #input("⏸️ Pressione Enter para continuar...")
return comando.get("angulo", 0.0), comando.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value), comando.get("simulacao", []), comando.get("parada_necessaria", False), comando.get("erro", False) return _montar_comando_retorno(comando)
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao definir comando MapaGPS: {e}") mostrar_log(f"❌ Erro ao definir comando MapaGPS: {e}")
@ -283,16 +306,38 @@ def _comando_mapeamento_visual(contexto):
# 🚦 Define se deve parar # 🚦 Define se deve parar
parada_necessaria = (status_carro == StatusCarroMapa.Parado) parada_necessaria = (status_carro == StatusCarroMapa.Parado)
latencia = time.perf_counter() - contexto.get("GPS", {}).get("Timestamp", 0.0)
# 🔧 Monta comando final # 🔧 Monta comando final
comando = { comando = {
"enviar_comando": True,
"parada_necessaria": parada_necessaria,
"erro": False,
"latencia": latencia,
"angulo": round(angulo, 2), "angulo": round(angulo, 2),
"tipo": tipo_movimento.value, "tipo": tipo_movimento.value,
"simulacao": [], "simulacao": []
"parada_necessaria": parada_necessaria
} }
return _montar_comando_retorno(comando)
return comando["angulo"], comando["tipo"], comando["simulacao"], comando["parada_necessaria"], False
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao definir comando MapeamentoVisual: {e}") mostrar_log(f"❌ Erro ao definir comando MapeamentoVisual: {e}")
return 0.0, TipoMovimentoDirecional.RodasDianteiras.value, [], True return 0.0, TipoMovimentoDirecional.RodasDianteiras.value, [], True
def _comando_anterior(latencia, enviar):
_controle = ContextoGlobalRedis.get_controle()
comando_anterior = {
"enviar_comando": enviar,
"parada_necessaria": False,
"erro": False,
"latencia": latencia,
"angulo": _controle.get("angulo_sp", 0.0),
"tipo": _controle.get("tipo_movimento_direcional", TipoMovimentoDirecional.RodasDianteiras.value),
"simulacao": []
}
return _montar_comando_retorno(comando_anterior)
def _montar_comando_retorno(comando, latencia: float = -1):
if latencia > -1:
comando["latencia"] = latencia
return comando

View File

@ -26,9 +26,11 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
_controle = ContextoGlobalRedis.get_controle() _controle = ContextoGlobalRedis.get_controle()
pulverizador_automatico = _controle.get("pulverizador_automatico", False) pulverizador_automatico = _controle.get("pulverizador_automatico", False)
ervas_no_radar = _contexto.get("Gerais", {}).get("ervas_no_radar", False) ervas_no_radar = _contexto.get("Gerais", {}).get("ervas_no_radar", False)
percentual_ervas_no_radar = _contexto.get("Gerais", {}).get("percentual_ervas_no_radar", 0)
vel_min = _operacao.get("Mov", {}).get("percent_vel_min", 0) vel_min = _operacao.get("Mov", {}).get("percent_vel_min", 0)
vel_max = _operacao.get("Mov", {}).get("percent_vel_max", 100) vel_max = _operacao.get("Mov", {}).get("percent_vel_max", 100)
ang_max = _operacao.get("Dir", {}).get("angulo_max", 30) ang_max = _operacao.get("Dir", {}).get("angulo_max", 30)
ervas_min_percenet = 15.0
status_carro = StatusCarroMapa.Parado status_carro = StatusCarroMapa.Parado
@ -55,9 +57,9 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
else: else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro) velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro)
elif status_carro in [StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando]: elif status_carro in [StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando]:
velocidade_sp = vel_min velocidade_sp = min(15, vel_min)
elif status_carro == StatusCarroMapa.CaminhandoRua: elif status_carro == StatusCarroMapa.CaminhandoRua:
if pulverizador_automatico and ervas_no_radar: if pulverizador_automatico and ervas_no_radar and percentual_ervas_no_radar > ervas_min_percenet:
velocidade_sp = vel_min velocidade_sp = vel_min
else: else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro) velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro)
@ -65,7 +67,7 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
# Aplica limites # Aplica limites
velocidade_sp = max(vel_min, min(vel_max, velocidade_sp)) velocidade_sp = max(vel_min, min(vel_max, velocidade_sp))
if velocidade_sp <= vel_min: if velocidade_sp < 1.0:
filtro_vel.reset(velocidade_sp) filtro_vel.reset(velocidade_sp)
else: else:
velocidade_sp = filtro_vel.filtrar(velocidade_sp) velocidade_sp = filtro_vel.filtrar(velocidade_sp)

View File

@ -1,3 +1,4 @@
from copy import deepcopy
import heapq import heapq
import math import math
import time import time
@ -35,7 +36,7 @@ class ControladorMPC:
self.ultima_atualizacao = None self.ultima_atualizacao = None
self.dt = 0 self.dt = 0
print(parametros_mpc) #print(parametros_mpc)
self.angulo_max_graus = parametros_mpc.get("angulo_max_graus", 30.0) self.angulo_max_graus = parametros_mpc.get("angulo_max_graus", 30.0)
self.velocidade_min = parametros_mpc.get("velocidade_min", 0.4) self.velocidade_min = parametros_mpc.get("velocidade_min", 0.4)
self.velocidade_max = parametros_mpc.get("velocidade_max", 1.9) self.velocidade_max = parametros_mpc.get("velocidade_max", 1.9)
@ -364,6 +365,153 @@ class ControladorMPC:
except Exception as e: except Exception as e:
mostrar_log(f"Erro ao calcular pesos de movimento: {e}") mostrar_log(f"Erro ao calcular pesos de movimento: {e}")
def corrigir_pose_por_latencia(
self,
x, y, theta, visitados,
latency_s, # atraso a compensar (s)
dt, # dt nominal do seu controle
nova_posicao_fn, # self._nova_posicao
u_hold, # {'v': m/s, 'omega': rad/s} (ou forneça 'tipo' e 'angulo' + calc_omega)
cmd_seq=None, # opcional: [(dur_s, u_dict), ...] que cobre latency_s
time_left_ms=lambda: 1e9,
calc_omega_fn=None # opcional: fn(tipo, angulo_rad, v) -> omega
):
"""
Propaga (x,y,theta) por 'latency_s' usando 'nova_posicao_fn' que depende de self.dt.
Retorna (x, y, theta) estimados no presente.
"""
visitados_copy = deepcopy(visitados)
latency_s = max(0.0, float(latency_s))
if latency_s == 0.0:
return x, y, theta, visitados_copy
# --- constrói sequência efetiva de comandos ---
if not cmd_seq:
cmd_seq = [(latency_s, u_hold)]
else:
total = sum(max(0.0, d) for d, _ in cmd_seq)
if total < latency_s:
cmd_seq = list(cmd_seq) + [(latency_s - total, cmd_seq[-1][1])]
# --- adaptador para usar dt_eff com fn que usa self.dt ---
def aplicar_passo_dt(xk, yk, thetak, u, dt_eff):
# garantir que temos omega e v
v = u.get('v', 0.0)
if 'omega' in u:
omega = u['omega']
else:
if calc_omega_fn is not None and 'tipo' in u and 'angulo' in u:
omega = calc_omega_fn(v, u['angulo'], u['tipo'])
else:
omega = 0.0 # fallback: reta
old_dt = self.dt
try:
self.dt = dt_eff
return nova_posicao_fn(xk, yk, thetak, omega, v, u.get('tipo'), u.get('angulo', 0.0))
finally:
self.dt = old_dt
# --- integra por tempo, quebrando cada trecho em subpassos ~dt ---
t_rem = latency_s
for dur_s, u in cmd_seq:
if t_rem <= 0.0:
break
seg = min(dur_s, t_rem)
if seg <= 0.0:
continue
n = max(1, int(round(seg / dt)))
dt_eff = seg / n
for _ in range(n):
if time_left_ms() <= 0.0:
return x, y, theta, visitados_copy
x, y, theta = aplicar_passo_dt(x, y, theta, u, dt_eff)
idx_alvo = self._corrigir_pontos_visitados(x, y, visitados_copy)
t_rem -= seg
# normaliza theta se quiser
# theta = (theta + np.pi) % (2*np.pi) - np.pi
return x, y, theta, visitados_copy
def _wrap_pi(self, a):
try:
return (a + np.pi) % (2*np.pi) - np.pi
except Exception as e:
mostrar_log(f"Erro no wrap_pi: {e}")
def _circ_mean(self, a, b, wa=0.5):
wb = 1.0 - wa
ux = wa*np.cos(a) + wb*np.cos(b)
uy = wa*np.sin(a) + wb*np.sin(b)
return float(np.arctan2(uy, ux))
def _align_to_ref(self, theta, ref):
"""Devolve theta ou theta+pi, o que fica mais próximo de ref (evita flip 180°)."""
a = self._wrap_pi(theta - ref)
b = self._wrap_pi(theta + np.pi - ref)
return theta if abs(a) <= abs(b) else self._wrap_pi(theta + np.pi)
def delta_por_dist_esq_dir(
self,
estado_atual, # (x, y, theta_atual)
ponto_alvo_xy, # (x, y) do seu alvo (use seu idx_ponto_alvo/pure pursuit)
orient_corredor, # ângulo do caminho (rad) — se vier em graus, converta antes
dist_esq, dist_dir, # metros até borda esquerda/direita
angulo_max_graus=24.0,
sigma_factor=0.6, # 0.4..0.8 -> quanto “larga” é a zona de centralização
w_min=0.15, # peso mínimo p/ corredor (não zera nunca)
usar_nudge_centro=True, # opcional: empurrãozinho pro centro
k_e=1.0, v=0.0, v0=0.3 # ganho e velocidade para o “nudge” (tipo Stanley)
):
x, y, theta = estado_atual
# 1) Bearing até o alvo
orient = self.gps_handler.calcular_orientacao(ponto_alvo_xy, estado_atual)
theta_ponto = (orient - estado_atual[2] + np.pi) % (2*np.pi) - np.pi
# 2) Defina erro lateral a partir das distâncias
# Convenção: e_lat > 0 quando você está mais perto da ESQUERDA (dir > esq)
# (Se quiser o oposto, troque o sinal.)
e_lat = float((dist_dir - dist_esq) / 2.0)
print(e_lat)
# 3) Largura efetiva e peso para o ângulo do corredor
half = max((dist_esq + dist_dir) * 0.5, 1e-6)
sigma = sigma_factor * half
# Peso “tipo gaussiana”: centralizado -> w≈1; deslocado -> w≈0 (mas não menos que w_min)
w_rua = float(np.exp(- (e_lat*e_lat) / (2.0*sigma*sigma)))
w_rua = float(np.clip(w_rua, w_min, 1.0))
# 4) Alinhe o ângulo do corredor com o bearing (evita virar “de costas”)
orient_corredor = self._align_to_ref(orient_corredor, theta_ponto)
# 5) Média circular ponderada
theta_ref = self._circ_mean(orient_corredor, theta_ponto, wa=w_rua)
# 6) Opcional: empurrão suave pro centro (evita “andar paralelo” fora do meio)
nudge = 0.0
if usar_nudge_centro:
nudge = np.arctan2(k_e * e_lat, v + v0) # pequeno e estável
# 7) Delta final + clamp
delta = self._wrap_pi(theta_ref - theta) + nudge
ang_max = np.radians(angulo_max_graus)
delta = float(np.clip(delta, -ang_max, ang_max))
debug = {
"e_lat": e_lat, "half": half, "sigma": sigma, "w_rua": w_rua,
"theta_ponto_deg": np.degrees(theta_ponto),
"theta_corr_deg": np.degrees(orient_corredor),
"theta_ref_deg": np.degrees(theta_ref),
"nudge_deg": np.degrees(nudge),
"delta_deg": np.degrees(delta),
}
return delta, theta_ref, debug
# MPC COM GERACAO DE CANDIDATOS RECEDING # MPC COM GERACAO DE CANDIDATOS RECEDING
def compute_receding(self, contexto, comando_anterior): def compute_receding(self, contexto, comando_anterior):
@ -382,13 +530,13 @@ class ControladorMPC:
t_exec = max((now() - self.ultima_atualizacao), 1e-3) t_exec = max((now() - self.ultima_atualizacao), 1e-3)
if comando['erro']: if comando['erro']:
mostrar_log(f"🧭 Direcional MPC | Erro: {comando['erro']} | Parada: {comando['parada_necessaria']} | Movimento: {TipoMovimentoDirecional(comando['tipo']).name} | Ângulo: {comando['angulo']}° | Horizonte: {len(comando['simulacao'])} | dt: {dt_raw:.2f} s | freq = {(1.0 / dt_raw):.2f} Hz | t_exec: {t_exec:.2} s | f_exec: {(1.0 / t_exec):.2f} Hz") mostrar_log(f"🧭 Direcional MPC | Latencia: {comando['latencia']} | Parada: {comando['parada_necessaria']} | Movimento: {TipoMovimentoDirecional(comando['tipo']).name} | Ângulo: {comando['angulo']}° | Horizonte: {len(comando['simulacao'])} | dt: {dt_raw:.2f} s | freq = {(1.0 / dt_raw):.2f} Hz | t_exec: {t_exec:.2} s | f_exec: {(1.0 / t_exec):.2f} Hz")
return comando return comando
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao processar compute MPC: {e}") mostrar_log(f"❌ Erro ao processar compute MPC: {e}")
def _processar_mpc_receding(self, contexto, comando_anterior): def _processar_mpc_receding_old(self, contexto, comando_anterior):
now = time.perf_counter now = time.perf_counter
t0 = now() t0 = now()
@ -404,53 +552,73 @@ class ControladorMPC:
return None return None
GPS = contexto.get("GPS", {}) GPS = contexto.get("GPS", {})
pos_lat = GPS.get("Latitude", 0) pos_lat = GPS.get("Latitude", 0.0)
pos_lon = GPS.get("Longitude", 0) pos_lon = GPS.get("Longitude", 0.0)
pos_theta = GPS.get("AnguloCarro", 0) pos_theta = GPS.get("AnguloCarro", 0.0)
pos_passos_atraso = GPS.get("PassosAtraso", 0) pos_passos_atraso = GPS.get("PassosAtraso", 0)
pos_timestamp = GPS.get("Timestamp", 0.0)
pos_latencia = now() - pos_timestamp
LAT_MAX = 2.0
if pos_latencia >= LAT_MAX:
mostrar_log(f"[GPS] Grande latencia entre coordenadas detectado ({pos_latencia:.3f}/{LAT_MAX}): parando por fallback")
return self._comando_fallback_hot_stop(comando_anterior, latencia=pos_latencia)
if (pos_passos_atraso > 3):
mostrar_log(f"[GPS] Atraso detectado de {(pos_passos_atraso / 3.0):.1f} passos, {pos_latencia:.3f} s")
theta = np.radians(pos_theta)
velocidade = contexto.get("Carro", {}).get("Velocidade", 0)
x, y = self.gps_handler.converter_latlon_para_xz(pos_lat, pos_lon) x, y = self.gps_handler.converter_latlon_para_xz(pos_lat, pos_lon)
theta = np.radians(pos_theta)
x0, y0, t0 = x, y, theta
velocidade = contexto.get("Carro", {}).get("Velocidade", 0)
status_carro = contexto.get("Carro", {}).get("Status", StatusCarroMapa.Parado.value)
idx_proximo_ponto_real = contexto.get("Carro", {}).get("IdxProximoPonto", 0)
idx_ponto_alvo = contexto.get("Carro", {}).get("IdxPontoAlvo", 0)
Nmax = 20 if status_carro == StatusCarroMapa.CaminhandoRua.value else 60
# Comando de hold (se não tiver buffer de comandos aplicados)
u_hold = {
"tipo": comando_anterior.get("tipo"),
"angulo": np.radians(comando_anterior.get("angulo", 0.0)),
"v": velocidade,
"omega": self.gps_handler.calcular_omega(velocidade, np.radians(comando_anterior.get("angulo", 0.0)), comando_anterior.get("tipo"))
}
# Opcional: sequência real de comandos aplicados durante a latência (se você tiver)
cmd_seq = None # ou algo como [(0.12, u1), (0.08, u2), ...]
x, y, theta, pts_visitados = self.corrigir_pose_por_latencia(
x, y, theta, self.visitados_execucao,
latency_s=min(pos_latencia, LAT_MAX),
dt=self.dt,
nova_posicao_fn=self._nova_posicao,
u_hold=u_hold,
cmd_seq=cmd_seq,
time_left_ms=lambda: (deadline - now()) * 1000.0,
calc_omega_fn=self.gps_handler.calcular_omega
)
idx_alvo_correcao = self._corrigir_pontos_visitados(x, y, pts_visitados, idx_proximo_ponto_real) # marca pontos antigos como visitados
#print(f"Anterior: x: {x0}, y: {y0}, t: {t0} - Novo: x: {x}, y: {y}, t: {theta}")
#print(GPS) #print(GPS)
distancia_m = velocidade * self.dt distancia_m = velocidade * self.dt
idx_alvo_real = contexto.get("Carro", {}).get("IdxProximoPonto", 0) ultimo_ponto = idx_proximo_ponto_real >= len(self.pontos_info)
ultimo_ponto = idx_alvo_real >= len(self.pontos_info)
self.tempo_execucao_local = contexto.get("Carro", {}).get("TempoEntreComandos", 0.5) self.tempo_execucao_local = contexto.get("Carro", {}).get("TempoEntreComandos", 0.5)
if ultimo_ponto: if ultimo_ponto:
self.passos_horizonte_local = 1 self.passos_horizonte_local = 1
self.qtd_comandos_sucessivos = 3 self.qtd_comandos_sucessivos = 3
elif distancia_m > 0: elif distancia_m > 0:
passos_total = min(int(self.horizonte / distancia_m), 20) passos_total = min(int(self.horizonte / distancia_m), Nmax)
self.passos_horizonte_local = max(1, math.ceil(self.tempo_execucao_local / self.dt)) self.passos_horizonte_local = max(1, math.ceil(self.tempo_execucao_local / self.dt))
self.qtd_comandos_sucessivos = max(1, passos_total // self.passos_horizonte_local) self.qtd_comandos_sucessivos = max(1, passos_total // self.passos_horizonte_local)
else: else:
self.passos_horizonte_local = 0 self.passos_horizonte_local = 0
self.qtd_comandos_sucessivos = 0 self.qtd_comandos_sucessivos = 0
passos_atraso = contexto.get("Carro", {}).get("PassosAtraso", 1)
angulo_anterior = np.radians(comando_anterior.get("angulo", 0)) angulo_anterior = np.radians(comando_anterior.get("angulo", 0))
tipo_anterior = TipoMovimentoDirecional(comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value)) tipo_anterior = TipoMovimentoDirecional(comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value))
max_simulacoes = min(passos_atraso + pos_passos_atraso, 5)
if max_simulacoes >= 5:
mostrar_log(f"[GPS] Grande atraso detectado: parando por fallback")
return self._comando_fallback_hot_stop(comando_anterior)
if (pos_passos_atraso > 0):
mostrar_log(f"[GPS] Atraso detectado: simulando {pos_passos_atraso} passos")
for passo in range(max_simulacoes):
if ms_left() <= 0:
return self._comando_fallback_hot_stop(comando_anterior)
_, posicao_futura, _, pts_visitados = self._simular_passo(x, y, theta, tipo_anterior, angulo_anterior, {}, { "tipo": tipo_anterior, "angulo": angulo_anterior }, contexto, self.visitados_execucao.copy())
x, y, theta = posicao_futura[-1]
simulacao_latlon = [] simulacao_latlon = []
idx_alvo_correcao = self._corrigir_pontos_visitados(x, y, pts_visitados, idx_alvo_real) # marca pontos antigos como visitados
#mostrar_log(f"previsao_futura em {(t_1 - t_0):.4f}s, correcao_pontos_visitados em {(t_2 - t_1):.4f}") #mostrar_log(f"previsao_futura em {(t_1 - t_0):.4f}s, correcao_pontos_visitados em {(t_2 - t_1):.4f}")
@ -471,6 +639,7 @@ class ControladorMPC:
est = max(1.0, getattr(self, "_t_est_passo_ms", 6.0)) est = max(1.0, getattr(self, "_t_est_passo_ms", 6.0))
exp_budget = max(4, int(ms_left() // est)) exp_budget = max(4, int(ms_left() // est))
#print(f"Comecando, idx_proximo_ponto_real: {idx_proximo_ponto_real}, idx_alvo_correcao: {idx_alvo_correcao}")
for passo in range(self.qtd_comandos_sucessivos): for passo in range(self.qtd_comandos_sucessivos):
if ms_left() <= 0: if ms_left() <= 0:
break break
@ -480,13 +649,18 @@ class ControladorMPC:
if exp_budget <= 0 or ms_left() <= 0: if exp_budget <= 0 or ms_left() <= 0:
break break
x_atual, y_atual, theta_atual = candidato["x"], candidato["y"], candidato["theta"] x_atual, y_atual, theta_atual = candidato["x"], candidato["y"], candidato["theta"]
visitados = candidato["visitados"].copy() visitados = deepcopy(candidato["visitados"])
cmd_anterior = { cmd_anterior = {
"tipo": candidato["comandos"][-1][0], "tipo": candidato["comandos"][-1][0],
"angulo": candidato["comandos"][-1][1], "angulo": candidato["comandos"][-1][1],
} }
idx_alvo = self._corrigir_pontos_visitados(x_atual, y_atual, visitados) idx_alvo = self._corrigir_pontos_visitados(x_atual, y_atual, visitados)
ponto_alvo = self.pontos_info[idx_alvo]["xy"] #if passo == 0 and idx_alvo < idx_ponto_alvo:
# print(f"ponto alvo alterado de {idx_alvo} para {idx_ponto_alvo}")
# idx_alvo = idx_ponto_alvo
if idx_alvo < len(self.pontos_info) - 1:
idx_alvo = idx_alvo + 1
ponto_alvo = self.pontos_info[idx_alvo]
tipos, angs, custos_candidatos = self._gerar_angulos_candidatos_receding(ponto_alvo, (x_atual, y_atual, theta_atual), (x, y, theta), contexto, deadline, 15.0) tipos, angs, custos_candidatos = self._gerar_angulos_candidatos_receding(ponto_alvo, (x_atual, y_atual, theta_atual), (x, y, theta), contexto, deadline, 15.0)
if ms_left() <= 0 or exp_budget <= 0: if ms_left() <= 0 or exp_budget <= 0:
break break
@ -608,11 +782,13 @@ class ControladorMPC:
simulacao_latlon = [] simulacao_latlon = []
#print(f"Simulacao: {len(simulacao_latlon)}") #print(f"Simulacao: {len(simulacao_latlon)}")
_cmd = { _cmd = {
"enviar_comando": True,
"parada_necessaria": parada_necessaria,
"erro": False,
"latencia": pos_latencia,
"angulo": float(round(np.degrees(angulo_final), 2)), "angulo": float(round(np.degrees(angulo_final), 2)),
"tipo": tipo_final.value, "tipo": tipo_final.value,
"simulacao": simulacao_latlon, "simulacao": simulacao_latlon
"parada_necessaria": parada_necessaria,
"erro": False
} }
@ -624,7 +800,7 @@ class ControladorMPC:
# reconstroi set de candidatos só pra plot, com orçamento curtinho # reconstroi set de candidatos só pra plot, com orçamento curtinho
deadline_dbg = time.perf_counter() + 0.03 # ~30 ms pra não pesar deadline_dbg = time.perf_counter() + 0.03 # ~30 ms pra não pesar
tipos_dbg, angs_dbg, _ = self._gerar_angulos_candidatos_receding( tipos_dbg, angs_dbg, _ = self._gerar_angulos_candidatos_receding(
ponto_alvo=self.pontos_info[idx_alvo_correcao]["xy"], ponto_alvo=self.pontos_info[idx_alvo_correcao],
ponto_atual=(x, y, theta), ponto_atual=(x, y, theta),
ponto_ref=(x, y, theta), ponto_ref=(x, y, theta),
contexto=contexto, contexto=contexto,
@ -646,7 +822,310 @@ class ControladorMPC:
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao processar MPC: {e}") mostrar_log(f"❌ Erro ao processar MPC: {e}")
return self._comando_fallback_hot_stop(comando_anterior) return self._comando_fallback_hot_stop(comando_anterior, latencia=pos_latencia)
def _processar_mpc_receding(self, contexto, comando_anterior):
now = time.perf_counter
t0 = now()
# --- SLAs de execução ---
SLA_MS = getattr(self, "_mpc_sla_ms", 200.0) # 200 ms ⇒ alvo 5 Hz
HEAD_MS = getattr(self, "_mpc_headroom_ms", 5.0) # folga
deadline = t0 + (SLA_MS - HEAD_MS) / 1000.0
def ms_left():
return (deadline - now()) * 1000.0
try:
if not self.pontos_info:
return None
# -------------------- Estado atual (real) --------------------
GPS = contexto.get("GPS", {})
pos_lat = GPS.get("Latitude", 0.0)
pos_lon = GPS.get("Longitude", 0.0)
pos_theta = GPS.get("AnguloCarro", 0.0)
pos_passos_atraso = GPS.get("PassosAtraso", 0)
pos_timestamp = GPS.get("Timestamp", 0.0)
pos_latencia = now() - pos_timestamp
LAT_MAX = 2.0
if pos_latencia >= LAT_MAX:
mostrar_log(f"[GPS] Grande latencia entre coordenadas detectado ({pos_latencia:.3f}/{LAT_MAX}): parando por fallback")
return self._comando_fallback_hot_stop(comando_anterior, latencia=pos_latencia)
if (pos_passos_atraso > 3):
mostrar_log(f"[GPS] Atraso detectado de {(pos_passos_atraso / 3.0):.1f} passos, {pos_latencia:.3f} s")
x, y = self.gps_handler.converter_latlon_para_xz(pos_lat, pos_lon)
theta = np.radians(pos_theta)
velocidade = float(contexto.get("Carro", {}).get("Velocidade", 0.0))
status_carro = contexto.get("Carro", {}).get("Status", StatusCarroMapa.Parado.value)
idx_proximo_ponto_real = contexto.get("Carro", {}).get("IdxProximoPonto", 0)
idx_ponto_alvo = contexto.get("Carro", {}).get("IdxPontoAlvo", 0)
Nmax = 20 if status_carro == StatusCarroMapa.CaminhandoRua.value else 60
# -------------------- Correção por latência (vida real) --------------------
# Mantém correção com 'self.dt' e 'velocidade' REAIS
u_hold = {
"tipo": comando_anterior.get("tipo"),
"angulo": np.radians(comando_anterior.get("angulo", 0.0)),
"v": velocidade,
"omega": self.gps_handler.calcular_omega(
velocidade,
np.radians(comando_anterior.get("angulo", 0.0)),
comando_anterior.get("tipo")
)
}
cmd_seq = None
x, y, theta, pts_visitados = self.corrigir_pose_por_latencia(
x, y, theta, self.visitados_execucao,
latency_s=min(pos_latencia, LAT_MAX),
dt=self.dt,
nova_posicao_fn=self._nova_posicao,
u_hold=u_hold,
cmd_seq=cmd_seq,
time_left_ms=lambda: (deadline - now()) * 1000.0,
calc_omega_fn=self.gps_handler.calcular_omega
)
idx_alvo_correcao = self._corrigir_pontos_visitados(x, y, pts_visitados, idx_proximo_ponto_real)
# -------------------- Planejamento: horizonte ESPACIAL fixo --------------------
S_ALVO = float(getattr(self, "horizonte", 2.5))
V_FLOOR = float(getattr(self, "_vmin_planejamento", 0.40))
DT_MIN = float(getattr(self, "_dt_pred_min", 0.05))
DT_MAX = float(getattr(self, "_dt_pred_max", 0.25))
K_ALVO = int(getattr(self, "_k_alvo", 16))
B_EST = int(getattr(self, "_branch_est", 18))
v_sim = max(velocidade, V_FLOOR)
# orçamento de avaliações (continua igual, para pacing)
est_ms = max(1.0, float(getattr(self, "_t_est_passo_ms", 6.0)))
exp_budget = max(4, int(ms_left() // est_ms))
# >>> NOVO: limites teóricos de K impostos por DT_MAX/DT_MIN
K_lb = max(1, int(math.ceil(S_ALVO / max(1e-6, v_sim * DT_MAX)))) # mínimo p/ cobrir S
K_ub = max(K_lb, int(math.floor(S_ALVO / max(1e-6, v_sim * DT_MIN)))) # cima (granularidade)
# >>> NOVO: NÃO deixe Nmax te travar abaixo do necessário
# se quiser ainda respeitar Nmax, aumente-o dinamicamente:
Nmax = max(Nmax, K_lb)
# teto “de intenção” (não crítico, só para não explodir absurdamente)
K_cap = min(Nmax, max(K_ALVO, K_lb))
# >>> escolha K dentro do [K_lb, K_cap]; pacing do orçamento vem depois
K = max(K_lb, min(K_cap, K_ALVO))
# dt para que K passos cubram S (agora garantido dentro de [DT_MIN, DT_MAX])
dt_pred = (S_ALVO / (v_sim * K))
dt_pred = max(DT_MIN, min(dt_pred, DT_MAX))
self.tempo_execucao_local = contexto.get("Carro", {}).get("TempoEntreComandos", 0.5)
self.passos_horizonte_local = max(1, math.floor(1 / self.tempo_execucao_local))
self.qtd_comandos_sucessivos = K
# -------------------- Seleção do comando --------------------
angulo_anterior = np.radians(comando_anterior.get("angulo", 0.0))
tipo_anterior = TipoMovimentoDirecional(comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value))
simulacao_latlon = []
ultimo_ponto = idx_proximo_ponto_real >= len(self.pontos_info)
if ultimo_ponto:
angulo_final, tipo_final = comando_parado()
parada_necessaria = True
else:
candidatos_ativos = [{
"x": x, "y": y, "theta": theta,
"custo": 0.0,
"comandos": [(tipo_anterior, angulo_anterior)],
"trajetoria": [],
"visitados": pts_visitados,
"inicial": True
}]
# Loop de K comandos (receding); cada comando simula dt_pred com v_sim
for passo in range(self.qtd_comandos_sucessivos):
if ms_left() <= 0:
break
novos_candidatos = []
# nota: 'exp_budget' é decrecido dentro da simulação
for candidato in candidatos_ativos:
if exp_budget <= 0 or ms_left() <= 0:
break
x_atual, y_atual, theta_atual = candidato["x"], candidato["y"], candidato["theta"]
visitados = deepcopy(candidato["visitados"])
cmd_anterior_local = {
"tipo": candidato["comandos"][-1][0],
"angulo": candidato["comandos"][-1][1],
}
idx_alvo = self._corrigir_pontos_visitados(x_atual, y_atual, visitados)
if idx_alvo < len(self.pontos_info) - 1: idx_alvo += 1
ponto_alvo = self.pontos_info[idx_alvo]
tipos, angs, custos_candidatos = self._gerar_angulos_candidatos_receding(
ponto_alvo, (x_atual, y_atual, theta_atual), (x, y, theta),
contexto, deadline, 15.0
)
if ms_left() <= 0 or exp_budget <= 0:
break
# ---------- seleção leve (ordena por heurística) ----------
ang_array = np.asarray(angs, dtype=np.float32)
graus_r = np.round(np.degrees(ang_array), 2)
from itertools import chain
pairs_tipo, pairs_ang, pairs_heur = [], [], []
for tipo in tipos:
tv = tipo.value
h_tmp = np.array(
[float(custos_candidatos.get((tv, float(g)), 0.0)) for g in graus_r],
dtype=np.float32
)
pairs_tipo.append([tipo] * ang_array.size)
pairs_ang.append(ang_array)
pairs_heur.append(h_tmp)
if not pairs_tipo:
continue
tipos_flat = list(chain.from_iterable(pairs_tipo))
angs_flat = np.concatenate(pairs_ang, axis=0)
heur_flat = np.concatenate(pairs_heur, axis=0)
if heur_flat.size == 0:
continue
ord_idx = np.argsort(heur_flat)
tempo_util_ms = max(0.0, ms_left() - 10.0)
est_local = max(1.0, min(float(getattr(self, "_t_est_passo_ms", 6.0)), 20.0))
Kmax = 12
Kdyn = int(min(Kmax, max(3, tempo_util_ms // est_local)))
Kdyn = min(Kdyn, ord_idx.size)
melhor_custo_local = float("inf")
for k in range(Kdyn):
if ms_left() <= 0 or exp_budget <= 0:
break
i = int(ord_idx[k])
tipo_k = tipos_flat[i]
ang_k = float(angs_flat[i])
heur_k = float(heur_flat[i])
t_c_ini = now()
try:
# >>> CHANGED: passa dt_pred e v_sim para a simulação de um PASSO
custo, sim, valido, visitados_sim = self._simular_passo(
x_atual, y_atual, theta_atual,
tipo_k, ang_k,
custos_candidatos,
cmd_anterior_local, contexto,
visitados,
dt_pred=dt_pred, # <-- NOVO
v_planejado=v_sim # <-- NOVO
)
if valido:
x_f, y_f, theta_f = sim[-1]
novo = {
"x": x_f, "y": y_f, "theta": theta_f,
"custo": candidato["custo"] + float(custo),
"comandos": candidato["comandos"] + [(tipo_k, ang_k)],
"trajetoria": candidato["trajetoria"] + sim,
"visitados": visitados_sim,
"inicial": False
}
novos_candidatos.append(novo)
if novo["custo"] < melhor_custo_local:
melhor_custo_local = novo["custo"]
except Exception as e:
mostrar_log(f"Erro ao simular passo {tipo_k.name} | {np.degrees(ang_k):.2f}: {e}")
finally:
# EMA do custo de tempo por passo (auto-tuning de est_ms)
dt_ms = (now() - t_c_ini) * 1000.0
alpha = float(getattr(self, "_ema_alpha", 0.2))
self._t_est_passo_ms = (1.0 - alpha)*float(getattr(self, "_t_est_passo_ms", 6.0)) + alpha*dt_ms
exp_budget -= 1
if exp_budget <= 0 or ms_left() <= 0:
break
# Filtro de diversidade angular e top-N
candidatos_ativos = self._filtrar_por_margem_angular(novos_candidatos, margem_graus=2.0)
N_top = 5 if exp_budget > 6 else 3
candidatos_ativos = self._selecionar_melhores(candidatos_ativos, N=N_top)
candidatos_validos = [c for c in candidatos_ativos if not c.get("inicial", False)]
if len(candidatos_validos) > 0:
try:
melhor = min(candidatos_validos, key=lambda c: c["custo"])
except Exception as e:
mostrar_log(f"Erro ao selecionar melhor candidato: {e}")
melhor = None
else:
melhor = None
parada_necessaria = (melhor is None)
if not parada_necessaria:
_comandos = melhor.get("comandos", [])
if len(_comandos) == 0:
angulo_final = 0.0
tipo_final = TipoMovimentoDirecional.RodasDianteiras
else:
idx_cmd = 0 if len(_comandos) == 1 else 1
tipo_final, angulo_final = _comandos[idx_cmd]
simulacao_latlon = list(self.gps_handler.converter_trajetoria_para_latlon(melhor["trajetoria"]) or [])
simulacao_latlon.insert(0, (pos_lat, pos_lon, pos_theta))
else:
angulo_final = 0.0
tipo_final = TipoMovimentoDirecional.RodasDianteiras
simulacao_latlon = []
_cmd = {
"enviar_comando": True,
"parada_necessaria": parada_necessaria,
"erro": False,
"latencia": pos_latencia,
"angulo": float(round(np.degrees(angulo_final), 2)),
"tipo": tipo_final.value,
"simulacao": simulacao_latlon
}
# -------------------- Debug opcional da matriz de custo --------------------
self._tick_id = getattr(self, "_tick_id", 0) + 1
if (getattr(self, "debug_cost_vis", False) and contexto.get("RegrasAtivas", {}).get("matriz_custo", False) and (self._tick_id % 10 == 0)):
dados_matriz_custo = contexto.get("VisualWorker", {}).get("MatrizCusto", {})
deadline_dbg = time.perf_counter() + 0.03
tipos_dbg, angs_dbg, _ = self._gerar_angulos_candidatos_receding(
ponto_alvo=self.pontos_info[idx_alvo_correcao],
ponto_atual=(x, y, theta),
ponto_ref=(x, y, theta),
contexto=contexto,
deadline=deadline_dbg,
margem_ms=8.0,
)
angs_dbg_deg = [round(float(np.degrees(a)), 2) for a in angs_dbg]
self.debug_plot_matriz_custo(
dados_matriz_custo,
tipos_dbg,
angs_dbg_deg,
max_linhas=3,
titulo=f"debug_cost_{int(self._tick_id):06d}.png",
highlight=(tipo_final.value, round(float(np.degrees(angulo_final)), 2)),
)
return _cmd
except Exception as e:
mostrar_log(f"❌ Erro ao processar MPC: {e}")
return self._comando_fallback_hot_stop(comando_anterior, latencia=pos_latencia)
def _verifica_tempo_maximo_execucao(self, t_init, processo): def _verifica_tempo_maximo_execucao(self, t_init, processo):
limite = 1.0 / 5.0 limite = 1.0 / 5.0
@ -656,14 +1135,17 @@ class ControladorMPC:
mostrar_log(f"🚨 Tempo maximo de execucao excedido: {delta}/{limite}, processo: {processo}") mostrar_log(f"🚨 Tempo maximo de execucao excedido: {delta}/{limite}, processo: {processo}")
return hot_stop return hot_stop
def _comando_fallback_hot_stop(self, comando_anterior): def _comando_fallback_hot_stop(self, comando_anterior, latencia=0):
return { _cmd = {
"enviar_comando": True,
"parada_necessaria": True,
"erro": True,
"latencia": latencia,
"angulo": comando_anterior.get("angulo", 0), "angulo": comando_anterior.get("angulo", 0),
"tipo": comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value), "tipo": comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value),
"simulacao": [], "simulacao": []
"parada_necessaria": True,
"erro": True
} }
return _cmd
def _gerar_angulos_candidatos_receding(self, ponto_alvo, ponto_atual, ponto_ref, contexto, deadline, margem_ms): def _gerar_angulos_candidatos_receding(self, ponto_alvo, ponto_atual, ponto_ref, contexto, deadline, margem_ms):
""" """
@ -680,11 +1162,26 @@ class ControladorMPC:
now = time.perf_counter now = time.perf_counter
# 1) Ângulo ideal (wrap [-pi,pi] e clamp ao máx) # 1) Ângulo ideal (wrap [-pi,pi] e clamp ao máx)
orient = self.gps_handler.calcular_orientacao(ponto_alvo, ponto_atual) orient = self.gps_handler.calcular_orientacao(ponto_alvo["xy"], ponto_atual)
delta_theta = (orient - ponto_atual[2] + np.pi) % (2*np.pi) - np.pi delta_theta = (orient - ponto_atual[2] + np.pi) % (2*np.pi) - np.pi
#delta_theta, theta_ref, dbg = self.delta_por_dist_esq_dir(
# estado_atual=ponto_atual,
# ponto_alvo_xy=ponto_alvo["xy"],
# orient_corredor=np.radians(contexto["Carro"]["AnguloCaminho"]), # em rad
# dist_esq=contexto["Carro"]["DistanciaEsquerda"],
# dist_dir=contexto["Carro"]["DistanciaDireita"],
# angulo_max_graus=self.angulo_max_graus,
# sigma_factor=0.6,
# w_min=0.15,
# usar_nudge_centro=True,
# k_e=1.0, v=contexto["Carro"].get("Velocidade", 0.0), v0=0.3
#)
ang_max_rad = np.radians(self.angulo_max_graus) ang_max_rad = np.radians(self.angulo_max_graus)
angulo_ideal_rad = float(np.clip(delta_theta, -ang_max_rad, ang_max_rad)) angulo_ideal_rad = float(np.clip(delta_theta, -ang_max_rad, ang_max_rad))
angulo_ideal_deg = float(np.degrees(angulo_ideal_rad)) angulo_ideal_deg = float(np.degrees(angulo_ideal_rad))
#print(f"Orient: {np.degrees(orient)}, delta theta: {np.degrees(delta_theta)}, angulo ideal: {angulo_ideal_deg}")
# 2) Tipos válidos conforme contexto # 2) Tipos válidos conforme contexto
CARRO = contexto.get("Carro", {}) CARRO = contexto.get("Carro", {})
@ -1410,82 +1907,114 @@ class ControladorMPC:
mostrar_log(f"❌ Erro ao filtrar por margem angular: {e}") mostrar_log(f"❌ Erro ao filtrar por margem angular: {e}")
return candidatos return candidatos
def _simular_passo(self, x, y, theta, tipo, angulo_testado, custos_candidatos, comando_anterior, contexto, visitados): def _simular_passo(
# Custo pode variar de 0 ~ 13, em situacoes mais extremas, por passo self, x, y, theta,
tipo, angulo_testado,
custos_candidatos, comando_anterior,
contexto, visitados,
dt_pred, v_planejado
):
"""
Simula UM 'passo' do receding (um comando) cobrindo dt_pred de tempo,
subdividido em passos menores (sub_dt) para estabilidade geométrica.
O custo é acumulado 'por metro' (multiplicando por v_planejado * sub_dt).
"""
try: try:
custo_total = 0.0 custo_total = 0.0
x_sim, y_sim, theta_sim = x, y, theta x_sim, y_sim, theta_sim = x, y, theta
self.x_ant, self.y_ant = 0.0, 0.0 self.x_ant, self.y_ant = 0.0, 0.0
# chave para custo heurístico vindo do VisualWorker (por ângulo/tipo)
chave = (tipo.value, round(float(np.degrees(angulo_testado)), 2)) chave = (tipo.value, round(float(np.degrees(angulo_testado)), 2))
custo_visual_worker = float(custos_candidatos.get(chave, 0.0))
simulacoes = [] simulacoes = []
prev_ang = comando_anterior.get("angulo", 0.0) prev_ang = float(comando_anterior.get("angulo", 0.0))
prev_tipo = comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value) prev_tipo = comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value)
pontos_visitados = visitados.copy() pontos_visitados = deepcopy(visitados)
velocidade = contexto.get("Carro", {}).get("Velocidade", 0.0)
# Contexto usado no custo
angulo_caminho = np.radians(contexto.get("Carro", {}).get("AnguloCaminho", 0.0)) angulo_caminho = np.radians(contexto.get("Carro", {}).get("AnguloCaminho", 0.0))
status_carro = StatusCarroMapa(contexto.get("Carro", {}).get("Status", StatusCarroMapa.Parado.value)) status_carro = StatusCarroMapa(contexto.get("Carro", {}).get("Status", StatusCarroMapa.Parado.value))
dentro_corredor = contexto.get("Carro", {}).get("DentroCorredor", False) dentro_corredor = bool(contexto.get("Carro", {}).get("DentroCorredor", False))
#omega_bkp = self.gps_handler.calcular_omega_bkp(velocidade, angulo_testado, tipo)
omega = self.gps_handler.calcular_omega(velocidade, angulo_testado, tipo)
#print(f"Omega novo: {omega}, Omega antigo: {omega_bkp}, Velocidade: {velocidade}, Angulo: {angulo_testado}, Tipo: {tipo.name}")
custo_visual_worker = custos_candidatos.get(chave, 0.0)
for passo in range(self.passos_horizonte_local): # omega e sub-stepping
try: omega_const = self.gps_handler.calcular_omega(v_planejado, angulo_testado, tipo)
#old_theta = theta_sim
x_sim, y_sim, theta_sim = self._nova_posicao(x_sim, y_sim, theta_sim, omega, velocidade, tipo, angulo_testado) # Se alguém setar self.passos_horizonte_local > 1, a gente cobre tudo em dt_total:
passos_local = max(1, int(getattr(self, "passos_horizonte_local", 1)))
dt_total = float(dt_pred) * passos_local
SUB_MAX = float(getattr(self, "_dt_sub_max", 0.08)) # ~80 ms por subpasso
n_sub = max(1, int(math.ceil(dt_total / SUB_MAX)))
sub_dt = dt_total / n_sub
dist_sub = v_planejado * sub_dt # usado para custo por metro
for _ in range(n_sub):
# IMPORTANTE: _nova_posicao precisa aceitar dt opcional (ver nota abaixo)
x_sim, y_sim, theta_sim = self._nova_posicao(
x_sim, y_sim, theta_sim,
omega_const, v_planejado, # ordem original: (omega, v, tipo, angulo)
tipo, angulo_testado,
dt=sub_dt # <<--- dt variável
)
simulacoes.append((x_sim, y_sim, theta_sim)) simulacoes.append((x_sim, y_sim, theta_sim))
# alvo mais próximo após avançar
idx_alvo_sim = self._corrigir_pontos_visitados(x_sim, y_sim, pontos_visitados) idx_alvo_sim = self._corrigir_pontos_visitados(x_sim, y_sim, pontos_visitados)
ponto_alvo_sim = self.pontos_info[idx_alvo_sim]["xy"] ponto_alvo_sim = self.pontos_info[idx_alvo_sim]["xy"]
# orientação do caminho + erro angular combinado
orient_sim = self.gps_handler.calcular_orientacao((x_sim, y_sim), ponto_alvo_sim) orient_sim = self.gps_handler.calcular_orientacao((x_sim, y_sim), ponto_alvo_sim)
orient_sim = (orient_sim + np.pi) % (2 * np.pi) orient_sim = (orient_sim + np.pi) % (2 * np.pi)
erro_pos = np.linalg.norm([x_sim - ponto_alvo_sim[0], y_sim - ponto_alvo_sim[1]]) # erros
peso_erro_orientacao_caminho = 0.3 if status_carro == StatusCarroMapa.CaminhandoRua and dentro_corredor else 0.7 erro_pos = float(np.linalg.norm([x_sim - ponto_alvo_sim[0], y_sim - ponto_alvo_sim[1]]))
peso_erro_orientacao_caminho = 0.3 if (status_carro == StatusCarroMapa.CaminhandoRua and dentro_corredor) else 0.7
erro_ori_sim = self.gps_handler.erro_angular(orient_sim, theta_sim, angulo_caminho, peso_erro_orientacao_caminho) erro_ori_sim = self.gps_handler.erro_angular(orient_sim, theta_sim, angulo_caminho, peso_erro_orientacao_caminho)
# pesos dinâmicos
pesos = self._calcular_pesos_movimento(contexto, erro_ori_sim) pesos = self._calcular_pesos_movimento(contexto, erro_ori_sim)
peso_erro_pos, peso_erro_ori, peso_suavidade, peso_fator_re, peso_movimento = pesos[0], pesos[1], pesos[2], pesos[3], pesos[5] peso_erro_pos, peso_erro_ori, peso_suavidade, peso_fator_re, peso_movimento = pesos[0], pesos[1], pesos[2], pesos[3], pesos[5]
# ângulo relativo ao alvo e suavidade
vetor_alvo = np.array(ponto_alvo_sim) - np.array([x_sim, y_sim]) vetor_alvo = np.array(ponto_alvo_sim) - np.array([x_sim, y_sim])
vetor_alvo_norm = vetor_alvo / np.linalg.norm(vetor_alvo) vetor_alvo_norm = vetor_alvo / (np.linalg.norm(vetor_alvo) + 1e-9)
vetor_movel = np.array([np.cos(theta_sim), np.sin(theta_sim)]) vetor_movel = np.array([np.cos(theta_sim), np.sin(theta_sim)])
cos_angulo = -np.dot(vetor_movel, vetor_alvo_norm) cos_angulo = -float(np.dot(vetor_movel, vetor_alvo_norm))
delta_angulo = abs(angulo_testado - prev_ang) delta_angulo = abs(angulo_testado - prev_ang)
# componentes de custo
custo_pos = erro_pos * peso_erro_pos custo_pos = erro_pos * peso_erro_pos
custo_ori = ((erro_ori_sim / np.pi) * erro_pos) * peso_erro_ori custo_ori = ((erro_ori_sim / np.pi) * erro_pos) * peso_erro_ori
custo_suavidade = ((delta_angulo / np.pi) * erro_pos) * peso_suavidade custo_suavidade = ((delta_angulo / np.pi) * erro_pos) * peso_suavidade
custo_tipo_movimento = peso_movimento[tipo] custo_tipo_movimento = float(peso_movimento[tipo])
erro_re = (1.0 - cos_angulo) if cos_angulo > 0 else cos_angulo erro_re = (1.0 - cos_angulo) if cos_angulo > 0 else cos_angulo
custo_re = -erro_re * erro_pos * peso_fator_re custo_re = -erro_re * erro_pos * peso_fator_re
custo_mapa = (custo_pos + custo_ori + custo_suavidade + custo_tipo_movimento + custo_re + custo_visual_worker)
custo_total += custo_mapa # soma local (heurística do visual + demais), e NORMALIZA por metro
custo_mapa = (custo_pos + custo_ori + custo_suavidade + custo_tipo_movimento + custo_re + custo_visual_worker)
custo_total += custo_mapa * dist_sub # <<< custo POR METRO
prev_ang = angulo_testado prev_ang = angulo_testado
prev_tipo = tipo prev_tipo = tipo
#if matriz_concluida: return float(custo_total), simulacoes, True, pontos_visitados
# break
#print(f"Passo: {passo}, Alvo: {idx_alvo_sim}, Distancia: {((passo + 1) * distancia_m):.2f} m, Theta: {theta_sim:.2f}°, DX: {(x - x_sim):.2f} m, DY: {(y - y_sim):.2f} m, y_sim: {y_sim:.4f}, x_sim: {x_sim:.4f}, Tipo: {tipo.name}, Angulo: {np.degrees(angulo_testado):.2f}, custo: {custo_total}")
#mostrar_log(f"Omega: {np.degrees(omega):.2f}°, Ori: {np.degrees(orient_sim):.2f}°, E Ori: {np.degrees(erro_ori_sim):.3f}°, E Pos: {erro_pos:.3f}, F Re: {erro_re:.3f}, d°: {np.degrees(delta_angulo):.2f}, CM: {custo_mapa:.4f}, CP: {custo_pos:.4}, CO: {custo_ori:.4f}, CT: {custo_tipo_movimento:.4f}, CS: {custo_suavidade:.4f}, CR: {custo_re:.4f}")
except Exception as e:
mostrar_log(f"❌ Erro ao simular passo {passo}, {tipo.name}, {np.degrees(angulo_testado):.2f}: {e}")
return custo_total, simulacoes, True, pontos_visitados
except Exception as e: except Exception as e:
mostrar_log(f"Erro ao simular passo para {tipo.name} | angulo: {np.degrees(angulo_testado):.2f}: {e}") mostrar_log(f"Erro ao simular passo para {tipo.name} | angulo: {np.degrees(angulo_testado):.2f}: {e}")
return float('inf'), [(0, 0, 0)], False, pontos_visitados return float('inf'), [(0.0, 0.0, 0.0)], False, visitados
def _nova_posicao(self, x, y, theta, omega, velocidade, tipo, angulo_rad): def _nova_posicao(self, x, y, theta, omega, velocidade, tipo, angulo_rad, dt=None):
distancia_m = velocidade * self.dt """
Atualiza a pose integrando por 'dt' (se None, cai em self.dt).
Mantém a ordem dos parâmetros antigos para não quebrar chamadas existentes.
"""
dt_use = self.dt if (dt is None) else float(dt)
distancia_m = velocidade * dt_use
theta_sim = theta + (omega * self.dt) theta_sim = theta + (omega * dt_use)
x_sim = x + (np.sin(theta_sim) * distancia_m) x_sim = x + (np.sin(theta_sim) * distancia_m)
y_sim = y + (np.cos(theta_sim) * distancia_m) y_sim = y + (np.cos(theta_sim) * distancia_m)

View File

@ -1,3 +1,4 @@
import time
from shared.contexto_global_redis import ContextoGlobalRedis from shared.contexto_global_redis import ContextoGlobalRedis
from manager_worker.modulos.pid import PIDAdaptativo from manager_worker.modulos.pid import PIDAdaptativo
from .base import ProcessadorBase from .base import ProcessadorBase
@ -20,17 +21,24 @@ class ProcessadorEmAndamento(ProcessadorBase):
saida_min=-ang_max, saida_min=-ang_max,
saida_max=ang_max saida_max=ang_max
) )
self.ultimo_comando_enviado = time.time()
def processar(self): def processar(self):
try: try:
angulo_sp, tipo_dir, simulacao, parada_necessaria, erro = definir_comando_dir(self.pid_dir) agora = time.time()
velocidade_sp = definir_comando_mov(parada_necessaria, erro, self.filtro_mov) envio_necessario = (agora - self.ultimo_comando_enviado) > 0.3
comando_dir = definir_comando_dir(self.pid_dir, envio_necessario)
if comando_dir.get("enviar_comando", False):
self.ultimo_comando_enviado = agora
velocidade_sp = definir_comando_mov(comando_dir.get("parada_necessaria", False), comando_dir.get("erro", False), self.filtro_mov)
return comando_controle( return comando_controle(
percentual_velocidade=velocidade_sp, percentual_velocidade=velocidade_sp,
angulo=angulo_sp, angulo=comando_dir.get("angulo"),
tipo_movimento=tipo_dir, tipo_movimento=comando_dir.get("tipo"),
simulacao=simulacao, simulacao=comando_dir.get("simulacao"),
erro=erro erro=comando_dir.get("erro", False),
latencia=comando_dir.get("latencia", 0.0)
) )
except Exception as e: except Exception as e:
print(f"Erro no processador EmAndamento: {e}") print(f"Erro no processador EmAndamento: {e}")

View File

@ -3,7 +3,7 @@ from shared.enums import ModoOperacao, StatusOperacao
from manager_worker.config import mostrar_log from manager_worker.config import mostrar_log
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
def comando_controle(percentual_velocidade: float, angulo: float, tipo_movimento: int, simulacao = [], erro: bool = False): def comando_controle(percentual_velocidade: float, angulo: float, tipo_movimento: int, simulacao = [], erro: bool = False, latencia: float = 0.0):
hb_atual = ContextoGlobalRedis.get_controle().get("heartbeat", 0) hb_atual = ContextoGlobalRedis.get_controle().get("heartbeat", 0)
if erro == False: if erro == False:
try: try:
@ -18,14 +18,16 @@ def comando_controle(percentual_velocidade: float, angulo: float, tipo_movimento
tipo_movimento_direcional=tipo_movimento, tipo_movimento_direcional=tipo_movimento,
velocidade_sp=percentual_velocidade, velocidade_sp=percentual_velocidade,
simulacao=simulacao, simulacao=simulacao,
heartbeat=hb_atual heartbeat=hb_atual,
latencia=latencia
) )
return { return {
"velocidade_sp": percentual_velocidade, "velocidade_sp": percentual_velocidade,
"angulo_sp": angulo, "angulo_sp": angulo,
"tipo_movimento_direcional": tipo_movimento, "tipo_movimento_direcional": tipo_movimento,
"simulacao": simulacao, "simulacao": simulacao,
"heartbeat": hb_atual "heartbeat": hb_atual,
"latencia": latencia
} }
def comando_parado(): def comando_parado():

Some files were not shown because too many files have changed in this diff Show More