ajustes gps e movimentacao
This commit is contained in:
parent
36c7787d8d
commit
b8ea15d2f3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1277,7 +1277,7 @@ namespace AgroBase.Forms.IHM
|
|||
double Precisao = GpsInicializado ? (int)Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.QualidadeFix : -1;
|
||||
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);
|
||||
|
||||
double OrientacaoGPS = GpsInicializado ? Variaveis.OperacaoEmAndamento.Sensoriamento.Gps.OrientacaoReal : -1;
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@
|
|||
this.lblDistanciaPercorrida = new System.Windows.Forms.Label();
|
||||
this.txtTempoOperacao = new System.Windows.Forms.TextBox();
|
||||
this.lblTempoOperacao = new System.Windows.Forms.Label();
|
||||
this.txtErvasIdentificadas = new System.Windows.Forms.TextBox();
|
||||
this.lblErvasIdentificadas = new System.Windows.Forms.Label();
|
||||
this.txtLatenciaControle = new System.Windows.Forms.TextBox();
|
||||
this.lblLatenciaControle = new System.Windows.Forms.Label();
|
||||
this.txtHerbicidaAplicado = new System.Windows.Forms.TextBox();
|
||||
this.lblHerbicidaAplicado = new System.Windows.Forms.Label();
|
||||
this.txtBateriaConsumida = new System.Windows.Forms.TextBox();
|
||||
|
|
@ -271,24 +271,24 @@
|
|||
this.lblTempoOperacao.TabIndex = 13;
|
||||
this.lblTempoOperacao.Text = "Tempo de Operação";
|
||||
//
|
||||
// txtErvasIdentificadas
|
||||
// txtLatenciaControle
|
||||
//
|
||||
this.txtErvasIdentificadas.Font = new System.Drawing.Font("Microsoft Sans Serif", 12.25F);
|
||||
this.txtErvasIdentificadas.Location = new System.Drawing.Point(164, 261);
|
||||
this.txtErvasIdentificadas.Name = "txtErvasIdentificadas";
|
||||
this.txtErvasIdentificadas.ReadOnly = true;
|
||||
this.txtErvasIdentificadas.Size = new System.Drawing.Size(120, 26);
|
||||
this.txtErvasIdentificadas.TabIndex = 16;
|
||||
this.txtErvasIdentificadas.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.txtLatenciaControle.Font = new System.Drawing.Font("Microsoft Sans Serif", 12.25F);
|
||||
this.txtLatenciaControle.Location = new System.Drawing.Point(164, 261);
|
||||
this.txtLatenciaControle.Name = "txtLatenciaControle";
|
||||
this.txtLatenciaControle.ReadOnly = true;
|
||||
this.txtLatenciaControle.Size = new System.Drawing.Size(120, 26);
|
||||
this.txtLatenciaControle.TabIndex = 16;
|
||||
this.txtLatenciaControle.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// lblErvasIdentificadas
|
||||
// lblLatenciaControle
|
||||
//
|
||||
this.lblErvasIdentificadas.AutoSize = true;
|
||||
this.lblErvasIdentificadas.Location = new System.Drawing.Point(3, 264);
|
||||
this.lblErvasIdentificadas.Name = "lblErvasIdentificadas";
|
||||
this.lblErvasIdentificadas.Size = new System.Drawing.Size(144, 20);
|
||||
this.lblErvasIdentificadas.TabIndex = 15;
|
||||
this.lblErvasIdentificadas.Text = "Ervas Identificadas";
|
||||
this.lblLatenciaControle.AutoSize = true;
|
||||
this.lblLatenciaControle.Location = new System.Drawing.Point(3, 264);
|
||||
this.lblLatenciaControle.Name = "lblLatenciaControle";
|
||||
this.lblLatenciaControle.Size = new System.Drawing.Size(134, 20);
|
||||
this.lblLatenciaControle.TabIndex = 15;
|
||||
this.lblLatenciaControle.Text = "Latencia Controle";
|
||||
//
|
||||
// txtHerbicidaAplicado
|
||||
//
|
||||
|
|
@ -462,9 +462,9 @@
|
|||
this.pnlSensores.Controls.Add(this.txtBateriaConsumida);
|
||||
this.pnlSensores.Controls.Add(this.txtTempoOperacao);
|
||||
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.txtErvasIdentificadas);
|
||||
this.pnlSensores.Controls.Add(this.txtLatenciaControle);
|
||||
this.pnlSensores.Controls.Add(this.lblHerbicidaAplicado);
|
||||
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)));
|
||||
|
|
@ -957,8 +957,8 @@
|
|||
private System.Windows.Forms.Label lblDistanciaPercorrida;
|
||||
private System.Windows.Forms.TextBox txtTempoOperacao;
|
||||
private System.Windows.Forms.Label lblTempoOperacao;
|
||||
private System.Windows.Forms.TextBox txtErvasIdentificadas;
|
||||
private System.Windows.Forms.Label lblErvasIdentificadas;
|
||||
private System.Windows.Forms.TextBox txtLatenciaControle;
|
||||
private System.Windows.Forms.Label lblLatenciaControle;
|
||||
private System.Windows.Forms.TextBox txtHerbicidaAplicado;
|
||||
private System.Windows.Forms.Label lblHerbicidaAplicado;
|
||||
private System.Windows.Forms.TextBox txtBateriaConsumida;
|
||||
|
|
|
|||
|
|
@ -162,13 +162,13 @@ namespace AgroBase.Forms.IHM
|
|||
txtOperacaoLiberada.Text = opLiberada ? "Sim" : "Não";
|
||||
txtOperacaoLiberada.ForeColor = opLiberada ? Color.Black : Color.Red;
|
||||
CodigoFalha = _Sensoriamento.CodigoFalha;
|
||||
btnCodFalha.Text = CodigoFalha.ToString();
|
||||
btnCodFalha.Text = CodigoFalha.ToString("0.000");
|
||||
txtStatusAtual.Text = _Sensoriamento.StatusOperacao.ToString();
|
||||
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");
|
||||
txtDistanciaPercorrida.Text = _Sensoriamento.Trajetoria.DistanciaPercorrida.ToString("0.00") + " m";
|
||||
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";
|
||||
txtBateriaConsumida.Text = _Sensoriamento.Bateria.BateriaConsumida.ToString("0.00") + " %";
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
this.lblTipoMovimento = new System.Windows.Forms.Label();
|
||||
this.chbPulverizador = new System.Windows.Forms.CheckBox();
|
||||
this.flwCamerasSolo = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.chbSonarAtivado = new System.Windows.Forms.CheckBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.chartGraficos)).BeginInit();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabMapa.SuspendLayout();
|
||||
|
|
@ -637,12 +638,25 @@
|
|||
this.flwCamerasSolo.Size = new System.Drawing.Size(597, 172);
|
||||
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
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.ClientSize = new System.Drawing.Size(1243, 649);
|
||||
this.Controls.Add(this.chbSonarAtivado);
|
||||
this.Controls.Add(this.flwCamerasSolo);
|
||||
this.Controls.Add(this.chbPulverizador);
|
||||
this.Controls.Add(this.lblTipoMovimento);
|
||||
|
|
@ -742,5 +756,6 @@
|
|||
private System.Windows.Forms.Label lblMov_DF;
|
||||
private System.Windows.Forms.FlowLayoutPanel flwCamerasSolo;
|
||||
private System.Windows.Forms.Label lblGPSFix;
|
||||
private System.Windows.Forms.CheckBox chbSonarAtivado;
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
}
|
||||
|
||||
chbPulverizador.Checked = Variaveis.OperacaoEmAndamento.Controle.PulverizadorAutomatico;
|
||||
chbSonarAtivado.Checked = Variaveis.OperacaoEmAndamento.Controle.SonarAtivado;
|
||||
|
||||
InicializarComandosDirecional();
|
||||
PopularInformacoesGerais();
|
||||
|
|
@ -154,7 +155,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
lblDistanciaEsquerda.Text = "Distancia Esq: " + _Sensoriamento.Trajetoria.DistanciaEsquerda.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();
|
||||
lblGPSprecisao.Text = "Precisão: " + _Sensoriamento.Gps.PrecisaoCm.ToString("0.00") + " cm";
|
||||
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";
|
||||
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;
|
||||
if (!atuadorAtivado)
|
||||
{
|
||||
chbPulverizador.Checked = false;
|
||||
}
|
||||
|
||||
bool sonarAtivado = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual.Status == StatusModulo.Operante;
|
||||
chbSonarAtivado.Enabled = sonarAtivado;
|
||||
if (!sonarAtivado)
|
||||
{
|
||||
chbSonarAtivado.Checked = false;
|
||||
}
|
||||
|
||||
if (!ReqFrameCam)
|
||||
_ = AtualizarCameraSolo();
|
||||
|
||||
|
|
@ -674,7 +682,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
private void chbSonarAtivado_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
Variaveis.OperacaoEmAndamento.Controle.SonarAtivado = ((CheckBox)sender).Checked;
|
||||
}
|
||||
|
||||
private void chbSonarDesvioAutomatico_CheckedChanged(object sender, EventArgs e)
|
||||
|
|
|
|||
|
|
@ -1085,12 +1085,12 @@ namespace AgroBase.Forms.Operacoes
|
|||
txtStatusCarro.Text = Log.Trajetoria.StatusCarro.ToString();
|
||||
txtHerbicidaPorErva.Text = herbicidaPorErva.ToString("0.00") + " ml";
|
||||
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();
|
||||
txtErvasTerreno.Text = Log.Atuador.PercentualErvasTerreno.ToString("0.00");
|
||||
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)";
|
||||
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";
|
||||
txtErvasIdentificadas.Text = $"{totalAtuacoes}";
|
||||
|
||||
|
|
@ -1170,7 +1170,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
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_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)";
|
||||
|
||||
var LogLra = LogsOperacao.FirstOrDefault(x => x.Momento == MomentoAtual).DadosLoRa;
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmSimulacaoMapaGPS));
|
||||
this.pnlOpcoes = new System.Windows.Forms.Panel();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.txtErroTempoComando = new System.Windows.Forms.TextBox();
|
||||
this.lblDelayPosicao = new System.Windows.Forms.Label();
|
||||
this.txtDelayPosicao = new System.Windows.Forms.TextBox();
|
||||
this.lblCarregado = new System.Windows.Forms.Label();
|
||||
this.btnCarregarTrajetoria = new System.Windows.Forms.Button();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
|
|
@ -46,13 +46,15 @@
|
|||
this.lblKp = new System.Windows.Forms.Label();
|
||||
this.txtKp = new System.Windows.Forms.TextBox();
|
||||
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.txtQtdErro = new System.Windows.Forms.TextBox();
|
||||
this.txtDelayComando = new System.Windows.Forms.TextBox();
|
||||
this.btnAtualizarLeitura = new System.Windows.Forms.Button();
|
||||
this.btnCarregarMapa = new System.Windows.Forms.Button();
|
||||
this.btnIniciarSimulacao = new System.Windows.Forms.Button();
|
||||
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.chbSonarVirando = new System.Windows.Forms.CheckBox();
|
||||
this.btnAdicionarDeteccao = new System.Windows.Forms.Button();
|
||||
|
|
@ -102,8 +104,6 @@
|
|||
this.lblTempoRestante = new System.Windows.Forms.Label();
|
||||
this.lblPercentualRua = 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.gpbSonar.SuspendLayout();
|
||||
this.gpbGPS.SuspendLayout();
|
||||
|
|
@ -112,8 +112,8 @@
|
|||
//
|
||||
// pnlOpcoes
|
||||
//
|
||||
this.pnlOpcoes.Controls.Add(this.label7);
|
||||
this.pnlOpcoes.Controls.Add(this.txtErroTempoComando);
|
||||
this.pnlOpcoes.Controls.Add(this.lblDelayPosicao);
|
||||
this.pnlOpcoes.Controls.Add(this.txtDelayPosicao);
|
||||
this.pnlOpcoes.Controls.Add(this.lblCarregado);
|
||||
this.pnlOpcoes.Controls.Add(this.btnCarregarTrajetoria);
|
||||
this.pnlOpcoes.Controls.Add(this.label6);
|
||||
|
|
@ -128,9 +128,9 @@
|
|||
this.pnlOpcoes.Controls.Add(this.lblKp);
|
||||
this.pnlOpcoes.Controls.Add(this.txtKp);
|
||||
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.txtQtdErro);
|
||||
this.pnlOpcoes.Controls.Add(this.txtDelayComando);
|
||||
this.pnlOpcoes.Controls.Add(this.btnAtualizarLeitura);
|
||||
this.pnlOpcoes.Controls.Add(this.btnCarregarMapa);
|
||||
this.pnlOpcoes.Controls.Add(this.btnIniciarSimulacao);
|
||||
|
|
@ -143,25 +143,25 @@
|
|||
this.pnlOpcoes.Size = new System.Drawing.Size(1379, 100);
|
||||
this.pnlOpcoes.TabIndex = 0;
|
||||
//
|
||||
// label7
|
||||
// lblDelayPosicao
|
||||
//
|
||||
this.label7.AutoSize = true;
|
||||
this.label7.Location = new System.Drawing.Point(1277, 20);
|
||||
this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label7.Name = "label7";
|
||||
this.label7.Size = new System.Drawing.Size(76, 13);
|
||||
this.label7.TabIndex = 45;
|
||||
this.label7.Text = "Delay Controle";
|
||||
this.lblDelayPosicao.AutoSize = true;
|
||||
this.lblDelayPosicao.Location = new System.Drawing.Point(1277, 20);
|
||||
this.lblDelayPosicao.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lblDelayPosicao.Name = "lblDelayPosicao";
|
||||
this.lblDelayPosicao.Size = new System.Drawing.Size(75, 13);
|
||||
this.lblDelayPosicao.TabIndex = 45;
|
||||
this.lblDelayPosicao.Text = "Delay Posição";
|
||||
//
|
||||
// txtErroTempoComando
|
||||
// txtDelayPosicao
|
||||
//
|
||||
this.txtErroTempoComando.Location = new System.Drawing.Point(1279, 35);
|
||||
this.txtErroTempoComando.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txtErroTempoComando.Name = "txtErroTempoComando";
|
||||
this.txtErroTempoComando.Size = new System.Drawing.Size(88, 20);
|
||||
this.txtErroTempoComando.TabIndex = 44;
|
||||
this.txtErroTempoComando.Text = "1";
|
||||
this.txtErroTempoComando.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.txtDelayPosicao.Location = new System.Drawing.Point(1279, 35);
|
||||
this.txtDelayPosicao.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txtDelayPosicao.Name = "txtDelayPosicao";
|
||||
this.txtDelayPosicao.Size = new System.Drawing.Size(88, 20);
|
||||
this.txtDelayPosicao.TabIndex = 44;
|
||||
this.txtDelayPosicao.Text = "1";
|
||||
this.txtDelayPosicao.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// lblCarregado
|
||||
//
|
||||
|
|
@ -307,15 +307,15 @@
|
|||
this.btnReiniciar.UseVisualStyleBackColor = true;
|
||||
this.btnReiniciar.Click += new System.EventHandler(this.btnReiniciar_Click);
|
||||
//
|
||||
// lblQtdErro
|
||||
// lblDelayComando
|
||||
//
|
||||
this.lblQtdErro.AutoSize = true;
|
||||
this.lblQtdErro.Location = new System.Drawing.Point(1087, 20);
|
||||
this.lblQtdErro.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lblQtdErro.Name = "lblQtdErro";
|
||||
this.lblQtdErro.Size = new System.Drawing.Size(45, 13);
|
||||
this.lblQtdErro.TabIndex = 29;
|
||||
this.lblQtdErro.Text = "Qtd erro";
|
||||
this.lblDelayComando.AutoSize = true;
|
||||
this.lblDelayComando.Location = new System.Drawing.Point(1087, 20);
|
||||
this.lblDelayComando.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.lblDelayComando.Name = "lblDelayComando";
|
||||
this.lblDelayComando.Size = new System.Drawing.Size(58, 13);
|
||||
this.lblDelayComando.TabIndex = 29;
|
||||
this.lblDelayComando.Text = "Delay Cmd";
|
||||
//
|
||||
// chbAutomatico
|
||||
//
|
||||
|
|
@ -329,15 +329,15 @@
|
|||
this.chbAutomatico.UseVisualStyleBackColor = true;
|
||||
this.chbAutomatico.CheckedChanged += new System.EventHandler(this.chbAutomatico_CheckedChanged);
|
||||
//
|
||||
// txtQtdErro
|
||||
// txtDelayComando
|
||||
//
|
||||
this.txtQtdErro.Location = new System.Drawing.Point(1089, 35);
|
||||
this.txtQtdErro.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txtQtdErro.Name = "txtQtdErro";
|
||||
this.txtQtdErro.Size = new System.Drawing.Size(54, 20);
|
||||
this.txtQtdErro.TabIndex = 28;
|
||||
this.txtQtdErro.Text = "1";
|
||||
this.txtQtdErro.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.txtDelayComando.Location = new System.Drawing.Point(1089, 35);
|
||||
this.txtDelayComando.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.txtDelayComando.Name = "txtDelayComando";
|
||||
this.txtDelayComando.Size = new System.Drawing.Size(54, 20);
|
||||
this.txtDelayComando.TabIndex = 28;
|
||||
this.txtDelayComando.Text = "1";
|
||||
this.txtDelayComando.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// btnAtualizarLeitura
|
||||
//
|
||||
|
|
@ -394,6 +394,26 @@
|
|||
this.gpbSonar.TabStop = false;
|
||||
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
|
||||
//
|
||||
this.chbMoverObstaculo.AutoSize = true;
|
||||
|
|
@ -953,26 +973,6 @@
|
|||
this.lblPercentualOperacao.TabIndex = 42;
|
||||
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
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
|
@ -1071,8 +1071,8 @@
|
|||
private System.Windows.Forms.Label lblStatusOperacao;
|
||||
private System.Windows.Forms.Label lblTempoOperacao;
|
||||
private System.Windows.Forms.CheckBox chbAutomatico;
|
||||
private System.Windows.Forms.Label lblQtdErro;
|
||||
private System.Windows.Forms.TextBox txtQtdErro;
|
||||
private System.Windows.Forms.Label lblDelayComando;
|
||||
private System.Windows.Forms.TextBox txtDelayComando;
|
||||
private System.Windows.Forms.Button btnReiniciar;
|
||||
private System.Windows.Forms.Label lblKd;
|
||||
private System.Windows.Forms.TextBox txtKd;
|
||||
|
|
@ -1091,8 +1091,8 @@
|
|||
private System.Windows.Forms.TextBox txtTempoGPS;
|
||||
private System.Windows.Forms.Button btnCarregarTrajetoria;
|
||||
private System.Windows.Forms.Label lblCarregado;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private System.Windows.Forms.TextBox txtErroTempoComando;
|
||||
private System.Windows.Forms.Label lblDelayPosicao;
|
||||
private System.Windows.Forms.TextBox txtDelayPosicao;
|
||||
private System.Windows.Forms.Label label8;
|
||||
private System.Windows.Forms.ComboBox cmbStatusCorredor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace AgroBase.Forms
|
|||
DateTime UltimaAtualizacaoGps = DateTime.MinValue;
|
||||
|
||||
Queue<double> historicoAngulosControle = new Queue<double>();
|
||||
GPSModel posicaoAtual = new GPSModel();
|
||||
|
||||
List<MPCSimulacaoModel> simulacao = new List<MPCSimulacaoModel>();
|
||||
|
||||
|
|
@ -67,7 +68,7 @@ namespace AgroBase.Forms
|
|||
|
||||
private void frmSimulacaoMapaGPS_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
tmrLeitura.Dispose();
|
||||
tmrLeitura?.Dispose();
|
||||
Variaveis.OperacaoEmAndamento.Iniciado = false;
|
||||
}
|
||||
|
||||
|
|
@ -196,6 +197,14 @@ namespace AgroBase.Forms
|
|||
|
||||
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();
|
||||
btnAcrescentarGPS.Enabled = true;
|
||||
txtAnguloGPS.Enabled = true;
|
||||
|
|
@ -213,29 +222,24 @@ namespace AgroBase.Forms
|
|||
Longitude = GPSService.UltimaLeitura.Longitude,
|
||||
Latitude = GPSService.UltimaLeitura.Latitude,
|
||||
Heartbeat = GPSService.UltimaLeitura.Heartbeat,
|
||||
OrientacaoReal = GPSService.UltimaLeitura.OrientacaoReal
|
||||
};
|
||||
|
||||
if (!GPSService.Iniciado || (txtLongitude.Text != "" && txtLatitude.Text != ""))
|
||||
{
|
||||
GPSService.UltimaLeitura = new GPSModel()
|
||||
{
|
||||
DataHora = DateTime.Now,
|
||||
Longitude = double.Parse(txtLongitude.Text.Replace(".", ",")),
|
||||
Latitude = double.Parse(txtLatitude.Text.Replace(".", ",")),
|
||||
Heartbeat = GPSService.UltimaLeitura.Heartbeat
|
||||
};
|
||||
GPSService.UltimaLeitura = posicaoAtual;
|
||||
}
|
||||
|
||||
GPSService.AtualizarAtrasoPosicoes(Convert.ToInt32(txtDelayPosicao.Text));
|
||||
GPSService.AtualizarCoordenadasGPS();
|
||||
}
|
||||
|
||||
private async void btnAcrescentarGPS_Click(object sender, EventArgs e)
|
||||
{
|
||||
GPSModel novaPosicao = new GPSModel();
|
||||
double velocidadeMs = FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.SimulacaoRpmControle);
|
||||
velocidadeCarroMs = FuncoesMatematicas.ConverteMsParaKmh(velocidadeMs);
|
||||
GPSModel novaPosicao = null;
|
||||
velocidadeCarroMs = FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.SimulacaoRpmControle);
|
||||
|
||||
Variaveis.OperacaoEmAndamento.DispMvd.Dados.VelocidadeMedia = velocidadeMs;
|
||||
Variaveis.OperacaoEmAndamento.DispMvd.Dados.VelocidadeMedia = velocidadeCarroMs;
|
||||
|
||||
double tempoPassado =
|
||||
!chbAutomatico.Checked || UltimaAtualizacaoGps == DateTime.MinValue ? // esta no modo manual
|
||||
|
|
@ -266,10 +270,7 @@ namespace AgroBase.Forms
|
|||
|
||||
anguloControle = double.Parse(txtAnguloControle.Text);
|
||||
|
||||
double.TryParse(txtErroTempoComando.Text, out double fatorErro);
|
||||
int ticksMax = (int)(ticksControleMax * Math.Max(1, fatorErro));
|
||||
|
||||
bool atualizarAnguloControle = (ticksControle >= ticksMax);
|
||||
bool atualizarAnguloControle = (ticksControle >= ticksControleMax);
|
||||
if (atualizarAnguloControle)
|
||||
{
|
||||
ticksControle = 0;
|
||||
|
|
@ -288,20 +289,23 @@ namespace AgroBase.Forms
|
|||
|
||||
// Calcula a nova posicao do robo
|
||||
//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($"Py: {DadosPosicao(pos)}");
|
||||
|
||||
// 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");
|
||||
txtDistanciaGPS.Text = novaPosicao.Distancia.ToString("0.0000");
|
||||
txtLatitude.Text = novaPosicao.Latitude.ToString();
|
||||
txtLongitude.Text = novaPosicao.Longitude.ToString();
|
||||
txtDistanciaGPS.Text = posicaoAtual.Distancia.ToString("0.0000");
|
||||
txtLatitude.Text = posicaoAtual.Latitude.ToString();
|
||||
txtLongitude.Text = posicaoAtual.Longitude.ToString();
|
||||
|
||||
UltimaAtualizacaoGps = DateTime.Now;
|
||||
|
||||
|
|
@ -420,8 +424,10 @@ namespace AgroBase.Forms
|
|||
{
|
||||
try
|
||||
{
|
||||
double angulo = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloCaminho;
|
||||
double anguloCorredor = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloMedioCorredor;
|
||||
//double angulo = Variaveis.OperacaoEmAndamento.Trajetoria.AnguloCaminho;
|
||||
//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);
|
||||
}
|
||||
catch { }
|
||||
|
|
@ -455,19 +461,14 @@ namespace AgroBase.Forms
|
|||
|
||||
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);
|
||||
|
||||
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");
|
||||
txtAnguloControle.Text = historicoAngulosControle.Peek().ToString("0.00");
|
||||
}
|
||||
|
||||
private void chbSonarVirando_CheckedChanged(object sender, EventArgs e)
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
// lblVariacaoMagnetica
|
||||
//
|
||||
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.Name = "lblVariacaoMagnetica";
|
||||
this.lblVariacaoMagnetica.Size = new System.Drawing.Size(129, 13);
|
||||
|
|
@ -122,7 +122,7 @@
|
|||
// lblCursoVerdadeiro
|
||||
//
|
||||
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.Name = "lblCursoVerdadeiro";
|
||||
this.lblCursoVerdadeiro.Size = new System.Drawing.Size(119, 13);
|
||||
|
|
@ -197,7 +197,7 @@
|
|||
// lblFix
|
||||
//
|
||||
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.Name = "lblFix";
|
||||
this.lblFix.Size = new System.Drawing.Size(96, 13);
|
||||
|
|
@ -225,7 +225,7 @@
|
|||
// lblOrientacao
|
||||
//
|
||||
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.Name = "lblOrientacao";
|
||||
this.lblOrientacao.Size = new System.Drawing.Size(90, 13);
|
||||
|
|
@ -255,7 +255,7 @@
|
|||
// lblDatahora
|
||||
//
|
||||
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.Name = "lblDatahora";
|
||||
this.lblDatahora.Size = new System.Drawing.Size(77, 13);
|
||||
|
|
@ -265,7 +265,7 @@
|
|||
// lblSatelites
|
||||
//
|
||||
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.Name = "lblSatelites";
|
||||
this.lblSatelites.Size = new System.Drawing.Size(99, 13);
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ namespace AgroBase.Forms
|
|||
txtLongitude.Text = Gps.Longitude.ToString();
|
||||
lblPrecisaoHorizontal.Text = "Precisão Horizontal: " + Gps.PrecisaoHorizontal.ToString("0.00");
|
||||
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";
|
||||
lblOrientacao.Text = "Orientação: " + Gps.OrientacaoReal.ToString("0.00") + "º (" + Gps.OrientacaoMovimento.ToString("0.00") + "º)";
|
||||
lblCursoVerdadeiro.Text = "Curso Verdadeiro: " + Gps.CursoVerdadeiro.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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ namespace AgroBase.Models
|
|||
public class GPSModel
|
||||
{
|
||||
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 Ntrip_ativado { get; set; }
|
||||
public double Latitude { get; set; }
|
||||
|
|
@ -19,7 +21,8 @@ namespace AgroBase.Models
|
|||
public double Velocidade { get; set; }
|
||||
public double Distancia { 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 CursoVerdadeiro { get; set; }
|
||||
public double OrientacaoReal { get; set; }
|
||||
|
|
@ -59,6 +62,8 @@ namespace AgroBase.Models
|
|||
{
|
||||
return new GPSModel()
|
||||
{
|
||||
TimestampPos = TimestampPos.Clone(),
|
||||
TimestampOri = TimestampOri.Clone(),
|
||||
Ntrip_ativado = Ntrip_ativado,
|
||||
NumeroSatelites = NumeroSatelites,
|
||||
Altitude = Altitude,
|
||||
|
|
|
|||
|
|
@ -686,7 +686,7 @@ namespace AgroBase
|
|||
var Bico = DispAtu.Dados.BicosPulverizadores.FirstOrDefault(x => x.ID == ID);
|
||||
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;
|
||||
(id_num, posicao, data) = Bico.ProtocoloComando();
|
||||
}
|
||||
|
|
@ -755,10 +755,11 @@ namespace AgroBase
|
|||
{
|
||||
List<Keys> ComandosImpedidos = new List<Keys>();
|
||||
|
||||
/*var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
|
||||
if (Sonar.Iniciado && Variaveis.OperacaoEmAndamento.Controle.SonarAtivado && Sonar.Resumo.DesvioNecessario)
|
||||
var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
|
||||
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:
|
||||
ComandosImpedidos.Add(Keys.Up);
|
||||
|
|
@ -769,8 +770,8 @@ namespace AgroBase
|
|||
case Direcao.Direita:
|
||||
ComandosImpedidos.Add(Keys.Left);
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
|
||||
if (UltrasonicA05Service.Iniciado)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -786,7 +786,7 @@ namespace AgroBase.Models.Modules
|
|||
|
||||
bool AtingiuPressao() => PressaoLinha >= Variaveis.OperacaoEmAndamento.Controle.PressaoLinha;
|
||||
|
||||
await FuncoesGlobais.AguardarCondicaoAsync(AtingiuPressao, 10000, 200);
|
||||
await FuncoesGlobais.AguardarCondicaoAsync(AtingiuPressao, 6000, 200);
|
||||
|
||||
BombaOk = AtingiuPressao();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2958,7 +2958,7 @@ namespace AgroBase.Models.Modules
|
|||
|
||||
VariaveisOperacao.PosicaoBase.Inicializado = iniciado;
|
||||
VariaveisOperacao.PosicaoBase.QualidadeFix = qualidadeFix;
|
||||
VariaveisOperacao.PosicaoBase.NumeroSatelites.valor = numeroSatelites;
|
||||
VariaveisOperacao.PosicaoBase.NumeroSatelites = numeroSatelites;
|
||||
VariaveisOperacao.PosicaoBase.PrecisaoHorizontal = precisao;
|
||||
VariaveisOperacao.PosicaoBase.Momento = DateTime.Now;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -727,6 +727,8 @@ namespace AgroBase.Models
|
|||
|
||||
Variaveis.OperacaoEmAndamento.Finalizando = true;
|
||||
|
||||
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, false, Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombasPressurizadoras.FirstOrDefault()?.ID ?? "", true);
|
||||
|
||||
Variaveis.OperacaoEmAndamento.Trajetoria?.LoopAtualizaDados();
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
|
@ -1023,12 +1025,24 @@ namespace AgroBase.Models
|
|||
{
|
||||
ControleAnterior.BicosAtuados = Controle.BicosAtuados.Select(x => x.Clone()).ToList();
|
||||
}
|
||||
bool enviarComando = false;
|
||||
for (int i = 0; i < _Controle.BicosAtuados.Count; i++)
|
||||
{
|
||||
if (ForcarEnvio || _Controle.BicosAtuados[i].ComandoAtuar != _ControleAnterior.BicosAtuados[i].ComandoAtuar)
|
||||
{
|
||||
GeneralJoystick.EnviarComandoAtuador(S_Code.sBIC, _Controle.BicosAtuados[i].ComandoAtuar, _Controle.BicosAtuados[i].ID);
|
||||
_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();
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -1423,6 +1448,22 @@ namespace AgroBase.Models
|
|||
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);
|
||||
}
|
||||
|
|
@ -1554,6 +1595,7 @@ namespace AgroBase.Models
|
|||
public TiposControladorDirecional TipoControleDirecional { get; set; } = TiposControladorDirecional.PID;
|
||||
|
||||
public List<MPCSimulacaoModel> SimulacaoMPC { get; set; } = new List<MPCSimulacaoModel>();
|
||||
public double Latencia { get; set; }
|
||||
|
||||
public OperacaoControleModel Clone()
|
||||
{
|
||||
|
|
@ -1580,7 +1622,8 @@ namespace AgroBase.Models
|
|||
PulverizadorAutomatico = PulverizadorAutomatico,
|
||||
MovimentoAutomatico = MovimentoAutomatico,
|
||||
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 WeedWorkerModel OperadorErvas { get; set; }
|
||||
public List<ManagerWorkerMessageResponseModulosPendentesModel> ModulosSaude { get; set; }
|
||||
public bool CanSerialAtivado { get; set; }
|
||||
|
||||
|
||||
|
||||
|
|
@ -1708,6 +1752,7 @@ namespace AgroBase.Models
|
|||
PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno,
|
||||
HerbicidaPorErva = HerbicidaPorErva,
|
||||
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),
|
||||
VolumeReservatorio = _DispAtu.Dados.VolumeReservatorio,
|
||||
PressaoLinha = _DispAtu.Dados.PressaoLinha,
|
||||
|
|
@ -1772,6 +1817,7 @@ namespace AgroBase.Models
|
|||
PercentualOperacao = _Trajetoria?.PercentualTrajetoria ?? 0,
|
||||
PercentualRuaAtual = _Trajetoria?.CorredorAtual?.Progresso ?? 0,
|
||||
IdxRuaAtual = _Trajetoria?.CorredorAtual?.Idx ?? 0,
|
||||
QtdPontos = _Trajetoria?.TrajetoriaFixa?.Count ?? 0
|
||||
};
|
||||
|
||||
var dadosGps = GPSService.UltimaLeitura.Clone();
|
||||
|
|
@ -1800,6 +1846,7 @@ namespace AgroBase.Models
|
|||
DataInicio = Variaveis.OperacaoEmAndamento.DataInicio,
|
||||
DataFim = Variaveis.OperacaoEmAndamento.DataFim,
|
||||
TempoDecorridoSeg = Variaveis.OperacaoEmAndamento.TempoDecorridoSegs,
|
||||
CanSerialAtivado = CanManager.UseCanSerial,
|
||||
IMU = dadosIMU,
|
||||
Atuador = dadosAtuador,
|
||||
Movimentacao = dadosMovimentacao,
|
||||
|
|
@ -1834,6 +1881,7 @@ namespace AgroBase.Models
|
|||
DataInicio = DataInicio,
|
||||
DataFim = DataFim,
|
||||
TempoDecorridoSeg = TempoDecorridoSeg,
|
||||
CanSerialAtivado = CanSerialAtivado,
|
||||
Atuador = Atuador?.Clone(),
|
||||
Movimentacao = Movimentacao?.Clone(),
|
||||
Bateria = Bateria?.Clone(),
|
||||
|
|
@ -1885,6 +1933,7 @@ namespace AgroBase.Models
|
|||
public double PercentualErvasTerreno { get; set; }
|
||||
public double HerbicidaPorErva { get; set; }
|
||||
public bool ErvasNoRadar { get; set; }
|
||||
public double PercentualErvasNoRadar { get; set; }
|
||||
public double PercentualReservatorio { get; set; }
|
||||
public double VolumeReservatorio { get; set; }
|
||||
public double PressaoLinha { get; set; }
|
||||
|
|
@ -1899,6 +1948,7 @@ namespace AgroBase.Models
|
|||
{
|
||||
QtdCamerasSolo = QtdCamerasSolo,
|
||||
ErvasNoRadar = ErvasNoRadar,
|
||||
PercentualErvasNoRadar = PercentualErvasNoRadar,
|
||||
HerbicidaConsumido = HerbicidaConsumido,
|
||||
HerbicidaPorErva = HerbicidaPorErva,
|
||||
PercentualErvasTerreno = PercentualErvasTerreno,
|
||||
|
|
@ -2007,6 +2057,7 @@ namespace AgroBase.Models
|
|||
public int IdxRuaAtual { get; set; }
|
||||
public double PercentualOperacao { get; set; }
|
||||
public double PercentualRuaAtual { get; set; }
|
||||
public int QtdPontos { get; set; }
|
||||
|
||||
public OperacaoSensoriamentoLogTrajetoriaModel Clone()
|
||||
{
|
||||
|
|
@ -2038,6 +2089,7 @@ namespace AgroBase.Models
|
|||
IdxRuaAtual = IdxRuaAtual,
|
||||
PercentualOperacao = PercentualOperacao,
|
||||
PercentualRuaAtual = PercentualRuaAtual,
|
||||
QtdPontos = QtdPontos
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ namespace AgroBase.Models.Operadores
|
|||
public TipoMovimentoDirecional tipo_movimento { get; set; }
|
||||
public List<double[]> simulacao { get; set; }
|
||||
public int heartbeat { get; set; }
|
||||
public double latencia { get; set; }
|
||||
}
|
||||
|
||||
public class ManagerWorkerMessageResponseModulosPendentesModel
|
||||
|
|
@ -62,6 +63,7 @@ namespace AgroBase.Models.Operadores
|
|||
public double saude { get; set; }
|
||||
public List<string> motivos { get; set; }
|
||||
public List<ManagerWorkerMessageResponseModulosPendentesSaudeModel> saude_individual { get; set; }
|
||||
public List<ManagerWorkerMessageResponseModulosPendentesSaudeCondicoesModel> condicoes_operacionais { get; set; }
|
||||
}
|
||||
|
||||
public class ManagerWorkerMessageResponseModulosPendentesSaudeModel
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace AgroBase.Models.Operadores
|
|||
public Dictionary<CameraFrameType, OAKCameraFrameModel> CameraFrames { get; set; } = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
|
||||
public VisualWorkerMessageAnaliseModel Analises { get; set; } = new VisualWorkerMessageAnaliseModel();
|
||||
public VisualWorkerMessageIMUModel Imu { get; set; } = new VisualWorkerMessageIMUModel();
|
||||
public StatusModulo Status { get; set; }
|
||||
|
||||
public VisualWorkerModel Clone()
|
||||
{
|
||||
|
|
@ -29,6 +30,7 @@ namespace AgroBase.Models.Operadores
|
|||
Analises = Analises?.Clone(),
|
||||
Imu = Imu?.Clone(),
|
||||
UltimaMensagem = UltimaMensagem,
|
||||
Status = Status
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +43,7 @@ namespace AgroBase.Models.Operadores
|
|||
CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
|
||||
Analises = new VisualWorkerMessageAnaliseModel();
|
||||
Imu = new VisualWorkerMessageIMUModel();
|
||||
Status = StatusModulo.Desconectado;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static OpenTK.Graphics.OpenGL.GL;
|
||||
|
||||
namespace AgroBase.Models.Operadores
|
||||
{
|
||||
|
|
|
|||
|
|
@ -699,13 +699,15 @@ namespace AgroBase.Models
|
|||
dictParams.TryGetValue("tipo_movimento_direcional", out var _movimento);
|
||||
dictParams.TryGetValue("simulacao", out var _simulacao);
|
||||
dictParams.TryGetValue("heartbeat", out var _heartbeat);
|
||||
dictParams.TryGetValue("latencia", out var _latencia);
|
||||
novoComando = new ManagerWorkerMessageResponseComandoModel()
|
||||
{
|
||||
angulo = Convert.ToDouble(_angulo),
|
||||
percentual_velocidade = Convert.ToDouble(_velocidade),
|
||||
tipo_movimento = (TipoMovimentoDirecional)Convert.ToInt32(_movimento),
|
||||
simulacao = JsonConvert.DeserializeObject<List<double[]>>((_simulacao ?? "").ToString()),
|
||||
heartbeat = Convert.ToInt32(_heartbeat)
|
||||
heartbeat = Convert.ToInt32(_heartbeat),
|
||||
latencia = Convert.ToDouble(_latencia.ToString().Replace(".", ",")),
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -724,6 +726,7 @@ namespace AgroBase.Models
|
|||
_Controle.TipoMovimento = novoComando.tipo_movimento;
|
||||
_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.Latencia = novoComando.latencia;
|
||||
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao();
|
||||
}
|
||||
|
||||
|
|
@ -1075,7 +1078,7 @@ namespace AgroBase.Models
|
|||
double distancia = GPSUtils.DistanciaEntrePontos(ultimoPontoTrajetoria.Posicao, PontoInicial.Posicao);
|
||||
bool distanciaMinima = distancia < DistanciaManobraEntreRuas;
|
||||
|
||||
if (distanciaMinima)
|
||||
if (distanciaMinima && false)
|
||||
{
|
||||
int pontosAdicionr = Convert.ToInt32(CorredoresLarguras[idx] / (DistanciaEntrePontosCurva * _FatorLarguraCorredor));
|
||||
|
||||
|
|
@ -1149,7 +1152,8 @@ namespace AgroBase.Models
|
|||
anguloProjetar2 += anguloAcrescentar;
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ using Microsoft.Web.WebView2.WinForms;
|
|||
using Microsoft.Web.WebView2.Core;
|
||||
using System.Text.Json;
|
||||
using AgroBase.Services.Operadores;
|
||||
using MathNet.Numerics.Distributions;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AgroBase.Models
|
||||
{
|
||||
|
|
@ -113,7 +115,7 @@ namespace AgroBase.Models
|
|||
public static double TensaoMaximaBateria { get; set; } = 42.0;
|
||||
public static double CorrenteMaximaBateria { get; set; } = 20.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 ReducaoDirecional { get; set; } = 20.0;
|
||||
public static int RPM_Min_Roda { get; set; } = 15;
|
||||
|
|
@ -381,40 +383,41 @@ namespace AgroBase.Models
|
|||
|
||||
// Integração
|
||||
double theta = anguloAtualDeg * Math.PI / 180.0;
|
||||
double omega = -velocidadeMs * kappaEf; // rad/s
|
||||
double omega = velocidadeMs * kappaEf; // rad/s
|
||||
double dtheta = omega * tempoDelta;
|
||||
theta += dtheta;
|
||||
|
||||
double moveHeading = theta; // direção de deslocamento
|
||||
double heading = theta;
|
||||
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;
|
||||
if (tipoMovimento == TipoMovimentoDirecional.MovimentoDiagonal || Math.Abs(omega) < 1e-6)
|
||||
{
|
||||
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
|
||||
}
|
||||
// >>> mesma convenção do Python: 0=Norte
|
||||
double dx = velocidadeMs * tempoDelta * Math.Sin(heading); // Leste(+)
|
||||
double dy = velocidadeMs * tempoDelta * Math.Cos(heading); // Norte(+)
|
||||
|
||||
// Atualiza posição geográfica (aproximação local)
|
||||
// geo
|
||||
double R_earth = GPSUtils.RaioDaTerra;
|
||||
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;
|
||||
|
||||
return new GPSModel
|
||||
GPSModel ultimaPosicao = GPSService.historicoPosicao.Peek();
|
||||
GPSModel novaPosicao = new GPSModel
|
||||
{
|
||||
Momento = DateTime.Now,
|
||||
Latitude = pos.Latitude + dLat,
|
||||
Longitude = pos.Longitude + dLon,
|
||||
OrientacaoReal = GPSUtils.NormalizarAngulo(theta * 180.0 / Math.PI),
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,20 @@ namespace AgroBase.Services
|
|||
// 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
|
||||
|
|
@ -143,7 +157,7 @@ namespace AgroBase.Services
|
|||
public interface ICanService : IDisposable
|
||||
{
|
||||
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 Fechar();
|
||||
string _portName { get; set; }
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace AgroBase.Services
|
|||
public SerialPort _PortaCAN;
|
||||
public string _portName { get; set; }
|
||||
private int _baudRate;
|
||||
private int _bitRate;
|
||||
private object _lock = new object();
|
||||
public bool IsConnected { get; private set; }
|
||||
public bool Iniciado
|
||||
|
|
@ -31,6 +32,7 @@ namespace AgroBase.Services
|
|||
private ConcurrentQueue<string> FramesRecebidos = new ConcurrentQueue<string>();
|
||||
private StringBuilder SerialCANBuffer = new StringBuilder();
|
||||
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 List<CanMessage> Mensagens = new List<CanMessage>();
|
||||
|
|
@ -50,14 +52,20 @@ namespace AgroBase.Services
|
|||
|
||||
public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
|
||||
{
|
||||
_roteadores[endereco_rx] = handler;
|
||||
MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Inicializar(SerialPort Porta = null)
|
||||
public bool Inicializar(SerialPort Porta = null, int BitRate = 250)
|
||||
{
|
||||
try
|
||||
{
|
||||
_bitRate = BitRate;
|
||||
if (Porta == null && _PortaCAN == null)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -86,7 +94,7 @@ namespace AgroBase.Services
|
|||
Thread.Sleep(50);
|
||||
EnviarComando("Z0\r"); // desabilita timestamp
|
||||
Thread.Sleep(50);
|
||||
EnviarComando("S6\r"); // 500 kbps
|
||||
EnviarComando(ComandoBitRate(_bitRate)); // 500 kbps
|
||||
Thread.Sleep(50);
|
||||
EnviarComando("O\r"); // open
|
||||
Thread.Sleep(50);
|
||||
|
|
@ -94,16 +102,19 @@ namespace AgroBase.Services
|
|||
_PortaCAN.DiscardInBuffer();
|
||||
_PortaCAN.DiscardOutBuffer();
|
||||
|
||||
UltimoRx = DateTime.Now;
|
||||
UltimoTx = DateTime.Now;
|
||||
|
||||
tmrOuvirCAN?.Dispose();
|
||||
tmrOuvirCAN = new AsyncTaskTimerModel("tmrOuvirCAN", OuvirCAN, 5);
|
||||
tmrOuvirCAN = new AsyncTaskTimerModel("tmrOuvirCAN", OuvirCAN, 1);
|
||||
tmrOuvirCAN.Start();
|
||||
|
||||
tmrEnviarCAN?.Dispose();
|
||||
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 5);
|
||||
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 3);
|
||||
tmrEnviarCAN.Start();
|
||||
|
||||
tmrProcessarCAN?.Dispose();
|
||||
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 10);
|
||||
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 5);
|
||||
tmrProcessarCAN.Start();
|
||||
|
||||
IsConnected = true;
|
||||
|
|
@ -126,20 +137,22 @@ namespace AgroBase.Services
|
|||
|
||||
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;
|
||||
|
||||
while (processed < MAX_BATCH && FramesRecebidos.TryDequeue(out string frame))
|
||||
{
|
||||
if (FramesRecebidos.TryDequeue(out string frame))
|
||||
{
|
||||
ProcessarFrameCAN(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A fila ficou vazia antes de atingir o limite
|
||||
ProcessarFrameCAN(frame);
|
||||
processed++;
|
||||
|
||||
// Para não monopolizar o loop se entrar enxurrada de frames
|
||||
if (sw.ElapsedTicks * (1_000_000.0 / System.Diagnostics.Stopwatch.Frequency) > TIME_BUDGET_US)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LimparMensagensAntigas();
|
||||
VerificaSaudeConexao();
|
||||
}
|
||||
|
|
@ -371,31 +384,36 @@ namespace AgroBase.Services
|
|||
{
|
||||
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)
|
||||
{
|
||||
await Task.Delay(5);
|
||||
}
|
||||
//if (!PodeEnviarAgora(msg)) break; // sem token “perdido”: sai do burst e tenta no próximo tick
|
||||
|
||||
if (msg.Dispositivo == T_Code.Mks)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
if (!EnviarComando(frame))
|
||||
// Evite delays arbitrários em caminho “quente”
|
||||
// Se precisar espaçar MKS, use cooldown por-ID (abaixo)
|
||||
if (!EnviarComando(CriarFrame(msg)))
|
||||
{
|
||||
MostrarLog("Erro ao enviar frame", msg.IdTx);
|
||||
break; // evita apertar ainda mais se o driver falhar
|
||||
}
|
||||
|
||||
msg.Enviado = true;
|
||||
msg.EnviadoEm = DateTime.Now;
|
||||
_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)
|
||||
|
|
@ -444,8 +462,11 @@ namespace AgroBase.Services
|
|||
|
||||
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);
|
||||
try { _PortaCAN?.Close(); } catch { }
|
||||
try
|
||||
|
|
@ -453,10 +474,8 @@ namespace AgroBase.Services
|
|||
_PortaCAN?.Open();
|
||||
EnviarComando("C\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);
|
||||
UltimoRx = DateTime.MinValue;
|
||||
UltimoTx = DateTime.MinValue;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
|
@ -514,5 +533,52 @@ namespace AgroBase.Services
|
|||
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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
public WaveshareCANFDService Connector = new WaveshareCANFDService(0);
|
||||
public string _portName { get; set; } = "CAN-FD";
|
||||
private int _bitRate;
|
||||
public bool IsConnected { get; private set; }
|
||||
public bool Iniciado
|
||||
{
|
||||
|
|
@ -24,11 +25,11 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
private AsyncTaskTimerModel tmrEnviarCAN;
|
||||
private AsyncTaskTimerModel tmrOuvirCAN;
|
||||
private AsyncTaskTimerModel tmrProcessarCAN;
|
||||
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, bool> _roteadoresRegistrados = new ConcurrentDictionary<byte, bool>();
|
||||
private byte? _ultimoIdEnviado = null;
|
||||
private List<CanMessage> MensagensPendentes = new List<CanMessage>();
|
||||
private readonly object _LockMensagens = new object();
|
||||
|
|
@ -46,23 +47,48 @@ namespace AgroBase.Services
|
|||
|
||||
public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
|
||||
{
|
||||
_roteadores[endereco_rx] = handler;
|
||||
MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Inicializar(SerialPort Porta = null)
|
||||
public bool Inicializar(SerialPort Porta = null, int BitRate = 250)
|
||||
{
|
||||
_bitRate = BitRate;
|
||||
bool canIniciado = Connector.IsOpen;
|
||||
|
||||
if (!canIniciado)
|
||||
{
|
||||
canIniciado = Connector.Open((VCI_CAN_OBJ frame) =>
|
||||
canIniciado = Connector.Open((VCI_CAN_OBJ[] frames) =>
|
||||
{
|
||||
byte[] dataRx = new byte[frame.DataLen];
|
||||
Array.Copy(frame.Data, dataRx, frame.DataLen);
|
||||
FramesRecebidos.Enqueue((frame.ID, dataRx));
|
||||
UltimoRx = DateTime.Now;
|
||||
}, samplePoint: 0.8, bitrateKbps: 250);
|
||||
try
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
for (int i = 0; i < frames.Length; i++)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
|
@ -80,12 +106,15 @@ namespace AgroBase.Services
|
|||
|
||||
if (canIniciado || !IsConnected)
|
||||
{
|
||||
UltimoRx = DateTime.Now;
|
||||
UltimoTx = DateTime.Now;
|
||||
|
||||
tmrEnviarCAN?.Dispose();
|
||||
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 5);
|
||||
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 3);
|
||||
tmrEnviarCAN.Start();
|
||||
|
||||
tmrProcessarCAN?.Dispose();
|
||||
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 10);
|
||||
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 5);
|
||||
tmrProcessarCAN.Start();
|
||||
|
||||
IsConnected = true;
|
||||
|
|
@ -96,20 +125,22 @@ namespace AgroBase.Services
|
|||
|
||||
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;
|
||||
|
||||
while (processed < MAX_BATCH && FramesRecebidos.TryDequeue(out var frame))
|
||||
{
|
||||
if (FramesRecebidos.TryDequeue(out (uint, byte[]) frame))
|
||||
{
|
||||
ProcessarFrameCAN(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A fila ficou vazia antes de atingir o limite
|
||||
ProcessarFrameCAN(frame);
|
||||
processed++;
|
||||
|
||||
// Para não monopolizar o loop se entrar enxurrada de frames
|
||||
if (sw.ElapsedTicks * (1_000_000.0 / System.Diagnostics.Stopwatch.Frequency) > TIME_BUDGET_US)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LimparMensagensAntigas();
|
||||
VerificaSaudeConexao();
|
||||
}
|
||||
|
|
@ -125,10 +156,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
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;
|
||||
byte fCodeRx = data[0];
|
||||
|
|
@ -260,35 +288,56 @@ namespace AgroBase.Services
|
|||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
await Task.Delay(5);
|
||||
}
|
||||
//if (!PodeEnviarAgora(msg)) break; // sem token “perdido”: sai do burst e tenta no próximo tick
|
||||
|
||||
if (msg.Dispositivo == T_Code.Mks)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
if (!EnviarComando(frame, msg))
|
||||
// Evite delays arbitrários em caminho “quente”
|
||||
// Se precisar espaçar MKS, use cooldown por-ID (abaixo)
|
||||
if (!EnviarComando(CriarFrame(msg), msg))
|
||||
{
|
||||
MostrarLog("Erro ao enviar frame", msg.IdTx);
|
||||
break; // evita apertar ainda mais se o driver falhar
|
||||
}
|
||||
|
||||
msg.Enviado = true;
|
||||
msg.EnviadoEm = DateTime.Now;
|
||||
_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)
|
||||
|
|
@ -333,7 +382,7 @@ namespace AgroBase.Services
|
|||
.Where(x =>
|
||||
(!x.ComResposta && x.Enviado) ||
|
||||
(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))
|
||||
)
|
||||
.ToList();
|
||||
|
|
@ -346,15 +395,16 @@ namespace AgroBase.Services
|
|||
|
||||
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);
|
||||
try { Connector.Close(); } catch { }
|
||||
try
|
||||
{
|
||||
Inicializar();
|
||||
UltimoRx = DateTime.MinValue;
|
||||
UltimoTx = DateTime.MinValue;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
|
@ -362,7 +412,6 @@ namespace AgroBase.Services
|
|||
|
||||
public void Fechar()
|
||||
{
|
||||
tmrOuvirCAN?.Dispose();
|
||||
tmrEnviarCAN?.Dispose();
|
||||
|
||||
lock (_LockMensagens)
|
||||
|
|
@ -371,7 +420,10 @@ namespace AgroBase.Services
|
|||
}
|
||||
|
||||
SerialCANBuffer.Clear();
|
||||
FramesRecebidos = new ConcurrentQueue<(uint, byte[])>();
|
||||
while (FramesRecebidos.Count > 0)
|
||||
{
|
||||
FramesRecebidos.TryDequeue(out var frame);
|
||||
}
|
||||
_roteadores.Clear();
|
||||
_ultimoIdEnviado = null;
|
||||
|
||||
|
|
@ -405,5 +457,26 @@ namespace AgroBase.Services
|
|||
byte parametro = (data.Length > 1) ? data[1] : (byte)0x00;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
|
|
@ -44,8 +45,9 @@ namespace AgroBase.Services
|
|||
private static int rtk_timeout = 60;
|
||||
private static int TempoMin_Ntrip = 10;
|
||||
private static bool LoopRTK_Ntrip = false;
|
||||
public static bool CorrecaoRTK_Ntrip = true;
|
||||
public static bool CorrecaoRTK_Ntrip = false;
|
||||
public static DateTime UltimoEnvioCorrecaoRTK = DateTime.MinValue;
|
||||
private static AsyncTaskTimerModel tmrHeartbeat;
|
||||
|
||||
public static void AtualizarPortaCOM(SerialPort Porta)
|
||||
{
|
||||
|
|
@ -75,6 +77,9 @@ namespace AgroBase.Services
|
|||
{
|
||||
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[] comandos = {
|
||||
|
|
@ -126,6 +131,7 @@ namespace AgroBase.Services
|
|||
|
||||
// heading 2 antenas
|
||||
$"config heading fixlength\r\n",
|
||||
$"config heading tractor\r\n",
|
||||
$"config heading length {comprimento_antena} {tolerancia_antena}\r\n",
|
||||
// (se precisar, existe 'config heading offset <azim_off> <pitch_off>')
|
||||
|
||||
|
|
@ -262,7 +268,7 @@ namespace AgroBase.Services
|
|||
AtualizarCoordenadasGPS();
|
||||
}
|
||||
// Coordenadas
|
||||
else if (sentenca.StartsWith("$GNGGA"))
|
||||
else if (sentenca.StartsWith("$GNGGA") || sentenca.StartsWith("$GLGGA"))
|
||||
{
|
||||
ProcessarGNGGA(sentenca);
|
||||
AtualizarCoordenadasGPS();
|
||||
|
|
@ -283,9 +289,10 @@ namespace AgroBase.Services
|
|||
ProcessarGSV(sentenca);
|
||||
}
|
||||
// Orientação real
|
||||
else if (sentenca.StartsWith("$GNTHS") || sentenca.StartsWith("$GPTHS"))
|
||||
else if (sentenca.StartsWith("$GNTHS") || sentenca.StartsWith("$GPTHS") || sentenca.StartsWith("$GATHS"))
|
||||
{
|
||||
ProcessarGNTHS(sentenca);
|
||||
//AtualizarCoordenadasGPS();
|
||||
}
|
||||
// GLL (GP/GN/GL/GA/BD)
|
||||
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(',');
|
||||
|
||||
PenultimaLeitura.Momento = UltimaLeitura.Momento;
|
||||
PenultimaLeitura.TimestampPos = UltimaLeitura.TimestampPos.Clone();
|
||||
PenultimaLeitura.Latitude = UltimaLeitura.Latitude;
|
||||
PenultimaLeitura.Longitude = UltimaLeitura.Longitude;
|
||||
PenultimaLeitura.Altitude = UltimaLeitura.Altitude;
|
||||
PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal;
|
||||
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] != "")
|
||||
{
|
||||
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.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.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}");
|
||||
|
||||
PenultimaLeitura.TimestampPos = UltimaLeitura.TimestampPos.Clone();
|
||||
PenultimaLeitura.Momento = UltimaLeitura.Momento;
|
||||
PenultimaLeitura.Latitude = UltimaLeitura.Latitude;
|
||||
PenultimaLeitura.Longitude = UltimaLeitura.Longitude;
|
||||
PenultimaLeitura.Altitude = UltimaLeitura.Altitude;
|
||||
PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal;
|
||||
PenultimaLeitura.NumeroSatelites.valor = UltimaLeitura.NumeroSatelites.valor;
|
||||
PenultimaLeitura.NumeroSatelites = UltimaLeitura.NumeroSatelites;
|
||||
PenultimaLeitura.QualidadeFix = UltimaLeitura.QualidadeFix;
|
||||
PenultimaLeitura.DataHora = UltimaLeitura.DataHora;
|
||||
PenultimaLeitura.IdadeCorrecao = UltimaLeitura.IdadeCorrecao;
|
||||
|
||||
// Armazenar os valores na última leitura
|
||||
UltimaLeitura.TimestampPos.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
||||
UltimaLeitura.Momento = DateTime.Now;
|
||||
UltimaLeitura.Latitude = latitude;
|
||||
UltimaLeitura.Longitude = longitude;
|
||||
UltimaLeitura.Altitude = altitude;
|
||||
UltimaLeitura.PrecisaoHorizontal = precisao;
|
||||
UltimaLeitura.NumeroSatelites.valor = nsatelites;
|
||||
UltimaLeitura.NumeroSatelites = nsatelites;
|
||||
UltimaLeitura.QualidadeFix = (TiposCorrecaoGPS)fix;
|
||||
UltimaLeitura.IdadeCorrecao = string.IsNullOrEmpty(idadeCorrecaoRaw) ? -1 : idadeCorrecao;
|
||||
|
||||
|
|
@ -537,6 +550,7 @@ namespace AgroBase.Services
|
|||
|
||||
private static void ProcessarGNTHS(string sentenca)
|
||||
{
|
||||
//Console.WriteLine(sentenca);
|
||||
// Remove o caractere de início '$' e divide os campos
|
||||
var campos = sentenca.TrimStart('$').Split(',');
|
||||
|
||||
|
|
@ -558,10 +572,12 @@ namespace AgroBase.Services
|
|||
string status = campos[2].Split('*')[0]; // Campo <Status>
|
||||
string checksum = campos[2].Split('*')[1]; // Campo <Checksum>
|
||||
|
||||
PenultimaLeitura.TimestampOri = UltimaLeitura.TimestampOri.Clone();
|
||||
PenultimaLeitura.Momento = UltimaLeitura.Momento;
|
||||
PenultimaLeitura.OrientacaoReal = UltimaLeitura.OrientacaoReal;
|
||||
PenultimaLeitura.TipoOrientacao = UltimaLeitura.TipoOrientacao;
|
||||
|
||||
UltimaLeitura.TimestampOri.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
||||
UltimaLeitura.Momento = DateTime.Now;
|
||||
UltimaLeitura.OrientacaoReal = headingTrue;
|
||||
UltimaLeitura.TipoOrientacao = status;
|
||||
|
|
@ -661,7 +677,7 @@ namespace AgroBase.Services
|
|||
|
||||
// Atualiza apenas o que faz sentido complementar
|
||||
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:
|
||||
UltimaLeitura.PDOP = !double.IsInfinity(pdop) && !double.IsNaN(pdop) ? pdop : UltimaLeitura.PDOP;
|
||||
|
|
@ -1083,7 +1099,7 @@ namespace AgroBase.Services
|
|||
|
||||
if (Variaveis.OperacaoEmAndamento.Simulando)
|
||||
{
|
||||
anguloFinal = UltimaLeitura.OrientacaoMovimento;
|
||||
anguloFinal = UltimaLeitura.OrientacaoReal;
|
||||
}
|
||||
else if (gpsIniciado)
|
||||
{
|
||||
|
|
@ -1194,25 +1210,47 @@ namespace AgroBase.Services
|
|||
return anguloUnificado;
|
||||
}
|
||||
|
||||
|
||||
private static async Task tmrHeartbeat_Tick()
|
||||
{
|
||||
AtualizaDadosRedis();
|
||||
}
|
||||
|
||||
private static void AtualizaDadosRedis()
|
||||
{
|
||||
GPSModel posicaoAtual = Variaveis.OperacaoEmAndamento.Simulando ? historicoPosicao.Peek() : UltimaLeitura;
|
||||
|
||||
RedisService.AtualizarCampos(
|
||||
RedisService.ModKey(T_Code.Gps),
|
||||
("timestamp", UltimaLeitura.Momento),
|
||||
("conectado", Iniciado),
|
||||
("freq_base", TaxaAmostragemHz),
|
||||
("lat", UltimaLeitura.Latitude),
|
||||
("lon", UltimaLeitura.Longitude),
|
||||
("theta", UltimaLeitura.AnguloCarroDefinido),
|
||||
("fix", (int)UltimaLeitura.QualidadeFix),
|
||||
("rtk", UltimaLeitura.IdadeCorrecao < rtk_timeout),
|
||||
("hAcc", UltimaLeitura.PrecisaoCm),
|
||||
("nSatelites", UltimaLeitura.NumeroSatelites.valor),
|
||||
("freq", UltimaLeitura.NumeroSatelites.frequencia),
|
||||
("age", UltimaLeitura.IdadeCorrecao),
|
||||
("heartbeat", UltimaLeitura.Heartbeat)
|
||||
("lat", posicaoAtual.Latitude),
|
||||
("lon", posicaoAtual.Longitude),
|
||||
("theta", posicaoAtual.AnguloCarroDefinido),
|
||||
("fix", (int)posicaoAtual.QualidadeFix),
|
||||
("rtk", posicaoAtual.IdadeCorrecao < rtk_timeout),
|
||||
("hAcc", posicaoAtual.PrecisaoCm),
|
||||
("nSatelites", posicaoAtual.NumeroSatelites),
|
||||
("freq.posicao", posicaoAtual.TimestampPos.frequencia),
|
||||
("freq.orientacao", posicaoAtual.TimestampOri.frequencia),
|
||||
("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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ namespace AgroBase.Services
|
|||
fix.AddRange(CanPrefix(DadosLoRaParse.GpsQualidade));
|
||||
fix.Add((byte)(Gps.Inicializado ? 1 : 0)); // 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
|
||||
|
||||
List<byte> _lat = new List<byte>();
|
||||
|
|
|
|||
|
|
@ -114,33 +114,44 @@ namespace AgroBase.Services.Operadores
|
|||
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(
|
||||
CtxKey.DadosContexto,
|
||||
("Trajetoria", _Sensoriamento.Trajetoria != null ? new
|
||||
("Trajetoria", _t != null ? new
|
||||
{
|
||||
status = (int)_Sensoriamento.Trajetoria.StatusCarro,
|
||||
manobrando = _Sensoriamento.Trajetoria.ManobrandoEntreRuas,
|
||||
erro_angular = _Sensoriamento.Trajetoria.ErroOrientacaoAngularCombinado,
|
||||
erro_angular_caminho = _Sensoriamento.Trajetoria.ErroOrientacaoAngularCaminho,
|
||||
erro_angular_proximo_ponto = _Sensoriamento.Trajetoria.ErroOrientacaoAngularProximoPonto,
|
||||
idx_proximo_ponto = _Sensoriamento.Trajetoria.ProximoPonto.idxPonto,
|
||||
status = (int)_t.StatusCarro,
|
||||
manobrando = _t.ManobrandoEntreRuas,
|
||||
erro_angular = _t.ErroOrientacaoAngularCombinado,
|
||||
erro_angular_caminho = _t.ErroOrientacaoAngularCaminho,
|
||||
erro_angular_proximo_ponto = _t.ErroOrientacaoAngularProximoPonto,
|
||||
idx_proximo_ponto = _t.ProximoPonto.idxPonto,
|
||||
idx_ponto_alvo = idx_p_alvo,
|
||||
CorredorAtual = new
|
||||
{
|
||||
concluido = _Sensoriamento.Trajetoria.CorredorAtual.Concluido,
|
||||
dentro = _Sensoriamento.Trajetoria.CorredorAtual.Dentro,
|
||||
ultimo = _Sensoriamento.Trajetoria.CorredorAtual.Ultimo,
|
||||
largura = _Sensoriamento.Trajetoria.CorredorAtual.Largura,
|
||||
progresso = _Sensoriamento.Trajetoria.CorredorAtual.Progresso,
|
||||
dist_esq = _Sensoriamento.Trajetoria.DistanciaEsquerda,
|
||||
dist_dir = _Sensoriamento.Trajetoria.DistanciaDireita,
|
||||
angulo = _Sensoriamento.Trajetoria.AnguloCaminho,
|
||||
concluido = _t.CorredorAtual.Concluido,
|
||||
dentro = _t.CorredorAtual.Dentro,
|
||||
ultimo = _t.CorredorAtual.Ultimo,
|
||||
largura = _t.CorredorAtual.Largura,
|
||||
progresso = _t.CorredorAtual.Progresso,
|
||||
dist_esq = _t.DistanciaEsquerda,
|
||||
dist_dir = _t.DistanciaDireita,
|
||||
angulo = _t.AnguloMedioCorredor,
|
||||
},
|
||||
} : null
|
||||
),
|
||||
("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,
|
||||
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;
|
||||
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
|
||||
.Where(y => y.analise_health)
|
||||
|
|
@ -462,7 +473,7 @@ namespace AgroBase.Services.Operadores
|
|||
int id = bico.ID_Num;
|
||||
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
|
||||
.Where(y => y.analise_health)
|
||||
|
|
@ -501,7 +512,7 @@ namespace AgroBase.Services.Operadores
|
|||
int id = bomba.ID_Num;
|
||||
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
|
||||
.Where(y => y.analise_health)
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ namespace AgroBase.Services.Operadores
|
|||
catch { /* opcional: logar erro */ }
|
||||
}
|
||||
|
||||
DadosLeitura.Status = HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == Enums.T_Code.Snr)?.status ?? Enums.StatusModulo.Desconectado;
|
||||
|
||||
// VisualWorker
|
||||
var json = RedisService.Get(CtxKey.DadosVisualWorker);
|
||||
if (string.IsNullOrWhiteSpace(json)) return;
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ namespace AgroBase.Services.Operadores
|
|||
RedisService.AtualizarCampos(
|
||||
CtxKey.DadosWeedWorker,
|
||||
("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.estatisticas", new WeedWorkerAnaliseEstatisticasModel())
|
||||
);
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ namespace AgroBase.Services
|
|||
|
||||
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));
|
||||
DefinirEquipamentoDesconectado();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ namespace AgroBase.Services
|
|||
|
||||
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)
|
||||
{
|
||||
DispositivosMapeados.Remove(Dispositivo);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using AgroBase.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -9,15 +10,39 @@ public class WaveshareCANFDService
|
|||
private const uint DEVICE_INDEX = 0;
|
||||
|
||||
private readonly uint channelIndex;
|
||||
private Action<VCI_CAN_OBJ> callback;
|
||||
private AsyncTaskTimerModel tmrLeitura;
|
||||
private AsyncTaskTimerModel tmrWatchdogRx;
|
||||
private AsyncTaskTimerModel tmrWatchdogSaude;
|
||||
private readonly int delayTickTimer = 1;
|
||||
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 int Canal => ((int)channelIndex + 1);
|
||||
|
||||
private int _lastKbps = 500;
|
||||
private double _lastSp = 0.875;
|
||||
|
||||
public WaveshareCANFDService(uint channelIndex)
|
||||
{
|
||||
this.channelIndex = channelIndex;
|
||||
|
|
@ -62,6 +87,8 @@ public class WaveshareCANFDService
|
|||
|
||||
[DllImport("ControlCANFD.dll", CallingConvention = CallingConvention.StdCall)]
|
||||
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)]
|
||||
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)]
|
||||
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;
|
||||
|
||||
// >>> 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;
|
||||
if (!InitAndStartCAN()) return false;
|
||||
|
||||
tmrLeitura?.Dispose();
|
||||
tmrLeitura = new AsyncTaskTimerModel($"tmrLeitura_CAN_{Canal}", tmrLeitura_Tick, delayTickTimer);
|
||||
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;
|
||||
Console.WriteLine($"[CAN-FD] Canal {Canal} aberto @ {bitrateKbps} kbps, SP≈{samplePoint * 100:0.#}%");
|
||||
return true;
|
||||
|
|
@ -154,27 +176,56 @@ public class WaveshareCANFDService
|
|||
_rxBusy = true;
|
||||
try
|
||||
{
|
||||
// (re)aloca buffer local por tick para evitar lixo compartilhado
|
||||
var local = new VCI_CAN_OBJ[100];
|
||||
for (int i = 0; i < local.Length; i++)
|
||||
local[i].Data = new byte[8];
|
||||
_ultimoRxTick = DateTime.Now;
|
||||
int emptyReads = 0;
|
||||
|
||||
var count = VCI_Receive(DEVICE_TYPE, DEVICE_INDEX, channelIndex, local, 100, 100);
|
||||
if (count > 0)
|
||||
const int MAX_PER_TICK = 512;
|
||||
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];
|
||||
var data = new byte[frame.DataLen];
|
||||
Array.Copy(frame.Data, data, frame.DataLen);
|
||||
emptyReads++;
|
||||
break; // sem dados agora; libera CPU
|
||||
}
|
||||
|
||||
// 🔧 substitui o Data pelo array "desanexado" do buffer
|
||||
frame.Data = data;
|
||||
// chegou algo
|
||||
_ultimoRxFrame = DateTime.Now;
|
||||
_rxRecoveryStep = 0; // reset da escada de recovery, está saudável
|
||||
|
||||
// entrega frame "isolado" ao callback
|
||||
callback?.Invoke(frame);
|
||||
for (int i = 0; i < got && total < MAX_PER_TICK; i++, total++)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
|
@ -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()
|
||||
{
|
||||
if (!isOpen) return;
|
||||
tmrLeitura?.Dispose();
|
||||
tmrWatchdogRx?.Dispose();
|
||||
tmrWatchdogSaude?.Dispose();
|
||||
VCI_CloseDevice(DEVICE_TYPE, DEVICE_INDEX);
|
||||
isOpen = false;
|
||||
Console.WriteLine($"[CAN-FD] Canal {Canal} fechado");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"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",
|
||||
"config_version": 0,
|
||||
"model_version": "0",
|
||||
|
|
@ -27,7 +20,14 @@
|
|||
"padded_top_topics_start_index": 0,
|
||||
"taxonomy_version": 0,
|
||||
"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",
|
||||
"next_scheduled_calculation_time": "13401199940855097"
|
||||
"next_scheduled_calculation_time": "13401819543884442"
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/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/08/25-17:06:11.381 d6c 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.510 23f4 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.517 23f4 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/08/25-17:05:24.181 da4 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.765 3b04 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.773 3b04 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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"}}}
|
||||
|
|
@ -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
|
|
@ -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/08/25-17:07:32.656 d6c 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.010 23f4 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.012 23f4 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/08/25-17:05:59.945 da4 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.810 3b04 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.812 3b04 Recovering log #3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/08/25-17:06:11.309 51e4 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.431 67f8 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.433 67f8 Recovering log #7
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/08/25-17:05:24.106 698c 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.681 4a8c 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.683 4a8c Recovering log #7
|
||||
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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
139.0.3405.111
|
||||
139.0.3405.125
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1,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
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_b2cfb7c0b8cd21733288866dbc6da89b {
|
||||
#map_b38d811fa61af8b65eff35dfc9035850 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,14 +54,14 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_b2cfb7c0b8cd21733288866dbc6da89b" ></div>
|
||||
<div class="folium-map" id="map_b38d811fa61af8b65eff35dfc9035850" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_b2cfb7c0b8cd21733288866dbc6da89b = L.map(
|
||||
"map_b2cfb7c0b8cd21733288866dbc6da89b",
|
||||
var map_b38d811fa61af8b65eff35dfc9035850 = L.map(
|
||||
"map_b38d811fa61af8b65eff35dfc9035850",
|
||||
{
|
||||
center: [0.0, 0.0],
|
||||
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",
|
||||
{
|
||||
"minZoom": 0,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
);
|
||||
|
||||
|
||||
tile_layer_bab172d3f76c66b031f76d6ae9baaaaf.addTo(map_b2cfb7c0b8cd21733288866dbc6da89b);
|
||||
tile_layer_ad727f4f6e04926dedef75d899276bfb.addTo(map_b38d811fa61af8b65eff35dfc9035850);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_b2cfb7c0b8cd21733288866dbc6da89b);
|
||||
trajeto_json.addTo(map_b38d811fa61af8b65eff35dfc9035850);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -179,9 +179,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
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(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_b2cfb7c0b8cd21733288866dbc6da89b.setView(novaPosicao, map_b2cfb7c0b8cd21733288866dbc6da89b.getZoom());
|
||||
map_b38d811fa61af8b65eff35dfc9035850.setView(novaPosicao, map_b38d811fa61af8b65eff35dfc9035850.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@
|
|||
marcadorDinamico.setRotationAngle(angulo);
|
||||
|
||||
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
|
||||
map_b2cfb7c0b8cd21733288866dbc6da89b.setView(novaPosicao, map_b2cfb7c0b8cd21733288866dbc6da89b.getZoom());*/
|
||||
map_b38d811fa61af8b65eff35dfc9035850.setView(novaPosicao, map_b38d811fa61af8b65eff35dfc9035850.getZoom());*/
|
||||
});
|
||||
|
||||
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_b21baaed2057e8bbe47af17d68ecedd6 {
|
||||
#map_21ca758b860567b95d146fc0853c971f {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,16 +54,16 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_b21baaed2057e8bbe47af17d68ecedd6" ></div>
|
||||
<div class="folium-map" id="map_21ca758b860567b95d146fc0853c971f" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_b21baaed2057e8bbe47af17d68ecedd6 = L.map(
|
||||
"map_b21baaed2057e8bbe47af17d68ecedd6",
|
||||
var map_21ca758b860567b95d146fc0853c971f = L.map(
|
||||
"map_21ca758b860567b95d146fc0853c971f",
|
||||
{
|
||||
center: [-22.172640909416664, -47.3952078835],
|
||||
center: [-22.172636164916668, -47.395186322185666],
|
||||
crs: L.CRS.EPSG3857,
|
||||
...{
|
||||
"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",
|
||||
{
|
||||
"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({
|
||||
|
||||
|
|
@ -148,23 +148,23 @@
|
|||
}*/
|
||||
});
|
||||
};
|
||||
var geo_json_99cfc632ca5e92adc9931d372fcc1b88 = L.geoJson(null, {
|
||||
onEachFeature: geo_json_99cfc632ca5e92adc9931d372fcc1b88_onEachFeature,
|
||||
var geo_json_40d1c4f07ee5691fa37b3e9a1cd24536 = L.geoJson(null, {
|
||||
onEachFeature: geo_json_40d1c4f07ee5691fa37b3e9a1cd24536_onEachFeature,
|
||||
|
||||
...{
|
||||
}
|
||||
});
|
||||
|
||||
function geo_json_99cfc632ca5e92adc9931d372fcc1b88_add (data) {
|
||||
geo_json_99cfc632ca5e92adc9931d372fcc1b88
|
||||
function geo_json_40d1c4f07ee5691fa37b3e9a1cd24536_add (data) {
|
||||
geo_json_40d1c4f07ee5691fa37b3e9a1cd24536
|
||||
.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_99cfc632ca5e92adc9931d372fcc1b88.setStyle(function(feature) {return feature.properties.style;});
|
||||
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_40d1c4f07ee5691fa37b3e9a1cd24536.setStyle(function(feature) {return feature.properties.style;});
|
||||
|
||||
|
||||
|
||||
geo_json_99cfc632ca5e92adc9931d372fcc1b88.addTo(map_b21baaed2057e8bbe47af17d68ecedd6);
|
||||
geo_json_40d1c4f07ee5691fa37b3e9a1cd24536.addTo(map_21ca758b860567b95d146fc0853c971f);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_b21baaed2057e8bbe47af17d68ecedd6);
|
||||
trajeto_json.addTo(map_21ca758b860567b95d146fc0853c971f);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
}
|
||||
trajeto_dinamico_json_add({"features": []});
|
||||
|
||||
trajeto_dinamico_json.addTo(map_b21baaed2057e8bbe47af17d68ecedd6);
|
||||
trajeto_dinamico_json.addTo(map_21ca758b860567b95d146fc0853c971f);
|
||||
|
||||
function adicionarGeometriaDinamica(novaGeometria) {
|
||||
trajeto_dinamico_json.addData(novaGeometria);
|
||||
|
|
@ -296,9 +296,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
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(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_b21baaed2057e8bbe47af17d68ecedd6.setView(novaPosicao, map_b21baaed2057e8bbe47af17d68ecedd6.getZoom());
|
||||
map_21ca758b860567b95d146fc0853c971f.setView(novaPosicao, map_21ca758b860567b95d146fc0853c971f.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@
|
|||
function atualizarSelecaoRuas(selecionadas) {
|
||||
selecionadas = JSON.parse(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))) {
|
||||
layer.setStyle({ color: 'blue' });
|
||||
} else {
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -12,18 +12,6 @@ class CameraOak:
|
|||
self.modelo_ia_seg = modelo_ia_seg
|
||||
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.modelo = "Desconhecido"
|
||||
self.ultima_saude = {}
|
||||
|
|
@ -37,6 +25,19 @@ class CameraOak:
|
|||
self.rodando = 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["mx_id"] = self.mx_id
|
||||
_camera["versao"] = self.versao
|
||||
|
|
@ -88,7 +89,7 @@ class CameraOak:
|
|||
self.q_seg = self.device.getOutputQueue(name="seg", maxSize=1, blocking=False)
|
||||
|
||||
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:
|
||||
dadosSnr = ContextoGlobalRedis.get_operacao().get("Snr", {})
|
||||
|
|
@ -204,28 +205,25 @@ class CameraOak:
|
|||
script = pipeline.create(dai.node.Script)
|
||||
if self.modelo_ia_seg is not None or self.modelo_ia_det is not None:
|
||||
try:
|
||||
N_seg = (self.modelo_ia_seg or {}).get("det_every_n", 1)
|
||||
N_seg = (self.modelo_ia_seg or {}).get("seg_every_n", 1)
|
||||
N_det = (self.modelo_ia_det or {}).get("det_every_n", 1)
|
||||
script.setProcessor(dai.ProcessorType.LEON_CSS)
|
||||
script.setScript(f"""
|
||||
from time import monotonic
|
||||
i = 0
|
||||
while True:
|
||||
f = node.io['in'].get()
|
||||
"""
|
||||
+
|
||||
f"""
|
||||
if i % {N_seg} == 0:
|
||||
node.io['toSeg'].send(f)
|
||||
""" if self.modelo_ia_seg is not None else ""
|
||||
+
|
||||
f"""
|
||||
if i % {N_det} == 0:
|
||||
node.io['toDet'].send(f)
|
||||
""" if self.modelo_ia_det is not None else ""
|
||||
+
|
||||
f"""
|
||||
i += 1
|
||||
i = 0
|
||||
while True:
|
||||
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)
|
||||
|
||||
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)
|
||||
elif send_det:
|
||||
node.io['toDet'].send(f)
|
||||
# se nenhum for enviar, apenas descarta 'f'
|
||||
i += 1
|
||||
""")
|
||||
cam.video.link(script.inputs['in'])
|
||||
except Exception as e:
|
||||
|
|
@ -299,6 +297,7 @@ class CameraOak:
|
|||
det.out.link(xout_det.input)
|
||||
|
||||
script.outputs['toDet'].link(manip_det.inputImage)
|
||||
#cam.video.link(manip_det.inputImage)
|
||||
|
||||
# (opcional) passthrough para sincronizar timestamp/frame com a detecção
|
||||
# xout_det_img = pipeline.createXLinkOut()
|
||||
|
|
|
|||
|
|
@ -31,21 +31,26 @@ class CameraManager:
|
|||
else:
|
||||
cam_existente["timestamp"] = self._ultimo_scan
|
||||
|
||||
ContextoGlobalRedis.set(CtxKey.DadosCameras, cameras_mapeadas)
|
||||
|
||||
for cam_id in cameras_mapeadas.keys():
|
||||
ids_para_remover = []
|
||||
for cam_id, val in cameras_mapeadas.items():
|
||||
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:
|
||||
ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.AtualizarSaudeCamera.value } )
|
||||
elif cam.get("dispositivo", T_Code.Vzo.value) == T_Code.Cam.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")
|
||||
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 } )
|
||||
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 } )
|
||||
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -30,20 +30,25 @@ class ModuloAtuador(ModuloDiagnosticoBase):
|
|||
FREQ_MIN = FREQ_BASE * 0.5
|
||||
SAUDE_MIN_ALERTA = 80
|
||||
|
||||
sensor_massa_ativado = False
|
||||
|
||||
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
|
||||
motivos = []
|
||||
condicoes_operacionais = []
|
||||
aferir = dados_bico.get("aferir", False)
|
||||
padroes = self.padroes_sensores.get(tipo, {}).get(bico_id, {})
|
||||
conectado = dados_bico.get("conectado", False)
|
||||
mandatorio = dados_bico.get("mandatorio", False)
|
||||
aferir = dados_sensor.get("aferir", False)
|
||||
padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
|
||||
conectado = dados_sensor.get("conectado", 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:
|
||||
saude = 0
|
||||
motivos.append(f"desconectado")
|
||||
else:
|
||||
for campo in dados_bico.get("dados", []):
|
||||
for campo in dados_sensor.get("dados", []):
|
||||
label = campo.get("label")
|
||||
if label is None:
|
||||
continue
|
||||
|
|
@ -69,10 +74,10 @@ class ModuloAtuador(ModuloDiagnosticoBase):
|
|||
"descricao": f"{label} fora do padrão"
|
||||
})
|
||||
saude = max(0, saude)
|
||||
chave = f"{S_Code(int(tipo)).name}_{bico_id}"
|
||||
chave = f"{S_Code(int(tipo)).name}_{sensor_id}"
|
||||
saude_mod = {
|
||||
"id": bico_id,
|
||||
"label": dados_bico.get("label"),
|
||||
"id": sensor_id,
|
||||
"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,
|
||||
"saude": saude,
|
||||
"motivos": motivos,
|
||||
|
|
@ -84,7 +89,7 @@ class ModuloAtuador(ModuloDiagnosticoBase):
|
|||
total_ativos += 1
|
||||
saude_total += saude
|
||||
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 bico_id, dados_bico in bicos.items():
|
||||
|
|
@ -198,7 +203,34 @@ class ModuloAtuador(ModuloDiagnosticoBase):
|
|||
motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
|
||||
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
|
||||
if not mod_conectado:
|
||||
|
|
@ -211,9 +243,10 @@ class ModuloAtuador(ModuloDiagnosticoBase):
|
|||
saude_geral = {
|
||||
"conectado": mod_conectado,
|
||||
"status": status.value,
|
||||
"saude": saude_final,
|
||||
"saude": max(0, saude_final),
|
||||
"motivos": motivos_gerais,
|
||||
"saude_individual": saude_individual
|
||||
"saude_individual": saude_individual,
|
||||
"condicoes_operacionais": cond_op_mod
|
||||
}
|
||||
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import math
|
||||
import time
|
||||
from shared.enums import T_Code, StatusModulo
|
||||
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
|
||||
|
|
@ -17,7 +18,8 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
|||
SAUDE_MIN_ALERTA = 80
|
||||
|
||||
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)
|
||||
rtk = modulo.get("rtk", False)
|
||||
hAcc = modulo.get("hAcc", 0)
|
||||
|
|
@ -30,16 +32,26 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
|||
if not conectado:
|
||||
motivos.append("GPS desconectado")
|
||||
saude = 0
|
||||
elif freq_hz <= FREQ_MIN:
|
||||
motivos.append("Sem resposta do GPS")
|
||||
elif freq_pos <= FREQ_MIN:
|
||||
motivos.append("Sem resposta de coordenadas")
|
||||
saude = 0
|
||||
elif freq_ori <= FREQ_MIN:
|
||||
motivos.append("Sem resposta de orientacao")
|
||||
saude = 0
|
||||
else:
|
||||
f = min(freq_hz, FREQ_BASE)
|
||||
erro = (FREQ_BASE - f) / FREQ_BASE
|
||||
p_freq = min(int(erro * 100), 40)
|
||||
saude -= abs(p_freq)
|
||||
if abs(p_freq) > 0:
|
||||
motivos.append(f"Frequência baixa: {f:.2f} Hz (penalidade {p_freq}%)")
|
||||
f_pos = min(freq_pos, FREQ_BASE)
|
||||
erro_pos = (FREQ_BASE - f_pos) / FREQ_BASE
|
||||
p_freq_pos = min(int(erro_pos * 100), 40)
|
||||
saude -= abs(p_freq_pos)
|
||||
if abs(p_freq_pos) > 0:
|
||||
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
|
||||
|
||||
p_fix = self._penalidade_fix(fix)
|
||||
|
|
@ -235,11 +247,7 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
|||
if h < 200: return 20
|
||||
return 30
|
||||
|
||||
def _penalidade_age(self, age) -> int:
|
||||
if age < 0: return 35
|
||||
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
|
||||
def _penalidade_age(self, age: float) -> float:
|
||||
if age < 0:
|
||||
return 35
|
||||
return round(min(35, 35 * (1 - math.exp(-age / 8))), 2)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
|
|||
FREQ_MIN = FREQ_BASE * 0.5
|
||||
SAUDE_MIN_ALERTA = 80
|
||||
|
||||
sensor_bateria_ativado = False
|
||||
|
||||
for tipo, sensores in dados_sen.get("sensores", {}).items():
|
||||
for sensor_id, dados_sensor in sensores.items():
|
||||
saude = 100
|
||||
|
|
@ -38,6 +40,9 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
|
|||
padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
|
||||
conectado = dados_sensor.get("conectado", 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:
|
||||
saude = 0
|
||||
motivos.append(f"desconectado")
|
||||
|
|
@ -138,7 +143,34 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
|
|||
motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
|
||||
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
|
||||
if not mod_conectado:
|
||||
|
|
@ -151,9 +183,10 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
|
|||
saude_geral = {
|
||||
"conectado": mod_conectado,
|
||||
"status": status.value,
|
||||
"saude": saude_final,
|
||||
"saude": max(0, saude_final),
|
||||
"motivos": motivos_gerais,
|
||||
"saude_individual": saude_individual
|
||||
"saude_individual": saude_individual,
|
||||
"condicoes_operacionais": cond_op_mod
|
||||
}
|
||||
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -24,7 +24,7 @@ def main():
|
|||
fila = WorkerFilaMensagens("controle", fila_callback, 1, True, 100)
|
||||
|
||||
loop_fixo = True
|
||||
frequencia_loop = 5 # hz
|
||||
frequencia_loop = 15 # hz
|
||||
|
||||
def loop_ativo():
|
||||
t0 = time.time()
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -7,7 +7,7 @@ from manager_worker.modulos.mpc import get_mpc, comando_parado
|
|||
from manager_worker.modulos.pid import PIDAdaptativo
|
||||
from visual_worker.processamento.costmap_fuser import unpack_snapshot
|
||||
|
||||
def definir_comando(pid: PIDAdaptativo):
|
||||
def definir_comando(pid: PIDAdaptativo, envio_necessario: bool):
|
||||
try:
|
||||
_operacao = ContextoGlobalRedis.get_operacao()
|
||||
_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
|
||||
)
|
||||
|
||||
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 = {
|
||||
"Operacao": {
|
||||
"Status": _operacao.get("status"),
|
||||
|
|
@ -56,16 +63,18 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
"Latitude": _gps.get("lat", 0),
|
||||
"Longitude": _gps.get("lon", 0),
|
||||
"AnguloCarro": _gps.get("theta", 0),
|
||||
"PassosAtraso": _gps_passos_travados
|
||||
"PassosAtraso": _gps_passos_travados,
|
||||
"Timestamp": _gps.get("timestamp", {}).get("posicao", 0.0)
|
||||
},
|
||||
"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),
|
||||
"ManobrandoEntreRuas": _trajetoria.get("manobrando", False),
|
||||
"DentroCorredor": _trajetoria.get("CorredorAtual", {}).get("dentro", False),
|
||||
"DistanciaEsquerda": _trajetoria.get("CorredorAtual", {}).get("dist_esq", 0.0),
|
||||
"DistanciaDireita": _trajetoria.get("CorredorAtual", {}).get("dist_dir", 0.0),
|
||||
"IdxProximoPonto": _trajetoria.get("idx_proximo_ponto", 0),
|
||||
"IdxPontoAlvo": _trajetoria.get("idx_ponto_alvo", 0),
|
||||
"AnguloCaminho": _trajetoria.get("CorredorAtual", {}).get("angulo", 0.0),
|
||||
"ErroAngular": _trajetoria.get("erro_angular", 0),
|
||||
"ErroAngularCaminho": _trajetoria.get("erro_angular_caminho", 0),
|
||||
|
|
@ -93,7 +102,7 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
},
|
||||
"RegrasAtivas": {
|
||||
"matriz_custo": False,
|
||||
"deteccao_obstaculos": True,
|
||||
"deteccao_obstaculos": False,
|
||||
},
|
||||
"Equipamento": {
|
||||
"largura": _equipamento.get("largura", 0.85),
|
||||
|
|
@ -108,8 +117,8 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
if _tipo_controle == TiposControladorDirecional.MPC:
|
||||
comando = _regras_taticas(contexto)
|
||||
|
||||
if (comando["comando_definido"]):
|
||||
return comando["angulo"], comando["tipo"], comando["simulacao"], comando["parada_necessaria"], False
|
||||
if (comando.get("comando_definido", False)):
|
||||
return _montar_comando_retorno(comando, latencia=latencia)
|
||||
else:
|
||||
op_modo = ModoOperacao(_operacao.get("modo", ModoOperacao.NaoDefinido.value))
|
||||
if op_modo == ModoOperacao.MapaGPS:
|
||||
|
|
@ -117,8 +126,8 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
elif op_modo == ModoOperacao.MapeamentoVisual:
|
||||
return _comando_mapeamento_visual(contexto)
|
||||
else:
|
||||
comando = _comando_direcional_parado()
|
||||
return comando["angulo"], comando["tipo"], comando["simulacao"], comando["parada_necessaria"], False
|
||||
comando = _comando_direcional_parado(True)
|
||||
return _montar_comando_retorno(comando, latencia=latencia)
|
||||
|
||||
elif _tipo_controle == TiposControladorDirecional.PID:
|
||||
_controle = ContextoGlobalRedis.get_controle()
|
||||
|
|
@ -145,7 +154,17 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
tipo_movimento = TipoMovimentoDirecional.MovimentoArco if usar_arco else TipoMovimentoDirecional.RodasDianteiras
|
||||
angulo = round(pid.atualizar(erro_angular), 2)
|
||||
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:
|
||||
mostrar_log(f"❌ Erro ao definir comando direcional: {e}")
|
||||
|
|
@ -153,13 +172,17 @@ def definir_comando(pid: PIDAdaptativo):
|
|||
def _comando_direcional_parado(definido: bool):
|
||||
try:
|
||||
angulo, tipo = comando_parado()
|
||||
return {
|
||||
"angulo": angulo,
|
||||
_cmd = {
|
||||
"comando_definido": definido,
|
||||
"enviar_comando": True,
|
||||
"parada_necessaria": True,
|
||||
"erro": False,
|
||||
"latencia": 0.0,
|
||||
"angulo": angulo,
|
||||
"tipo": tipo.value,
|
||||
"simulacao": [],
|
||||
"parada_necessaria": True,
|
||||
"comando_definido": definido
|
||||
"simulacao": []
|
||||
}
|
||||
return _cmd
|
||||
except Exception as 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...")
|
||||
|
||||
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:
|
||||
mostrar_log(f"❌ Erro ao definir comando MapaGPS: {e}")
|
||||
|
||||
|
|
@ -283,16 +306,38 @@ def _comando_mapeamento_visual(contexto):
|
|||
# 🚦 Define se deve parar
|
||||
parada_necessaria = (status_carro == StatusCarroMapa.Parado)
|
||||
|
||||
latencia = time.perf_counter() - contexto.get("GPS", {}).get("Timestamp", 0.0)
|
||||
|
||||
# 🔧 Monta comando final
|
||||
comando = {
|
||||
"angulo": round(angulo, 2),
|
||||
"tipo": tipo_movimento.value,
|
||||
"simulacao": [],
|
||||
"parada_necessaria": parada_necessaria
|
||||
"enviar_comando": True,
|
||||
"parada_necessaria": parada_necessaria,
|
||||
"erro": False,
|
||||
"latencia": latencia,
|
||||
"angulo": round(angulo, 2),
|
||||
"tipo": tipo_movimento.value,
|
||||
"simulacao": []
|
||||
}
|
||||
|
||||
return comando["angulo"], comando["tipo"], comando["simulacao"], comando["parada_necessaria"], False
|
||||
return _montar_comando_retorno(comando)
|
||||
|
||||
except Exception as e:
|
||||
mostrar_log(f"❌ Erro ao definir comando MapeamentoVisual: {e}")
|
||||
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
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
|
|||
_controle = ContextoGlobalRedis.get_controle()
|
||||
pulverizador_automatico = _controle.get("pulverizador_automatico", 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_max = _operacao.get("Mov", {}).get("percent_vel_max", 100)
|
||||
ang_max = _operacao.get("Dir", {}).get("angulo_max", 30)
|
||||
ervas_min_percenet = 15.0
|
||||
|
||||
|
||||
status_carro = StatusCarroMapa.Parado
|
||||
|
|
@ -55,9 +57,9 @@ def definir_comando(parada_necessaria: bool, erro: bool, filtro_vel: FiltroVeloc
|
|||
else:
|
||||
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, ang_max, erro)
|
||||
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:
|
||||
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
|
||||
else:
|
||||
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
|
||||
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)
|
||||
else:
|
||||
velocidade_sp = filtro_vel.filtrar(velocidade_sp)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from copy import deepcopy
|
||||
import heapq
|
||||
import math
|
||||
import time
|
||||
|
|
@ -35,7 +36,7 @@ class ControladorMPC:
|
|||
self.ultima_atualizacao = None
|
||||
self.dt = 0
|
||||
|
||||
print(parametros_mpc)
|
||||
#print(parametros_mpc)
|
||||
self.angulo_max_graus = parametros_mpc.get("angulo_max_graus", 30.0)
|
||||
self.velocidade_min = parametros_mpc.get("velocidade_min", 0.4)
|
||||
self.velocidade_max = parametros_mpc.get("velocidade_max", 1.9)
|
||||
|
|
@ -364,6 +365,153 @@ class ControladorMPC:
|
|||
except Exception as 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
|
||||
|
||||
def compute_receding(self, contexto, comando_anterior):
|
||||
|
|
@ -382,13 +530,13 @@ class ControladorMPC:
|
|||
|
||||
t_exec = max((now() - self.ultima_atualizacao), 1e-3)
|
||||
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
|
||||
|
||||
except Exception as 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
|
||||
t0 = now()
|
||||
|
||||
|
|
@ -404,53 +552,73 @@ class ControladorMPC:
|
|||
return None
|
||||
|
||||
GPS = contexto.get("GPS", {})
|
||||
pos_lat = GPS.get("Latitude", 0)
|
||||
pos_lon = GPS.get("Longitude", 0)
|
||||
pos_theta = GPS.get("AnguloCarro", 0)
|
||||
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")
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
distancia_m = velocidade * self.dt
|
||||
idx_alvo_real = contexto.get("Carro", {}).get("IdxProximoPonto", 0)
|
||||
ultimo_ponto = idx_alvo_real >= len(self.pontos_info)
|
||||
|
||||
ultimo_ponto = idx_proximo_ponto_real >= len(self.pontos_info)
|
||||
self.tempo_execucao_local = contexto.get("Carro", {}).get("TempoEntreComandos", 0.5)
|
||||
if ultimo_ponto:
|
||||
self.passos_horizonte_local = 1
|
||||
self.qtd_comandos_sucessivos = 3
|
||||
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.qtd_comandos_sucessivos = max(1, passos_total // self.passos_horizonte_local)
|
||||
else:
|
||||
self.passos_horizonte_local = 0
|
||||
self.qtd_comandos_sucessivos = 0
|
||||
|
||||
passos_atraso = contexto.get("Carro", {}).get("PassosAtraso", 1)
|
||||
angulo_anterior = np.radians(comando_anterior.get("angulo", 0))
|
||||
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 = []
|
||||
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}")
|
||||
|
||||
|
|
@ -471,6 +639,7 @@ class ControladorMPC:
|
|||
est = max(1.0, getattr(self, "_t_est_passo_ms", 6.0))
|
||||
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):
|
||||
if ms_left() <= 0:
|
||||
break
|
||||
|
|
@ -480,13 +649,18 @@ class ControladorMPC:
|
|||
if exp_budget <= 0 or ms_left() <= 0:
|
||||
break
|
||||
x_atual, y_atual, theta_atual = candidato["x"], candidato["y"], candidato["theta"]
|
||||
visitados = candidato["visitados"].copy()
|
||||
visitados = deepcopy(candidato["visitados"])
|
||||
cmd_anterior = {
|
||||
"tipo": candidato["comandos"][-1][0],
|
||||
"angulo": candidato["comandos"][-1][1],
|
||||
}
|
||||
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)
|
||||
if ms_left() <= 0 or exp_budget <= 0:
|
||||
break
|
||||
|
|
@ -608,11 +782,13 @@ class ControladorMPC:
|
|||
simulacao_latlon = []
|
||||
#print(f"Simulacao: {len(simulacao_latlon)}")
|
||||
_cmd = {
|
||||
"angulo": float(round(np.degrees(angulo_final), 2)),
|
||||
"tipo": tipo_final.value,
|
||||
"simulacao": simulacao_latlon,
|
||||
"parada_necessaria": parada_necessaria,
|
||||
"erro": False
|
||||
"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
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -624,7 +800,7 @@ class ControladorMPC:
|
|||
# reconstroi set de candidatos só pra plot, com orçamento curtinho
|
||||
deadline_dbg = time.perf_counter() + 0.03 # ~30 ms pra não pesar
|
||||
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_ref=(x, y, theta),
|
||||
contexto=contexto,
|
||||
|
|
@ -646,7 +822,310 @@ class ControladorMPC:
|
|||
|
||||
except Exception as 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):
|
||||
limite = 1.0 / 5.0
|
||||
|
|
@ -656,14 +1135,17 @@ class ControladorMPC:
|
|||
mostrar_log(f"🚨 Tempo maximo de execucao excedido: {delta}/{limite}, processo: {processo}")
|
||||
return hot_stop
|
||||
|
||||
def _comando_fallback_hot_stop(self, comando_anterior):
|
||||
return {
|
||||
"angulo": comando_anterior.get("angulo", 0),
|
||||
"tipo": comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value),
|
||||
"simulacao": [],
|
||||
"parada_necessaria": True,
|
||||
"erro": True
|
||||
def _comando_fallback_hot_stop(self, comando_anterior, latencia=0):
|
||||
_cmd = {
|
||||
"enviar_comando": True,
|
||||
"parada_necessaria": True,
|
||||
"erro": True,
|
||||
"latencia": latencia,
|
||||
"angulo": comando_anterior.get("angulo", 0),
|
||||
"tipo": comando_anterior.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value),
|
||||
"simulacao": []
|
||||
}
|
||||
return _cmd
|
||||
|
||||
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
|
||||
|
||||
# 1) Ângulo ideal (wrap [-pi,pi] e clamp ao máx)
|
||||
orient = self.gps_handler.calcular_orientacao(ponto_alvo, ponto_atual)
|
||||
delta_theta = (orient - ponto_atual[2] + np.pi) % (2 * np.pi) - np.pi
|
||||
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, 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)
|
||||
angulo_ideal_rad = float(np.clip(delta_theta, -ang_max_rad, ang_max_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
|
||||
CARRO = contexto.get("Carro", {})
|
||||
|
|
@ -1410,87 +1907,119 @@ class ControladorMPC:
|
|||
mostrar_log(f"❌ Erro ao filtrar por margem angular: {e}")
|
||||
return candidatos
|
||||
|
||||
def _simular_passo(self, x, y, theta, tipo, angulo_testado, custos_candidatos, comando_anterior, contexto, visitados):
|
||||
# Custo pode variar de 0 ~ 13, em situacoes mais extremas, por passo
|
||||
def _simular_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:
|
||||
custo_total = 0.0
|
||||
x_sim, y_sim, theta_sim = x, y, theta
|
||||
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))
|
||||
custo_visual_worker = float(custos_candidatos.get(chave, 0.0))
|
||||
|
||||
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)
|
||||
pontos_visitados = visitados.copy()
|
||||
velocidade = contexto.get("Carro", {}).get("Velocidade", 0.0)
|
||||
pontos_visitados = deepcopy(visitados)
|
||||
|
||||
# Contexto usado no custo
|
||||
angulo_caminho = np.radians(contexto.get("Carro", {}).get("AnguloCaminho", 0.0))
|
||||
status_carro = StatusCarroMapa(contexto.get("Carro", {}).get("Status", StatusCarroMapa.Parado.value))
|
||||
dentro_corredor = 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)
|
||||
dentro_corredor = bool(contexto.get("Carro", {}).get("DentroCorredor", False))
|
||||
|
||||
for passo in range(self.passos_horizonte_local):
|
||||
try:
|
||||
#old_theta = theta_sim
|
||||
x_sim, y_sim, theta_sim = self._nova_posicao(x_sim, y_sim, theta_sim, omega, velocidade, tipo, angulo_testado)
|
||||
# omega e sub-stepping
|
||||
omega_const = self.gps_handler.calcular_omega(v_planejado, angulo_testado, tipo)
|
||||
|
||||
simulacoes.append((x_sim, y_sim, theta_sim))
|
||||
# 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
|
||||
|
||||
idx_alvo_sim = self._corrigir_pontos_visitados(x_sim, y_sim, pontos_visitados)
|
||||
ponto_alvo_sim = self.pontos_info[idx_alvo_sim]["xy"]
|
||||
orient_sim = self.gps_handler.calcular_orientacao((x_sim, y_sim), ponto_alvo_sim)
|
||||
orient_sim = (orient_sim + np.pi) % (2 * np.pi)
|
||||
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
|
||||
|
||||
erro_pos = 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)
|
||||
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
|
||||
)
|
||||
|
||||
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]
|
||||
simulacoes.append((x_sim, y_sim, theta_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_movel = np.array([np.cos(theta_sim), np.sin(theta_sim)])
|
||||
cos_angulo = -np.dot(vetor_movel, vetor_alvo_norm)
|
||||
delta_angulo = abs(angulo_testado - prev_ang)
|
||||
# alvo mais próximo após avançar
|
||||
idx_alvo_sim = self._corrigir_pontos_visitados(x_sim, y_sim, pontos_visitados)
|
||||
ponto_alvo_sim = self.pontos_info[idx_alvo_sim]["xy"]
|
||||
|
||||
custo_pos = erro_pos * peso_erro_pos
|
||||
custo_ori = ((erro_ori_sim / np.pi) * erro_pos) * peso_erro_ori
|
||||
custo_suavidade = ((delta_angulo / np.pi) * erro_pos) * peso_suavidade
|
||||
custo_tipo_movimento = peso_movimento[tipo]
|
||||
erro_re = (1.0 - cos_angulo) if cos_angulo > 0 else cos_angulo
|
||||
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)
|
||||
# orientação do caminho + erro angular combinado
|
||||
orient_sim = self.gps_handler.calcular_orientacao((x_sim, y_sim), ponto_alvo_sim)
|
||||
orient_sim = (orient_sim + np.pi) % (2 * np.pi)
|
||||
|
||||
custo_total += custo_mapa
|
||||
# erros
|
||||
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)
|
||||
|
||||
prev_ang = angulo_testado
|
||||
prev_tipo = tipo
|
||||
# pesos dinâmicos
|
||||
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]
|
||||
|
||||
#if matriz_concluida:
|
||||
# break
|
||||
# ângulo relativo ao alvo e suavidade
|
||||
vetor_alvo = np.array(ponto_alvo_sim) - np.array([x_sim, y_sim])
|
||||
vetor_alvo_norm = vetor_alvo / (np.linalg.norm(vetor_alvo) + 1e-9)
|
||||
vetor_movel = np.array([np.cos(theta_sim), np.sin(theta_sim)])
|
||||
cos_angulo = -float(np.dot(vetor_movel, vetor_alvo_norm))
|
||||
delta_angulo = abs(angulo_testado - prev_ang)
|
||||
|
||||
#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}")
|
||||
# componentes de custo
|
||||
custo_pos = erro_pos * peso_erro_pos
|
||||
custo_ori = ((erro_ori_sim / np.pi) * erro_pos) * peso_erro_ori
|
||||
custo_suavidade = ((delta_angulo / np.pi) * erro_pos) * peso_suavidade
|
||||
custo_tipo_movimento = float(peso_movimento[tipo])
|
||||
erro_re = (1.0 - cos_angulo) if cos_angulo > 0 else cos_angulo
|
||||
custo_re = -erro_re * erro_pos * peso_fator_re
|
||||
|
||||
except Exception as e:
|
||||
mostrar_log(f"❌ Erro ao simular passo {passo}, {tipo.name}, {np.degrees(angulo_testado):.2f}: {e}")
|
||||
# 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_tipo = tipo
|
||||
|
||||
return float(custo_total), simulacoes, True, pontos_visitados
|
||||
|
||||
return custo_total, simulacoes, True, pontos_visitados
|
||||
except Exception as 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):
|
||||
distancia_m = velocidade * self.dt
|
||||
def _nova_posicao(self, x, y, theta, omega, velocidade, tipo, angulo_rad, dt=None):
|
||||
"""
|
||||
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)
|
||||
y_sim = y + (np.cos(theta_sim) * distancia_m)
|
||||
|
||||
return x_sim, y_sim, theta_sim
|
||||
|
||||
|
||||
def _prefiltrar_angulos_por_matriz_quick(
|
||||
self,
|
||||
angulos_deg, # iterable de graus
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import time
|
||||
from shared.contexto_global_redis import ContextoGlobalRedis
|
||||
from manager_worker.modulos.pid import PIDAdaptativo
|
||||
from .base import ProcessadorBase
|
||||
|
|
@ -20,18 +21,25 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
saida_min=-ang_max,
|
||||
saida_max=ang_max
|
||||
)
|
||||
self.ultimo_comando_enviado = time.time()
|
||||
|
||||
|
||||
def processar(self):
|
||||
try:
|
||||
angulo_sp, tipo_dir, simulacao, parada_necessaria, erro = definir_comando_dir(self.pid_dir)
|
||||
velocidade_sp = definir_comando_mov(parada_necessaria, erro, self.filtro_mov)
|
||||
return comando_controle(
|
||||
percentual_velocidade=velocidade_sp,
|
||||
angulo=angulo_sp,
|
||||
tipo_movimento=tipo_dir,
|
||||
simulacao=simulacao,
|
||||
erro=erro
|
||||
)
|
||||
agora = time.time()
|
||||
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(
|
||||
percentual_velocidade=velocidade_sp,
|
||||
angulo=comando_dir.get("angulo"),
|
||||
tipo_movimento=comando_dir.get("tipo"),
|
||||
simulacao=comando_dir.get("simulacao"),
|
||||
erro=comando_dir.get("erro", False),
|
||||
latencia=comando_dir.get("latencia", 0.0)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Erro no processador EmAndamento: {e}")
|
||||
return None
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -3,7 +3,7 @@ from shared.enums import ModoOperacao, StatusOperacao
|
|||
from manager_worker.config import mostrar_log
|
||||
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)
|
||||
if erro == False:
|
||||
try:
|
||||
|
|
@ -18,14 +18,16 @@ def comando_controle(percentual_velocidade: float, angulo: float, tipo_movimento
|
|||
tipo_movimento_direcional=tipo_movimento,
|
||||
velocidade_sp=percentual_velocidade,
|
||||
simulacao=simulacao,
|
||||
heartbeat=hb_atual
|
||||
heartbeat=hb_atual,
|
||||
latencia=latencia
|
||||
)
|
||||
return {
|
||||
"velocidade_sp": percentual_velocidade,
|
||||
"angulo_sp": angulo,
|
||||
"tipo_movimento_direcional": tipo_movimento,
|
||||
"simulacao": simulacao,
|
||||
"heartbeat": hb_atual
|
||||
"heartbeat": hb_atual,
|
||||
"latencia": latencia
|
||||
}
|
||||
|
||||
def comando_parado():
|
||||
|
|
|
|||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue