robustez contra first, ajuste no fps da camera traseira, ajuste no imu traseiro, reinciar mesma operacao,
This commit is contained in:
parent
b148d987b7
commit
6ca8aa5985
|
|
@ -679,7 +679,8 @@ namespace AgroBase.Forms.Operacoes
|
||||||
status: comando
|
status: comando
|
||||||
);
|
);
|
||||||
|
|
||||||
pnlAtuadores.Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: " + (comando ? "Ligado" : "Desligado");
|
var lbl = pnlAtuadores.Controls.OfType<Label>().FirstOrDefault(x => x.Name == "lblCmd_" + ID);
|
||||||
|
if (lbl != null) lbl.Text = "Comando: " + (comando ? "Ligado" : "Desligado");
|
||||||
btn.Text = comando ? "Desligar" : "Ligar";
|
btn.Text = comando ? "Desligar" : "Ligar";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1516,7 +1516,8 @@ namespace AgroBase.Forms.Operacoes
|
||||||
lblMagnetometro.ForeColor = !LogImu.Iniciado ? Color.Red : Color.Black;
|
lblMagnetometro.ForeColor = !LogImu.Iniciado ? Color.Red : Color.Black;
|
||||||
pnlOrientacaoMagnetometro.Invalidate();
|
pnlOrientacaoMagnetometro.Invalidate();
|
||||||
pnlInclinacao.Invalidate();
|
pnlInclinacao.Invalidate();
|
||||||
visualizador3D.AtualizarAngulos(LogImu.PitchSeguro, LogImu.RollSeguro, -LogsGPS[idxMomentoAtual].OrientacaoReal);
|
double yaw = -LogsGPS[idxMomentoAtual].OrientacaoReal;
|
||||||
|
visualizador3D.AtualizarAngulos(Lateral: LogImu.RollSeguro, Frontal: LogImu.PitchSeguro, Rotacao: LogImu.YawSeguro);
|
||||||
//txtTemperatura.Text = LogImu.Temperatura.ToString("0.00");
|
//txtTemperatura.Text = LogImu.Temperatura.ToString("0.00");
|
||||||
//txtAltitude.Text = LogImu.Altitude.ToString("0.00");
|
//txtAltitude.Text = LogImu.Altitude.ToString("0.00");
|
||||||
//txtPressao.Text = LogImu.Pressao + " Pa (" + (LogImu.Pressao / 101300.0).ToString("0.00") + " atm)";
|
//txtPressao.Text = LogImu.Pressao + " Pa (" + (LogImu.Pressao / 101300.0).ToString("0.00") + " atm)";
|
||||||
|
|
@ -1921,62 +1922,128 @@ namespace AgroBase.Forms.Operacoes
|
||||||
private void pnlInclinacao_Paint(object sender, PaintEventArgs e)
|
private void pnlInclinacao_Paint(object sender, PaintEventArgs e)
|
||||||
{
|
{
|
||||||
Graphics g = e.Graphics;
|
Graphics g = e.Graphics;
|
||||||
int centerX = pnlInclinacao.Width / 2;
|
|
||||||
int centerY = pnlInclinacao.Height / 2;
|
|
||||||
int radius = 10;
|
|
||||||
|
|
||||||
// Desenhar fundo com bordas arredondadas
|
|
||||||
GraphicsPath path = new GraphicsPath();
|
|
||||||
int cornerRadius = 20;
|
|
||||||
path.AddArc(0, 0, cornerRadius, cornerRadius, 180, 90);
|
|
||||||
path.AddArc(pnlInclinacao.Width - cornerRadius, 0, cornerRadius, cornerRadius, 270, 90);
|
|
||||||
path.AddArc(pnlInclinacao.Width - cornerRadius, pnlInclinacao.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90);
|
|
||||||
path.AddArc(0, pnlInclinacao.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
|
|
||||||
path.CloseAllFigures();
|
|
||||||
|
|
||||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
g.FillPath(new SolidBrush(Color.LightBlue), path);
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
|
||||||
g.DrawPath(new Pen(Color.DarkBlue, 2), path);
|
|
||||||
|
|
||||||
// Desenhar linhas de referência
|
int largura = pnlInclinacao.ClientSize.Width;
|
||||||
Pen pen = new Pen(Color.Gray, 1);
|
int altura = pnlInclinacao.ClientSize.Height;
|
||||||
g.DrawLine(pen, centerX, 0, centerX, pnlInclinacao.Height);
|
|
||||||
g.DrawLine(pen, 0, centerY, pnlInclinacao.Width, centerY);
|
|
||||||
|
|
||||||
// Desenhar linhas adicionais para ângulos
|
if (largura <= 0 || altura <= 0)
|
||||||
for (int i = -90; i <= 90; i += 15)
|
return;
|
||||||
|
|
||||||
|
float centerX = largura / 2f;
|
||||||
|
float centerY = altura / 2f;
|
||||||
|
|
||||||
|
float radius = 10f;
|
||||||
|
float margemX = radius + 5f;
|
||||||
|
float margemY = radius + 5f;
|
||||||
|
|
||||||
|
// X = lateral = Roll
|
||||||
|
double limiteLateral = Math.Max(1.0, Math.Abs(VariaveisEquipamento.AnguloInclinacaoRollMax));
|
||||||
|
// Y = frontal = Pitch
|
||||||
|
double limiteFrontal = Math.Max(1.0, Math.Abs(VariaveisEquipamento.AnguloInclinacaoPitchMax));
|
||||||
|
float escalaX = (largura / 2f - margemX) / (float)limiteLateral;
|
||||||
|
float escalaY = (altura / 2f - margemY) / (float)limiteFrontal;
|
||||||
|
|
||||||
|
GraphicsPath path = CriarRetanguloArredondado(new RectangleF(1, 1, largura - 2, altura - 2), 20f);
|
||||||
|
|
||||||
|
SolidBrush fundoBrush = new SolidBrush(Color.LightBlue);
|
||||||
|
Pen bordaPen = new Pen(Color.DarkBlue, 2f);
|
||||||
|
Pen eixoPen = new Pen(Color.Gray, 1f);
|
||||||
|
Pen gradePen = new Pen(Color.FromArgb(100, Color.Gray), 1f);
|
||||||
|
SolidBrush pontoBrush = new SolidBrush(Color.Red);
|
||||||
|
Font fonte = new Font("Arial", 8f);
|
||||||
|
StringFormat textoCentralizado = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||||
|
|
||||||
|
// Fundo
|
||||||
|
g.FillPath(fundoBrush, path);
|
||||||
|
g.DrawPath(bordaPen, path);
|
||||||
|
|
||||||
|
// Evita que desenhos ultrapassem o painel arredondado
|
||||||
|
Region estadoClip = g.Clip;
|
||||||
|
g.SetClip(path);
|
||||||
|
|
||||||
|
// Eixos centrais
|
||||||
|
g.DrawLine(eixoPen, centerX, margemY, centerX, altura - margemY);
|
||||||
|
g.DrawLine(eixoPen, margemX, centerY, largura - margemX, centerY);
|
||||||
|
|
||||||
|
const int quantidadeDivisoes = 4;
|
||||||
|
|
||||||
|
// Régua horizontal: Roll / lateral
|
||||||
|
for (int i = -quantidadeDivisoes; i <= quantidadeDivisoes; i++)
|
||||||
{
|
{
|
||||||
int posX = centerX + (int)(i * (pnlInclinacao.Width / 2 - radius) / 90);
|
double angulo = limiteLateral * i / quantidadeDivisoes;
|
||||||
int posY = centerY - (int)(i * (pnlInclinacao.Height / 2 - radius) / 90);
|
float posX = centerX + (float)angulo * escalaX;
|
||||||
g.DrawLine(pen, centerX, centerY, posX, centerY);
|
|
||||||
g.DrawLine(pen, centerX, centerY, centerX, posY);
|
g.DrawLine(gradePen, posX, margemY, posX, altura - margemY);
|
||||||
|
g.DrawLine(eixoPen, posX, centerY - 5f, posX, centerY + 5f);
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
RectangleF areaTexto = new RectangleF(posX - 25f, centerY + 7f, 50f, 18f);
|
||||||
|
g.DrawString($"{angulo:0.#}°", fonte, Brushes.Black, areaTexto, textoCentralizado);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Desenhar régua na linha central
|
// Régua vertical: Pitch / frontal
|
||||||
for (int i = -90; i <= 90; i += 15)
|
for (int i = -quantidadeDivisoes; i <= quantidadeDivisoes; i++)
|
||||||
{
|
{
|
||||||
int posX = centerX + (int)(i * (pnlInclinacao.Width / 2 - radius) / 90);
|
double angulo = limiteFrontal * i / quantidadeDivisoes;
|
||||||
int posY = centerY - (int)(i * (pnlInclinacao.Height / 2 - radius) / 90);
|
float posY = centerY - (float)angulo * escalaY;
|
||||||
|
|
||||||
// Marcas horizontais
|
g.DrawLine(gradePen, margemX, posY, largura - margemX, posY);
|
||||||
g.DrawLine(pen, posX, centerY - 5, posX, centerY + 5);
|
g.DrawLine(eixoPen, centerX - 5f, posY, centerX + 5f, posY);
|
||||||
g.DrawString(i.ToString(), new Font("Arial", 8), Brushes.Black, posX - 10, centerY + 10);
|
|
||||||
|
|
||||||
// Marcas verticais
|
if (i != 0)
|
||||||
g.DrawLine(pen, centerX - 5, posY, centerX + 5, posY);
|
{
|
||||||
g.DrawString(i.ToString(), new Font("Arial", 8), Brushes.Black, centerX + 10, posY - 5);
|
RectangleF areaTexto = new RectangleF(centerX + 7f, posY - 9f, 50f, 18f);
|
||||||
|
g.DrawString($"{angulo:0.#}°", fonte, Brushes.Black, areaTexto, textoCentralizado);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular a posição do círculo com base nos ângulos
|
|
||||||
var Log = LogsIMU[idxMomentoAtual];
|
var Log = LogsIMU[idxMomentoAtual];
|
||||||
int circlePosX = centerX + (int)((Log?.RollSeguro ?? 0) * (pnlInclinacao.Width / 2 - radius) / 90);
|
// X horizontal = lateral / Roll
|
||||||
int circlePosY = centerY - (int)((Log?.PitchSeguro ?? 0) * (pnlInclinacao.Height / 2 - radius) / 90);
|
double anguloLateralExibido = FuncoesMatematicas.Clamp(Log.RollSeguro, -limiteLateral, limiteLateral);
|
||||||
|
|
||||||
// Desenhar o círculo
|
// Y vertical = frontal / Pitch
|
||||||
Brush brush = new SolidBrush(Color.Red);
|
double anguloFrontalExibido = FuncoesMatematicas.Clamp(-Log.PitchSeguro, -limiteFrontal, limiteFrontal);
|
||||||
g.FillEllipse(brush, circlePosX - radius, circlePosY - radius, radius * 2, radius * 2);
|
|
||||||
|
float circlePosX = centerX + (float)anguloLateralExibido * escalaX;
|
||||||
|
float circlePosY = centerY + (float)anguloFrontalExibido * escalaY;
|
||||||
|
|
||||||
|
g.FillEllipse(pontoBrush, circlePosX - radius, circlePosY - radius, radius * 2f, radius * 2f);
|
||||||
|
|
||||||
|
// Restaura o recorte
|
||||||
|
g.Clip = estadoClip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static GraphicsPath CriarRetanguloArredondado(RectangleF retangulo, float raio)
|
||||||
|
{
|
||||||
|
GraphicsPath path = new GraphicsPath();
|
||||||
|
|
||||||
|
float diametro = Math.Min(raio, Math.Min(retangulo.Width, retangulo.Height));
|
||||||
|
|
||||||
|
RectangleF arco = new RectangleF(retangulo.X, retangulo.Y, diametro, diametro);
|
||||||
|
|
||||||
|
path.AddArc(arco, 180, 90);
|
||||||
|
|
||||||
|
arco.X = retangulo.Right - diametro;
|
||||||
|
path.AddArc(arco, 270, 90);
|
||||||
|
|
||||||
|
arco.Y = retangulo.Bottom - diametro;
|
||||||
|
path.AddArc(arco, 0, 90);
|
||||||
|
|
||||||
|
arco.X = retangulo.Left;
|
||||||
|
path.AddArc(arco, 90, 90);
|
||||||
|
|
||||||
|
path.CloseFigure();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void pnlCaminho_Paint(object sender, PaintEventArgs e)
|
private void pnlCaminho_Paint(object sender, PaintEventArgs e)
|
||||||
{
|
{
|
||||||
if (!chbDesenharCorredor.Checked) return;
|
if (!chbDesenharCorredor.Checked) return;
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ namespace AgroBase.Forms
|
||||||
|
|
||||||
double anguloControle = 0.0;
|
double anguloControle = 0.0;
|
||||||
TipoMovimentoDirecional tipoMovimento = Variaveis.OperacaoEmAndamento.Controle.TipoMovimento;
|
TipoMovimentoDirecional tipoMovimento = Variaveis.OperacaoEmAndamento.Controle.TipoMovimento;
|
||||||
double veloccidade = (Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.StatusOperacaoAtual == StatusOperacao.Concluido || Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.StatusOperacaoAtual == StatusOperacao.Aguardando) ? 0 : velocidadeCarroMs;
|
double veloccidade = (new List<StatusOperacao>() { StatusOperacao.Concluido, StatusOperacao.Aguardando, StatusOperacao.Parametrizando }.Contains(Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.StatusOperacaoAtual)) ? 0 : velocidadeCarroMs;
|
||||||
|
|
||||||
var tipoControle = Variaveis.OperacaoEmAndamento.Parametros.Controle.DirTipoMovimento;
|
var tipoControle = Variaveis.OperacaoEmAndamento.Parametros.Controle.DirTipoMovimento;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,10 @@ namespace AgroBase.Forms
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Chart chart = (Chart)(pnlFundo.Controls.Find("chart" + Modulo.Modulo_ID, false).First());
|
Chart chart = (Chart)(pnlFundo.Controls.Find("chart" + Modulo.Modulo_ID, false)?.FirstOrDefault());
|
||||||
|
if (chart == null)
|
||||||
|
return;
|
||||||
|
|
||||||
chart.Titles.Clear();
|
chart.Titles.Clear();
|
||||||
chart.Titles.Add(Modulo.Modulo_ID);
|
chart.Titles.Add(Modulo.Modulo_ID);
|
||||||
var Logs = (Modulo.MovMotor.LogGrafico.Count > MovimentacaoUnificadaModel.LogsManter ? Modulo.MovMotor.LogGrafico.Skip(Math.Max(0, Modulo.MovMotor.LogGrafico.Count - MovimentacaoUnificadaModel.LogsManter)).Take(MovimentacaoUnificadaModel.LogsManter).ToList() : Modulo.MovMotor.LogGrafico).Where(log => log.Modulo_ID == Modulo.Modulo_ID).ToList();
|
var Logs = (Modulo.MovMotor.LogGrafico.Count > MovimentacaoUnificadaModel.LogsManter ? Modulo.MovMotor.LogGrafico.Skip(Math.Max(0, Modulo.MovMotor.LogGrafico.Count - MovimentacaoUnificadaModel.LogsManter)).Take(MovimentacaoUnificadaModel.LogsManter).ToList() : Modulo.MovMotor.LogGrafico).Where(log => log.Modulo_ID == Modulo.Modulo_ID).ToList();
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,9 @@
|
||||||
this.label1.Location = new System.Drawing.Point(692, 397);
|
this.label1.Location = new System.Drawing.Point(692, 397);
|
||||||
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.label1.Name = "label1";
|
this.label1.Name = "label1";
|
||||||
this.label1.Size = new System.Drawing.Size(27, 26);
|
this.label1.Size = new System.Drawing.Size(79, 26);
|
||||||
this.label1.TabIndex = 51;
|
this.label1.TabIndex = 51;
|
||||||
this.label1.Text = "X";
|
this.label1.Text = "Frontal";
|
||||||
//
|
//
|
||||||
// label2
|
// label2
|
||||||
//
|
//
|
||||||
|
|
@ -127,9 +127,9 @@
|
||||||
this.label2.Location = new System.Drawing.Point(692, 468);
|
this.label2.Location = new System.Drawing.Point(692, 468);
|
||||||
this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.label2.Name = "label2";
|
this.label2.Name = "label2";
|
||||||
this.label2.Size = new System.Drawing.Size(28, 26);
|
this.label2.Size = new System.Drawing.Size(78, 26);
|
||||||
this.label2.TabIndex = 53;
|
this.label2.TabIndex = 53;
|
||||||
this.label2.Text = "Y";
|
this.label2.Text = "Lateral";
|
||||||
//
|
//
|
||||||
// txtY
|
// txtY
|
||||||
//
|
//
|
||||||
|
|
@ -151,9 +151,9 @@
|
||||||
this.label3.Location = new System.Drawing.Point(690, 539);
|
this.label3.Location = new System.Drawing.Point(690, 539);
|
||||||
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.label3.Name = "label3";
|
this.label3.Name = "label3";
|
||||||
this.label3.Size = new System.Drawing.Size(25, 26);
|
this.label3.Size = new System.Drawing.Size(93, 26);
|
||||||
this.label3.TabIndex = 55;
|
this.label3.TabIndex = 55;
|
||||||
this.label3.Text = "Z";
|
this.label3.Text = "Rotação";
|
||||||
//
|
//
|
||||||
// txtZ
|
// txtZ
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ namespace AgroBase.Forms
|
||||||
picBussola.Invalidate();
|
picBussola.Invalidate();
|
||||||
//glControl.Invalidate();
|
//glControl.Invalidate();
|
||||||
pnlInclinacao.Invalidate();
|
pnlInclinacao.Invalidate();
|
||||||
visualizador3D.AtualizarAngulos(AnguloX, AnguloY, AnguloZ);
|
visualizador3D.AtualizarAngulos(Frontal: AnguloX, Lateral: AnguloY, Rotacao: AnguloZ);
|
||||||
|
|
||||||
txtX.Text = AnguloX.ToString("0.00");
|
txtX.Text = AnguloX.ToString("0.00");
|
||||||
txtY.Text = AnguloY.ToString("0.00");
|
txtY.Text = AnguloY.ToString("0.00");
|
||||||
|
|
@ -324,59 +324,123 @@ namespace AgroBase.Forms
|
||||||
private void pnlInclinacao_Paint(object sender, PaintEventArgs e)
|
private void pnlInclinacao_Paint(object sender, PaintEventArgs e)
|
||||||
{
|
{
|
||||||
Graphics g = e.Graphics;
|
Graphics g = e.Graphics;
|
||||||
int centerX = pnlInclinacao.Width / 2;
|
|
||||||
int centerY = pnlInclinacao.Height / 2;
|
|
||||||
int radius = 10;
|
|
||||||
|
|
||||||
// Desenhar fundo com bordas arredondadas
|
|
||||||
GraphicsPath path = new GraphicsPath();
|
|
||||||
int cornerRadius = 20;
|
|
||||||
path.AddArc(0, 0, cornerRadius, cornerRadius, 180, 90);
|
|
||||||
path.AddArc(pnlInclinacao.Width - cornerRadius, 0, cornerRadius, cornerRadius, 270, 90);
|
|
||||||
path.AddArc(pnlInclinacao.Width - cornerRadius, pnlInclinacao.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90);
|
|
||||||
path.AddArc(0, pnlInclinacao.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
|
|
||||||
path.CloseAllFigures();
|
|
||||||
|
|
||||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
g.FillPath(new SolidBrush(Color.LightBlue), path);
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
|
||||||
g.DrawPath(new Pen(Color.DarkBlue, 2), path);
|
|
||||||
|
|
||||||
// Desenhar linhas de referência
|
int largura = pnlInclinacao.ClientSize.Width;
|
||||||
Pen pen = new Pen(Color.Gray, 1);
|
int altura = pnlInclinacao.ClientSize.Height;
|
||||||
g.DrawLine(pen, centerX, 0, centerX, pnlInclinacao.Height);
|
|
||||||
g.DrawLine(pen, 0, centerY, pnlInclinacao.Width, centerY);
|
|
||||||
|
|
||||||
// Desenhar linhas adicionais para ângulos
|
if (largura <= 0 || altura <= 0)
|
||||||
for (int i = -90; i <= 90; i += 15)
|
return;
|
||||||
|
|
||||||
|
float centerX = largura / 2f;
|
||||||
|
float centerY = altura / 2f;
|
||||||
|
|
||||||
|
float radius = 10f;
|
||||||
|
float margemX = radius + 5f;
|
||||||
|
float margemY = radius + 5f;
|
||||||
|
|
||||||
|
// X = lateral = Roll
|
||||||
|
double limiteLateral = Math.Max(1.0, Math.Abs(VariaveisEquipamento.AnguloInclinacaoRollMax));
|
||||||
|
// Y = frontal = Pitch
|
||||||
|
double limiteFrontal = Math.Max(1.0, Math.Abs(VariaveisEquipamento.AnguloInclinacaoPitchMax));
|
||||||
|
float escalaX = (largura / 2f - margemX) / (float)limiteLateral;
|
||||||
|
float escalaY = (altura / 2f - margemY) / (float)limiteFrontal;
|
||||||
|
|
||||||
|
GraphicsPath path = CriarRetanguloArredondado(new RectangleF(1, 1, largura - 2, altura - 2), 20f);
|
||||||
|
|
||||||
|
SolidBrush fundoBrush = new SolidBrush(Color.LightBlue);
|
||||||
|
Pen bordaPen = new Pen(Color.DarkBlue, 2f);
|
||||||
|
Pen eixoPen = new Pen(Color.Gray, 1f);
|
||||||
|
Pen gradePen = new Pen(Color.FromArgb(100, Color.Gray), 1f);
|
||||||
|
SolidBrush pontoBrush = new SolidBrush(Color.Red);
|
||||||
|
Font fonte = new Font("Arial", 8f);
|
||||||
|
StringFormat textoCentralizado = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||||
|
|
||||||
|
// Fundo
|
||||||
|
g.FillPath(fundoBrush, path);
|
||||||
|
g.DrawPath(bordaPen, path);
|
||||||
|
|
||||||
|
// Evita que desenhos ultrapassem o painel arredondado
|
||||||
|
Region estadoClip = g.Clip;
|
||||||
|
g.SetClip(path);
|
||||||
|
|
||||||
|
// Eixos centrais
|
||||||
|
g.DrawLine(eixoPen, centerX, margemY, centerX, altura - margemY);
|
||||||
|
g.DrawLine(eixoPen, margemX, centerY, largura - margemX, centerY);
|
||||||
|
|
||||||
|
const int quantidadeDivisoes = 4;
|
||||||
|
|
||||||
|
// Régua horizontal: Roll / lateral
|
||||||
|
for (int i = -quantidadeDivisoes; i <= quantidadeDivisoes; i++)
|
||||||
{
|
{
|
||||||
int posX = centerX + (int)(i * (pnlInclinacao.Width / 2 - radius) / 90);
|
double angulo = limiteLateral * i / quantidadeDivisoes;
|
||||||
int posY = centerY - (int)(i * (pnlInclinacao.Height / 2 - radius) / 90);
|
float posX = centerX + (float)angulo * escalaX;
|
||||||
g.DrawLine(pen, centerX, centerY, posX, centerY);
|
|
||||||
g.DrawLine(pen, centerX, centerY, centerX, posY);
|
g.DrawLine(gradePen, posX, margemY, posX, altura - margemY);
|
||||||
|
g.DrawLine(eixoPen, posX, centerY - 5f, posX, centerY + 5f);
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
RectangleF areaTexto = new RectangleF(posX - 25f, centerY + 7f, 50f, 18f);
|
||||||
|
g.DrawString($"{angulo:0.#}°", fonte, Brushes.Black, areaTexto, textoCentralizado);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Desenhar régua na linha central
|
// Régua vertical: Pitch / frontal
|
||||||
for (int i = -90; i <= 90; i += 15)
|
for (int i = -quantidadeDivisoes; i <= quantidadeDivisoes; i++)
|
||||||
{
|
{
|
||||||
int posX = centerX + (int)(i * (pnlInclinacao.Width / 2 - radius) / 90);
|
double angulo = limiteFrontal * i / quantidadeDivisoes;
|
||||||
int posY = centerY - (int)(i * (pnlInclinacao.Height / 2 - radius) / 90);
|
float posY = centerY - (float)angulo * escalaY;
|
||||||
|
|
||||||
// Marcas horizontais
|
g.DrawLine(gradePen, margemX, posY, largura - margemX, posY);
|
||||||
g.DrawLine(pen, posX, centerY - 5, posX, centerY + 5);
|
g.DrawLine(eixoPen, centerX - 5f, posY, centerX + 5f, posY);
|
||||||
g.DrawString(i.ToString(), new Font("Arial", 8), Brushes.Black, posX - 10, centerY + 10);
|
|
||||||
|
|
||||||
// Marcas verticais
|
if (i != 0)
|
||||||
g.DrawLine(pen, centerX - 5, posY, centerX + 5, posY);
|
{
|
||||||
g.DrawString(i.ToString(), new Font("Arial", 8), Brushes.Black, centerX + 10, posY - 5);
|
RectangleF areaTexto = new RectangleF(centerX + 7f, posY - 9f, 50f, 18f);
|
||||||
|
g.DrawString($"{angulo:0.#}°", fonte, Brushes.Black, areaTexto, textoCentralizado);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular a posição do círculo com base nos ângulos
|
// X horizontal = lateral / Roll
|
||||||
int circlePosX = centerX + (int)(AnguloX * (pnlInclinacao.Width / 2 - radius) / 90);
|
double anguloLateralExibido = FuncoesMatematicas.Clamp(AnguloY, -limiteLateral, limiteLateral);
|
||||||
int circlePosY = centerY - (int)(AnguloY * (pnlInclinacao.Height / 2 - radius) / 90);
|
|
||||||
|
|
||||||
// Desenhar o círculo
|
// Y vertical = frontal / Pitch
|
||||||
Brush brush = new SolidBrush(Color.Red);
|
double anguloFrontalExibido = FuncoesMatematicas.Clamp(AnguloX, -limiteFrontal, limiteFrontal);
|
||||||
g.FillEllipse(brush, circlePosX - radius, circlePosY - radius, radius * 2, radius * 2);
|
|
||||||
|
float circlePosX = centerX + (float)anguloLateralExibido * escalaX;
|
||||||
|
float circlePosY = centerY + (float)anguloFrontalExibido * escalaY;
|
||||||
|
|
||||||
|
g.FillEllipse(pontoBrush, circlePosX - radius, circlePosY - radius, radius * 2f, radius * 2f);
|
||||||
|
|
||||||
|
// Restaura o recorte
|
||||||
|
g.Clip = estadoClip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GraphicsPath CriarRetanguloArredondado(RectangleF retangulo, float raio)
|
||||||
|
{
|
||||||
|
GraphicsPath path = new GraphicsPath();
|
||||||
|
|
||||||
|
float diametro = Math.Min(raio, Math.Min(retangulo.Width, retangulo.Height));
|
||||||
|
|
||||||
|
RectangleF arco = new RectangleF(retangulo.X, retangulo.Y, diametro, diametro);
|
||||||
|
|
||||||
|
path.AddArc(arco, 180, 90);
|
||||||
|
|
||||||
|
arco.X = retangulo.Right - diametro;
|
||||||
|
path.AddArc(arco, 270, 90);
|
||||||
|
|
||||||
|
arco.Y = retangulo.Bottom - diametro;
|
||||||
|
path.AddArc(arco, 0, 90);
|
||||||
|
|
||||||
|
arco.X = retangulo.Left;
|
||||||
|
path.AddArc(arco, 90, 90);
|
||||||
|
|
||||||
|
path.CloseFigure();
|
||||||
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PicBussola_Paint(object sender, PaintEventArgs e)
|
private void PicBussola_Paint(object sender, PaintEventArgs e)
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,9 @@ namespace AgroBase.Models
|
||||||
|
|
||||||
public static GPSModel ProjetarPontoDeslocado(GPSModel pontoOriginal, double distancia, double angulo)
|
public static GPSModel ProjetarPontoDeslocado(GPSModel pontoOriginal, double distancia, double angulo)
|
||||||
{
|
{
|
||||||
|
if (pontoOriginal == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
// Convertendo ângulo em radianos
|
// Convertendo ângulo em radianos
|
||||||
double anguloRad = (Math.PI / 180) * angulo;
|
double anguloRad = (Math.PI / 180) * angulo;
|
||||||
|
|
||||||
|
|
@ -526,7 +529,10 @@ namespace AgroBase.Models
|
||||||
var resultado = trecho
|
var resultado = trecho
|
||||||
.Select((p, i) => (ponto: p, index: i, dist: DistanciaEntrePontos(pontoRef, p)))
|
.Select((p, i) => (ponto: p, index: i, dist: DistanciaEntrePontos(pontoRef, p)))
|
||||||
.OrderBy(x => x.dist)
|
.OrderBy(x => x.dist)
|
||||||
.First();
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (resultado.ponto == null)
|
||||||
|
return (null, -1);
|
||||||
|
|
||||||
return (resultado.ponto, resultado.index);
|
return (resultado.ponto, resultado.index);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -692,7 +692,8 @@ namespace AgroBase
|
||||||
else if (trasDigital) { botao = BotoesJoystick.Quadrado; valor = MaximoAnalogico; direcao = Direcao.Tras; }
|
else if (trasDigital) { botao = BotoesJoystick.Quadrado; valor = MaximoAnalogico; direcao = Direcao.Tras; }
|
||||||
else { botao = BotoesJoystick.Xis; valor = 0; direcao = Direcao.Parado; }
|
else { botao = BotoesJoystick.Xis; valor = 0; direcao = Direcao.Parado; }
|
||||||
|
|
||||||
var cmd = DeParaComandos.First(x => x.botaoJoy == botao).Clone();
|
var cmd = DeParaComandos.FirstOrDefault(x => x.botaoJoy == botao)?.Clone();
|
||||||
|
if (cmd == null) return;
|
||||||
cmd.CalcularValor(direcao == Direcao.Parado, valorJoy: valor);
|
cmd.CalcularValor(direcao == Direcao.Parado, valorJoy: valor);
|
||||||
bool mudou = direcao != _ultimaDirecaoMovEnviada || double.IsNaN(_ultimoValorMovEnviado) || Math.Abs(cmd.ValorAplicar - _ultimoValorMovEnviado) >= VariacaoMinimaMov;
|
bool mudou = direcao != _ultimaDirecaoMovEnviada || double.IsNaN(_ultimoValorMovEnviado) || Math.Abs(cmd.ValorAplicar - _ultimoValorMovEnviado) >= VariacaoMinimaMov;
|
||||||
bool heartbeat = (DateTime.Now - _ultimoEnvioMov).TotalMilliseconds >= HeartbeatComandoMs;
|
bool heartbeat = (DateTime.Now - _ultimoEnvioMov).TotalMilliseconds >= HeartbeatComandoMs;
|
||||||
|
|
@ -712,7 +713,8 @@ namespace AgroBase
|
||||||
else if (_estadoBotoes.Contains(BotoesJoystick.AEEsquerda)) { botao = BotoesJoystick.AEEsquerda; valor = s.X; solto = false; }
|
else if (_estadoBotoes.Contains(BotoesJoystick.AEEsquerda)) { botao = BotoesJoystick.AEEsquerda; valor = s.X; solto = false; }
|
||||||
else if (_estadoBotoes.Contains(BotoesJoystick.AEDireita)) { botao = BotoesJoystick.AEDireita; valor = s.X; solto = false; }
|
else if (_estadoBotoes.Contains(BotoesJoystick.AEDireita)) { botao = BotoesJoystick.AEDireita; valor = s.X; solto = false; }
|
||||||
|
|
||||||
var cmd = DeParaComandos.First(x => x.botaoJoy == botao).Clone();
|
var cmd = DeParaComandos.FirstOrDefault(x => x.botaoJoy == botao)?.Clone();
|
||||||
|
if (cmd == null) return;
|
||||||
cmd.CalcularValor(solto, valorJoy: valor);
|
cmd.CalcularValor(solto, valorJoy: valor);
|
||||||
bool mudou = double.IsNaN(_ultimoAnguloDirEnviado) || Math.Abs(cmd.ValorAplicar - _ultimoAnguloDirEnviado) >= VariacaoMinimaDir;
|
bool mudou = double.IsNaN(_ultimoAnguloDirEnviado) || Math.Abs(cmd.ValorAplicar - _ultimoAnguloDirEnviado) >= VariacaoMinimaDir;
|
||||||
bool heartbeat = (DateTime.Now - _ultimoEnvioDir).TotalMilliseconds >= HeartbeatComandoMs;
|
bool heartbeat = (DateTime.Now - _ultimoEnvioDir).TotalMilliseconds >= HeartbeatComandoMs;
|
||||||
|
|
|
||||||
|
|
@ -1261,6 +1261,10 @@ namespace AgroBase.Models.Modules
|
||||||
x.TempoAtuadoEmMovimento = 0;
|
x.TempoAtuadoEmMovimento = 0;
|
||||||
x.TempoComFluxo = 0;
|
x.TempoComFluxo = 0;
|
||||||
|
|
||||||
|
x.UltimoStatusDesejado = false;
|
||||||
|
x.UltimoAnguloDesejado = x.AnguloNeutro;
|
||||||
|
x.UltimoComandoEnviado = DateTime.MinValue;
|
||||||
|
|
||||||
x.ReiniciarHistoricoAtuacao(true);
|
x.ReiniciarHistoricoAtuacao(true);
|
||||||
|
|
||||||
x.ValoresLeituras.ForEach(l =>
|
x.ValoresLeituras.ForEach(l =>
|
||||||
|
|
@ -1279,6 +1283,12 @@ namespace AgroBase.Models.Modules
|
||||||
x.TempoComPotencia = 0;
|
x.TempoComPotencia = 0;
|
||||||
x.PotenciaAcumuladaPctSeg = 0;
|
x.PotenciaAcumuladaPctSeg = 0;
|
||||||
x.PotenciaMediaAtiva = 0;
|
x.PotenciaMediaAtiva = 0;
|
||||||
|
|
||||||
|
x.UltimoStatusDesejado = false;
|
||||||
|
x.UltimaPotenciaDesejada = 0;
|
||||||
|
x.UltimaPressaoDesejada = 0;
|
||||||
|
x.UltimoComandoEnviado = DateTime.MinValue;
|
||||||
|
|
||||||
x.ValoresLeituras.ForEach(l =>
|
x.ValoresLeituras.ForEach(l =>
|
||||||
{
|
{
|
||||||
l.atual = new CanCommandFreqModel<dynamic>();
|
l.atual = new CanCommandFreqModel<dynamic>();
|
||||||
|
|
@ -2313,8 +2323,13 @@ namespace AgroBase.Models.Modules
|
||||||
public Estado _EstadoLeitura => (Estado)(ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados1 && x.funcao == FuncoesPinout.EstadoLeitura)?.atual?.valor ?? 0);
|
public Estado _EstadoLeitura => (Estado)(ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados1 && x.funcao == FuncoesPinout.EstadoLeitura)?.atual?.valor ?? 0);
|
||||||
public double _AnguloBicoLeitura => ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados1 && x.funcao == FuncoesPinout.ServoAnguloLeitura)?.atual? .valor ?? 0;
|
public double _AnguloBicoLeitura => ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados1 && x.funcao == FuncoesPinout.ServoAnguloLeitura)?.atual? .valor ?? 0;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public DateTime UltimoComandoEnviado { get; set; } = DateTime.MinValue;
|
public DateTime UltimoComandoEnviado { get; set; } = DateTime.MinValue;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public bool UltimoStatusDesejado { get; set; } = false;
|
public bool UltimoStatusDesejado { get; set; } = false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public double UltimoAnguloDesejado { get; set; } = 0;
|
public double UltimoAnguloDesejado { get; set; } = 0;
|
||||||
|
|
||||||
public (CanMessagePosicaoDados, byte[]) ProtocoloConfiguracao(PinoutDataModel pinout, bool conectar)
|
public (CanMessagePosicaoDados, byte[]) ProtocoloConfiguracao(PinoutDataModel pinout, bool conectar)
|
||||||
|
|
@ -2597,9 +2612,16 @@ namespace AgroBase.Models.Modules
|
||||||
public double PotenciaMediaAtiva { get; set; }
|
public double PotenciaMediaAtiva { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public DateTime UltimoComandoEnviado { get; set; } = DateTime.MinValue;
|
public DateTime UltimoComandoEnviado { get; set; } = DateTime.MinValue;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public bool UltimoStatusDesejado { get; set; } = false;
|
public bool UltimoStatusDesejado { get; set; } = false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public double UltimaPotenciaDesejada { get; set; } = 0;
|
public double UltimaPotenciaDesejada { get; set; } = 0;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public double UltimaPressaoDesejada { get; set; } = 0;
|
public double UltimaPressaoDesejada { get; set; } = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ namespace AgroBase.Models.Modules
|
||||||
}
|
}
|
||||||
public double AnguloFolgaCompensar { get; set; }
|
public double AnguloFolgaCompensar { get; set; }
|
||||||
public double? Reducao { get; set; }
|
public double? Reducao { get; set; }
|
||||||
public double FatorVelocidadeReducao => (Reducao ?? VariaveisEquipamento.ReducaoDirecional) / 20.0;
|
public double FatorVelocidadeReducao => (Reducao ?? VariaveisEquipamento.ReducaoDirecional) / 40.0;
|
||||||
public double OffsetAnguloReal { get; set; }
|
public double OffsetAnguloReal { get; set; }
|
||||||
public double RPM
|
public double RPM
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2386,9 +2386,12 @@ namespace AgroBase.Models.Modules
|
||||||
|
|
||||||
public void AtualizarStatusSinaleiros(ModoOperacao Modo, StatusOperacao _statusOperacao)
|
public void AtualizarStatusSinaleiros(ModoOperacao Modo, StatusOperacao _statusOperacao)
|
||||||
{
|
{
|
||||||
Sinaleiros.First(x => x.ID == "LEDMN").Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(Modo == ModoOperacao.Manual ? StatusLED.Aceso : StatusLED.Apagado);
|
var ledMn = Sinaleiros.FirstOrDefault(x => x.ID == "LEDMN");
|
||||||
Sinaleiros.First(x => x.ID == "LEDAT").Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(Modo == ModoOperacao.Manual ? StatusLED.Apagado : StatusLED.Aceso);
|
var ledAt = Sinaleiros.FirstOrDefault(x => x.ID == "LEDAT");
|
||||||
Sinaleiros.First(x => x.ID == "LEDOP").Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(StatusLED.Piscando, status_operacao: _statusOperacao);
|
var ledOp = Sinaleiros.FirstOrDefault(x => x.ID == "LEDOP");
|
||||||
|
if (ledMn != null) ledMn.Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(Modo == ModoOperacao.Manual ? StatusLED.Aceso : StatusLED.Apagado);
|
||||||
|
if (ledAt != null) ledAt.Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(Modo == ModoOperacao.Manual ? StatusLED.Apagado : StatusLED.Aceso);
|
||||||
|
if (ledOp != null) ledOp.Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(StatusLED.Piscando, status_operacao: _statusOperacao);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,8 @@ namespace AgroBase.Models
|
||||||
public double SimulacaoRpmControle { get; set; } = 0;
|
public double SimulacaoRpmControle { get; set; } = 0;
|
||||||
|
|
||||||
public int TempoIniciarOperacao { get; set; } = 10;
|
public int TempoIniciarOperacao { get; set; } = 10;
|
||||||
|
public int TempoAguardarRetomada { get; set; } = 5;
|
||||||
|
|
||||||
public string ID { get; set; }
|
public string ID { get; set; }
|
||||||
|
|
||||||
private static string _discoverySessionId = Guid.NewGuid().ToString("N");
|
private static string _discoverySessionId = Guid.NewGuid().ToString("N");
|
||||||
|
|
@ -493,6 +495,7 @@ namespace AgroBase.Models
|
||||||
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
||||||
{
|
{
|
||||||
RegistrarDadosPosProcessamento = false,
|
RegistrarDadosPosProcessamento = false,
|
||||||
|
ControleManualAcionado = true,
|
||||||
|
|
||||||
MovimentoAutomatico = false,
|
MovimentoAutomatico = false,
|
||||||
DirecionalAutomatico = false,
|
DirecionalAutomatico = false,
|
||||||
|
|
@ -512,10 +515,11 @@ namespace AgroBase.Models
|
||||||
MovVelocidadeSErvasPercent = 35,
|
MovVelocidadeSErvasPercent = 35,
|
||||||
MovVelocidadeCErvasPercent = 12,
|
MovVelocidadeCErvasPercent = 12,
|
||||||
DirAnguloMaximo = 30,
|
DirAnguloMaximo = 30,
|
||||||
DirVelocidadeMovimento = 40,
|
DirVelocidadeMovimento = 80,
|
||||||
|
|
||||||
DirTipoMovimento = TiposControladorDirecional.Manual,
|
DirTipoMovimento = TiposControladorDirecional.Manual,
|
||||||
|
|
||||||
|
DistanciaManobra = 3.0,
|
||||||
|
|
||||||
AtuAlturaAreaPulverizacao = 10,
|
AtuAlturaAreaPulverizacao = 10,
|
||||||
AtuDuracaoAtuacao = 200,
|
AtuDuracaoAtuacao = 200,
|
||||||
AtuPercentualErvasBicoOff = 2,
|
AtuPercentualErvasBicoOff = 2,
|
||||||
|
|
@ -603,6 +607,7 @@ namespace AgroBase.Models
|
||||||
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
||||||
{
|
{
|
||||||
RegistrarDadosPosProcessamento = false,
|
RegistrarDadosPosProcessamento = false,
|
||||||
|
ControleManualAcionado = false,
|
||||||
|
|
||||||
MovimentoAutomatico = true,
|
MovimentoAutomatico = true,
|
||||||
DirecionalAutomatico = true,
|
DirecionalAutomatico = true,
|
||||||
|
|
@ -622,10 +627,11 @@ namespace AgroBase.Models
|
||||||
MovVelocidadeSErvasPercent = 50,
|
MovVelocidadeSErvasPercent = 50,
|
||||||
MovVelocidadeCErvasPercent = 20,
|
MovVelocidadeCErvasPercent = 20,
|
||||||
DirAnguloMaximo = 30,
|
DirAnguloMaximo = 30,
|
||||||
DirVelocidadeMovimento = 50,
|
DirVelocidadeMovimento = 80,
|
||||||
|
|
||||||
DirTipoMovimento = TiposControladorDirecional.MPC,
|
DirTipoMovimento = TiposControladorDirecional.MPC,
|
||||||
|
|
||||||
|
DistanciaManobra = 3.0,
|
||||||
|
|
||||||
AtuAlturaAreaPulverizacao = 10,
|
AtuAlturaAreaPulverizacao = 10,
|
||||||
AtuDuracaoAtuacao = 200,
|
AtuDuracaoAtuacao = 200,
|
||||||
AtuPercentualErvasBicoOff = 2,
|
AtuPercentualErvasBicoOff = 2,
|
||||||
|
|
@ -743,6 +749,7 @@ namespace AgroBase.Models
|
||||||
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
||||||
{
|
{
|
||||||
RegistrarDadosPosProcessamento = false,
|
RegistrarDadosPosProcessamento = false,
|
||||||
|
ControleManualAcionado = false,
|
||||||
|
|
||||||
MovimentoAutomatico = true,
|
MovimentoAutomatico = true,
|
||||||
DirecionalAutomatico = true,
|
DirecionalAutomatico = true,
|
||||||
|
|
@ -762,9 +769,11 @@ namespace AgroBase.Models
|
||||||
MovVelocidadeSErvasPercent = 50,
|
MovVelocidadeSErvasPercent = 50,
|
||||||
MovVelocidadeCErvasPercent = 20,
|
MovVelocidadeCErvasPercent = 20,
|
||||||
DirAnguloMaximo = 30,
|
DirAnguloMaximo = 30,
|
||||||
DirVelocidadeMovimento = 50,
|
DirVelocidadeMovimento = 80,
|
||||||
DirTipoMovimento = TiposControladorDirecional.IA,
|
DirTipoMovimento = TiposControladorDirecional.IA,
|
||||||
|
|
||||||
|
DistanciaManobra = 3.0,
|
||||||
|
|
||||||
AtuAlturaAreaPulverizacao = 10,
|
AtuAlturaAreaPulverizacao = 10,
|
||||||
AtuDuracaoAtuacao = 200,
|
AtuDuracaoAtuacao = 200,
|
||||||
AtuPercentualErvasBicoOff = 2,
|
AtuPercentualErvasBicoOff = 2,
|
||||||
|
|
@ -880,6 +889,7 @@ namespace AgroBase.Models
|
||||||
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
||||||
{
|
{
|
||||||
RegistrarDadosPosProcessamento = false,
|
RegistrarDadosPosProcessamento = false,
|
||||||
|
ControleManualAcionado = false,
|
||||||
|
|
||||||
MovimentoAutomatico = true,
|
MovimentoAutomatico = true,
|
||||||
DirecionalAutomatico = true,
|
DirecionalAutomatico = true,
|
||||||
|
|
@ -899,10 +909,11 @@ namespace AgroBase.Models
|
||||||
MovVelocidadeSErvasPercent = 40,
|
MovVelocidadeSErvasPercent = 40,
|
||||||
MovVelocidadeCErvasPercent = 20,
|
MovVelocidadeCErvasPercent = 20,
|
||||||
DirAnguloMaximo = 30,
|
DirAnguloMaximo = 30,
|
||||||
DirVelocidadeMovimento = 50,
|
DirVelocidadeMovimento = 80,
|
||||||
|
|
||||||
DirTipoMovimento = TiposControladorDirecional.PID,
|
DirTipoMovimento = TiposControladorDirecional.PID,
|
||||||
|
|
||||||
|
DistanciaManobra = 3.0,
|
||||||
|
|
||||||
AtuAlturaAreaPulverizacao = 10,
|
AtuAlturaAreaPulverizacao = 10,
|
||||||
AtuDuracaoAtuacao = 200,
|
AtuDuracaoAtuacao = 200,
|
||||||
AtuPercentualErvasBicoOff = 2,
|
AtuPercentualErvasBicoOff = 2,
|
||||||
|
|
@ -978,6 +989,7 @@ namespace AgroBase.Models
|
||||||
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
op.Parametros.Controle = new OperacaoParametrosControleModel()
|
||||||
{
|
{
|
||||||
RegistrarDadosPosProcessamento = false,
|
RegistrarDadosPosProcessamento = false,
|
||||||
|
ControleManualAcionado = false,
|
||||||
|
|
||||||
MovimentoAutomatico = true,
|
MovimentoAutomatico = true,
|
||||||
DirecionalAutomatico = true,
|
DirecionalAutomatico = true,
|
||||||
|
|
@ -997,10 +1009,11 @@ namespace AgroBase.Models
|
||||||
MovVelocidadeSErvasPercent = 60,
|
MovVelocidadeSErvasPercent = 60,
|
||||||
MovVelocidadeCErvasPercent = 20,
|
MovVelocidadeCErvasPercent = 20,
|
||||||
DirAnguloMaximo = 30,
|
DirAnguloMaximo = 30,
|
||||||
DirVelocidadeMovimento = 50,
|
DirVelocidadeMovimento = 80,
|
||||||
|
|
||||||
DirTipoMovimento = TiposControladorDirecional.MPC,
|
DirTipoMovimento = TiposControladorDirecional.MPC,
|
||||||
|
|
||||||
|
DistanciaManobra = 3.0,
|
||||||
|
|
||||||
AtuAlturaAreaPulverizacao = 10,
|
AtuAlturaAreaPulverizacao = 10,
|
||||||
AtuDuracaoAtuacao = 200,
|
AtuDuracaoAtuacao = 200,
|
||||||
AtuPercentualErvasBicoOff = 2,
|
AtuPercentualErvasBicoOff = 2,
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ namespace AgroBase.Models.Operadores
|
||||||
AtualizarDadosControle = 3,
|
AtualizarDadosControle = 3,
|
||||||
EnviarDadosControle = 4,
|
EnviarDadosControle = 4,
|
||||||
FinalizarOperacao = 5,
|
FinalizarOperacao = 5,
|
||||||
|
ReiniciarMPC = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using AgroBase.Models.Operadores;
|
using AgroBase.Models.Operadores;
|
||||||
using AgroBase.Services;
|
using AgroBase.Services;
|
||||||
using AgroBase.Services.Operadores;
|
using AgroBase.Services.Operadores;
|
||||||
|
using BlackSharp.Core.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -999,19 +1000,51 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
if (_TrajetoriaFixaDefinida && !VerificacaoInicialMeioRuaConcluida && ((considerarStatus && new List<StatusOperacao>() { StatusOperacao.Aguardando, StatusOperacao.EmAndamento }.Contains(Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.StatusOperacaoAtual)) || !considerarStatus))
|
if (_TrajetoriaFixaDefinida && !VerificacaoInicialMeioRuaConcluida && ((considerarStatus && new List<StatusOperacao>() { StatusOperacao.Aguardando, StatusOperacao.EmAndamento }.Contains(Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.StatusOperacaoAtual)) || !considerarStatus))
|
||||||
{
|
{
|
||||||
|
var _posicaoAtual = GPSPosicaoAtual;
|
||||||
|
if (_posicaoAtual == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
VerificacaoInicialMeioRuaConcluida = true;
|
VerificacaoInicialMeioRuaConcluida = true;
|
||||||
(bool noCoredor, var idxPontoMaisProximo) = VerificaEquipamentoDentroCorredorMontado(_TrajetoriaFixa[0].Posicao);
|
|
||||||
if (noCoredor)
|
string msg = "";
|
||||||
|
|
||||||
|
var ultimoPonto = _TrajetoriaFixa[_TrajetoriaFixa.Count - 1];
|
||||||
|
if (ultimoPonto?.Posicao == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double distanciaUltimoPonto = GPSUtils.DistanciaEntrePontos(_posicaoAtual, ultimoPonto.Posicao);
|
||||||
|
|
||||||
|
//int idxCorredorDentro = VerificaEquipamentoDentroCorredor(_posicaoAtual);
|
||||||
|
|
||||||
|
if (!double.IsNaN(distanciaUltimoPonto) && !double.IsInfinity(distanciaUltimoPonto) && distanciaUltimoPonto < 5.0)
|
||||||
|
{
|
||||||
|
msg = $"Início no meio da rua ignorado: robô está a {distanciaUltimoPonto:0.00}m do fim. Nova operação começará do zero.";
|
||||||
|
Variaveis.OperacaoEmAndamento?.Sensoriamento?.InserirLog(T_Code.Trj, StatusModulo.Operante, 100, msg);
|
||||||
|
Variaveis.MostrarLog($"[TRJ] {msg}");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(bool noCoredor, var idxPontoMaisProximo) = VerificaEquipamentoDentroCorredorMontado(_posicaoAtual);
|
||||||
|
if (noCoredor && idxPontoMaisProximo >= 0 && idxPontoMaisProximo < _TrajetoriaFixa.Count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i <= idxPontoMaisProximo; i++)
|
for (int i = 0; i <= idxPontoMaisProximo; i++)
|
||||||
{
|
{
|
||||||
_TrajetoriaFixa[i].Visitado = true;
|
_TrajetoriaFixa[i].Visitado = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CorredorAtual.AtualizarDados();
|
CorredorAtual?.AtualizarDados();
|
||||||
|
|
||||||
|
msg = $"Início no meio da rua confirmado: idx={idxPontoMaisProximo}/{_TrajetoriaFixa.Count - 1}, marcados={idxPontoMaisProximo + 1}/{_TrajetoriaFixa.Count}.";
|
||||||
|
Variaveis.OperacaoEmAndamento?.Sensoriamento?.InserirLog(T_Code.Trj, StatusModulo.Alerta, 100, msg);
|
||||||
|
Variaveis.MostrarLog($"[TRJ] {msg}");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg = $"Início no meio da rua ignorado: robô está fora do corredor. Nova operação começará do zero.";
|
||||||
|
Variaveis.OperacaoEmAndamento?.Sensoriamento?.InserirLog(T_Code.Trj, StatusModulo.Operante, 100, msg);
|
||||||
|
Variaveis.MostrarLog($"[TRJ] {msg}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2099,7 +2132,7 @@ namespace AgroBase.Models
|
||||||
// 2. Para cada ponto da rua1 interpolada, encontrar o mais próximo na rua2 interpolada
|
// 2. Para cada ponto da rua1 interpolada, encontrar o mais próximo na rua2 interpolada
|
||||||
foreach (var ponto1 in rua1Interpolada)
|
foreach (var ponto1 in rua1Interpolada)
|
||||||
{
|
{
|
||||||
var pontoMaisProximo = rua2Interpolada.OrderBy(ponto2 => GPSUtils.DistanciaEntrePontos(ponto1, ponto2)).First();
|
var pontoMaisProximo = rua2Interpolada.OrderBy(ponto2 => GPSUtils.DistanciaEntrePontos(ponto1, ponto2)).FirstOrDefault();
|
||||||
|
|
||||||
double distancia = GPSUtils.DistanciaEntrePontos(ponto1, pontoMaisProximo);
|
double distancia = GPSUtils.DistanciaEntrePontos(ponto1, pontoMaisProximo);
|
||||||
|
|
||||||
|
|
@ -2191,8 +2224,8 @@ namespace AgroBase.Models
|
||||||
CorredorAtual.Reverse();
|
CorredorAtual.Reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
double anguloProjetar1 = GPSUtils.CalcularOrientacao(CorredorAtual.Skip(1).First(), CorredorAtual.First());
|
double anguloProjetar1 = GPSUtils.CalcularOrientacao(CorredorAtual.Skip(1).FirstOrDefault(), CorredorAtual.FirstOrDefault());
|
||||||
GPSModel PrimeiroPonto = GPSUtils.ProjetarPontoDeslocado(CorredorAtual.First(), DistanciaProjecaoRua, anguloProjetar1);
|
GPSModel PrimeiroPonto = GPSUtils.ProjetarPontoDeslocado(CorredorAtual.FirstOrDefault(), DistanciaProjecaoRua, anguloProjetar1);
|
||||||
|
|
||||||
var ultimoPontoTrajetoria = _trajetoriaFixa.LastOrDefault();
|
var ultimoPontoTrajetoria = _trajetoriaFixa.LastOrDefault();
|
||||||
|
|
||||||
|
|
@ -2220,8 +2253,8 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
int pontosAdicionr = Convert.ToInt32(CorredoresLarguras[idx] / (DistanciaEntrePontosCurva * _FatorLarguraCorredor));
|
int pontosAdicionr = Convert.ToInt32(CorredoresLarguras[idx] / (DistanciaEntrePontosCurva * _FatorLarguraCorredor));
|
||||||
var _penultimoPonto = _trajetoriaFixa[_trajetoriaFixa.Count() - 2].Posicao;
|
var _penultimoPonto = _trajetoriaFixa[_trajetoriaFixa.Count() - 2].Posicao;
|
||||||
double anguloProjecao = GPSUtils.CalcularOrientacao(Corredores[idx - 1].First(), Corredores[idx - 1].Last());
|
double anguloProjecao = GPSUtils.CalcularOrientacao(Corredores[idx - 1].FirstOrDefault(), Corredores[idx - 1].LastOrDefault());
|
||||||
GPSModel _ultimoPonto = GPSUtils.ProjetarPontoDeslocado(Corredores[idx - 1].Last(), DistanciaProjecaoRua, anguloProjecao);
|
GPSModel _ultimoPonto = GPSUtils.ProjetarPontoDeslocado(Corredores[idx - 1].LastOrDefault(), DistanciaProjecaoRua, anguloProjecao);
|
||||||
List<GPSModel> CurvaConexao = CriarCurvaEntrePontos(ultimoPontoTrajetoria.Posicao, PrimeiroPonto, DistanciaProjecaoRua * 2.0, anguloProjecao, pontosAdicionr, false);
|
List<GPSModel> CurvaConexao = CriarCurvaEntrePontos(ultimoPontoTrajetoria.Posicao, PrimeiroPonto, DistanciaProjecaoRua * 2.0, anguloProjecao, pontosAdicionr, false);
|
||||||
|
|
||||||
for (int i = 0; i < CurvaConexao.Count; i++)
|
for (int i = 0; i < CurvaConexao.Count; i++)
|
||||||
|
|
@ -2242,7 +2275,7 @@ namespace AgroBase.Models
|
||||||
idxPontoCorredor = _trajetoriaFixa.Count(x => x.idxCorredor == idx),
|
idxPontoCorredor = _trajetoriaFixa.Count(x => x.idxCorredor == idx),
|
||||||
Posicao = ponto,
|
Posicao = ponto,
|
||||||
Direcao = pontoLigacao ? direcaoAtual : DirecaoCarroRua.Manobra,
|
Direcao = pontoLigacao ? direcaoAtual : DirecaoCarroRua.Manobra,
|
||||||
Orientacao = GPSUtils.CalcularOrientacao(_trajetoriaFixa.Last().Posicao, ponto),
|
Orientacao = GPSUtils.CalcularOrientacao(_trajetoriaFixa.LastOrDefault().Posicao, ponto),
|
||||||
Visitado = false,
|
Visitado = false,
|
||||||
LarguraCorredor = larguraCorredorMenor * 0.9
|
LarguraCorredor = larguraCorredorMenor * 0.9
|
||||||
});
|
});
|
||||||
|
|
@ -2284,7 +2317,7 @@ namespace AgroBase.Models
|
||||||
double distancia_projetar = !ultimoCorredor ? (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva) : (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva);
|
double distancia_projetar = !ultimoCorredor ? (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva) : (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva);
|
||||||
|
|
||||||
GPSModel ultimoPontoCorredorAtual = CorredorAtual.Last();
|
GPSModel ultimoPontoCorredorAtual = CorredorAtual.Last();
|
||||||
double anguloProjetar2 = GPSUtils.CalcularOrientacao(CorredorAtual.Skip(CorredorAtual.Count() - 2).First(), ultimoPontoCorredorAtual);
|
double anguloProjetar2 = GPSUtils.CalcularOrientacao(CorredorAtual.Skip(CorredorAtual.Count() - 2).FirstOrDefault(), ultimoPontoCorredorAtual);
|
||||||
GPSModel UltimoPonto = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2);
|
GPSModel UltimoPonto = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2);
|
||||||
|
|
||||||
// Realiza um acréscimo no angulo à projetar, permitindo que o equipamento faça uma curva mais aberta entre corredores
|
// Realiza um acréscimo no angulo à projetar, permitindo que o equipamento faça uma curva mais aberta entre corredores
|
||||||
|
|
@ -2798,6 +2831,8 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
var lista = new List<GPSModel>();
|
var lista = new List<GPSModel>();
|
||||||
|
|
||||||
|
if (a == null || b == null) return lista;
|
||||||
|
|
||||||
double dist = GPSUtils.DistanciaEntrePontos(a, b);
|
double dist = GPSUtils.DistanciaEntrePontos(a, b);
|
||||||
if (dist < espacamento_m)
|
if (dist < espacamento_m)
|
||||||
{
|
{
|
||||||
|
|
@ -2850,8 +2885,10 @@ namespace AgroBase.Models
|
||||||
bool angCorretoI = Math.Abs(difAngI) <= limiar;
|
bool angCorretoI = Math.Abs(difAngI) <= limiar;
|
||||||
if (!angCorreto && !angCorretoI) continue;
|
if (!angCorreto && !angCorretoI) continue;
|
||||||
|
|
||||||
var pInicio = corredor.First();
|
var pInicio = corredor.FirstOrDefault();
|
||||||
var pFim = corredor.Last();
|
var pFim = corredor.LastOrDefault();
|
||||||
|
|
||||||
|
if (pInicio == null || pFim == null) continue;
|
||||||
|
|
||||||
double dBaseInicio = GPSUtils.DistanciaEntrePontos(posBase, pInicio);
|
double dBaseInicio = GPSUtils.DistanciaEntrePontos(posBase, pInicio);
|
||||||
double dBaseFim = GPSUtils.DistanciaEntrePontos(posBase, pFim);
|
double dBaseFim = GPSUtils.DistanciaEntrePontos(posBase, pFim);
|
||||||
|
|
@ -2888,9 +2925,11 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
var corredor = Corredores[c.idxCorredor];
|
var corredor = Corredores[c.idxCorredor];
|
||||||
var ponto =
|
var ponto =
|
||||||
c.condicaoCarro == TipoCondicaoRoboRua.EntraPeloInicio || c.condicaoCarro == TipoCondicaoRoboRua.SaiPeloInicio ? corredor.First() :
|
c.condicaoCarro == TipoCondicaoRoboRua.EntraPeloInicio || c.condicaoCarro == TipoCondicaoRoboRua.SaiPeloInicio ? corredor.FirstOrDefault() :
|
||||||
c.condicaoCarro == TipoCondicaoRoboRua.EntraPeloFim || c.condicaoCarro == TipoCondicaoRoboRua.SaiPeloFim ? corredor.Last() :
|
c.condicaoCarro == TipoCondicaoRoboRua.EntraPeloFim || c.condicaoCarro == TipoCondicaoRoboRua.SaiPeloFim ? corredor.LastOrDefault() :
|
||||||
corredor.First();
|
corredor.FirstOrDefault();
|
||||||
|
|
||||||
|
if (ponto == null) continue;
|
||||||
|
|
||||||
double comprimento = 0;
|
double comprimento = 0;
|
||||||
for (int i = 1; i < corredor.Count; i++)
|
for (int i = 1; i < corredor.Count; i++)
|
||||||
|
|
@ -2920,8 +2959,8 @@ namespace AgroBase.Models
|
||||||
var corredor = Corredores[idxCorredor];
|
var corredor = Corredores[idxCorredor];
|
||||||
|
|
||||||
bool sentidoCorreto = condicaoCarro == TipoCondicaoRoboRua.EntraPeloInicio || condicaoCarro == TipoCondicaoRoboRua.SaiPeloInicio;
|
bool sentidoCorreto = condicaoCarro == TipoCondicaoRoboRua.EntraPeloInicio || condicaoCarro == TipoCondicaoRoboRua.SaiPeloInicio;
|
||||||
GPSModel entrada = sentidoCorreto ? corredor.First() : corredor.Last();
|
GPSModel entrada = sentidoCorreto ? corredor.FirstOrDefault() : corredor.LastOrDefault();
|
||||||
GPSModel saida = sentidoCorreto ? corredor.Last() : corredor.First();
|
GPSModel saida = sentidoCorreto ? corredor.LastOrDefault() : corredor.FirstOrDefault();
|
||||||
|
|
||||||
// 1️⃣ robô → entrada do corredor
|
// 1️⃣ robô → entrada do corredor
|
||||||
pontos.AddRange(GerarTrechoReto(posRobo, entrada, DistanciaEntrePontos));
|
pontos.AddRange(GerarTrechoReto(posRobo, entrada, DistanciaEntrePontos));
|
||||||
|
|
@ -3063,7 +3102,7 @@ namespace AgroBase.Models
|
||||||
if (pts.Count < 3) return pts;
|
if (pts.Count < 3) return pts;
|
||||||
|
|
||||||
// pivot: menor y, depois menor x
|
// pivot: menor y, depois menor x
|
||||||
var pivot = pts.OrderBy(p => p.y).ThenBy(p => p.x).First();
|
var pivot = pts.OrderBy(p => p.y).ThenBy(p => p.x).FirstOrDefault();
|
||||||
|
|
||||||
// ordena por ângulo polar com pivot
|
// ordena por ângulo polar com pivot
|
||||||
var sorted = pts
|
var sorted = pts
|
||||||
|
|
|
||||||
|
|
@ -696,12 +696,12 @@ namespace AgroBase.Services
|
||||||
|
|
||||||
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
||||||
{
|
{
|
||||||
proximoId = idsDisponiveis.First();
|
proximoId = idsDisponiveis.FirstOrDefault();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
var index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
||||||
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.First();
|
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seleciona a mensagem mais antiga com esse IdTx
|
// Seleciona a mensagem mais antiga com esse IdTx
|
||||||
|
|
|
||||||
|
|
@ -510,12 +510,12 @@ namespace AgroBase.Services
|
||||||
|
|
||||||
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
||||||
{
|
{
|
||||||
proximoId = idsDisponiveis.First();
|
proximoId = idsDisponiveis.FirstOrDefault();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
var index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
||||||
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.First();
|
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seleciona a mensagem mais antiga com esse IdTx
|
// Seleciona a mensagem mais antiga com esse IdTx
|
||||||
|
|
|
||||||
|
|
@ -480,11 +480,11 @@ namespace AgroBase.Services
|
||||||
|
|
||||||
uint proximoId;
|
uint proximoId;
|
||||||
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
||||||
proximoId = idsDisponiveis.First();
|
proximoId = idsDisponiveis.FirstOrDefault();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
int index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
||||||
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.First();
|
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseLista.Where(x => x.IdTx == proximoId).OrderBy(x => x.Momento).FirstOrDefault();
|
return baseLista.Where(x => x.IdTx == proximoId).OrderBy(x => x.Momento).FirstOrDefault();
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,38 @@ namespace AgroBase.Services
|
||||||
private const double JanelaAutonomia_m = 150.0;
|
private const double JanelaAutonomia_m = 150.0;
|
||||||
private const double JanelaAutonomiaTempo_s = 300.0;
|
private const double JanelaAutonomiaTempo_s = 300.0;
|
||||||
|
|
||||||
private const double AlphaHistorico = 0.20;
|
|
||||||
private const double FatorConservadorHistorico = 1.10;
|
private const double FatorConservadorHistorico = 1.10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A EMA recente só é atualizada depois de acumular este deslocamento.
|
||||||
|
* Isso evita atualizar centenas de vezes com janelas quase idênticas.
|
||||||
|
*/
|
||||||
|
private const double PassoAtualizacaoEma_m = 5.0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reação assimétrica:
|
||||||
|
* - sobe rápido quando o consumo piora;
|
||||||
|
* - desce devagar quando o consumo melhora.
|
||||||
|
*/
|
||||||
|
private const double AlphaEmaSubida = 0.35;
|
||||||
|
private const double AlphaEmaDescida = 0.08;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Peso da condição recente na fusão.
|
||||||
|
* Se o trecho recente estiver pior que a média global,
|
||||||
|
* seu peso aumenta dinamicamente.
|
||||||
|
*/
|
||||||
|
private const double PesoRecenteNormal = 0.30;
|
||||||
|
private const double PesoRecenteMaximo = 0.60;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Barreiras contra divisões instáveis e amostras absurdas.
|
||||||
|
* O limite máximo deve ser generoso e serve apenas para rejeitar
|
||||||
|
* corrupção de dados, salto de distância ou intervalo elétrico inválido.
|
||||||
|
*/
|
||||||
|
private const double MinWhPorMetroValido = 0.01;
|
||||||
|
private const double MaxWhPorMetroValido = 5.00;
|
||||||
|
|
||||||
private const double DiferencaTensaoAlerta_V = 2.0;
|
private const double DiferencaTensaoAlerta_V = 2.0;
|
||||||
private const double DiferencaCorrenteMinAlerta_A = 3.0;
|
private const double DiferencaCorrenteMinAlerta_A = 3.0;
|
||||||
private const double DiferencaCorrenteRelativaAlerta = 0.35;
|
private const double DiferencaCorrenteRelativaAlerta = 0.35;
|
||||||
|
|
@ -60,6 +89,31 @@ namespace AgroBase.Services
|
||||||
private readonly double _dalyCorrenteDescargaSinal;
|
private readonly double _dalyCorrenteDescargaSinal;
|
||||||
private readonly double _sensorCorrenteDescargaSinal;
|
private readonly double _sensorCorrenteDescargaSinal;
|
||||||
|
|
||||||
|
private double _somaDistanciaOperacao_m;
|
||||||
|
private double _somaTempoOperacao_s;
|
||||||
|
private double _somaEnergiaOperacao_Wh;
|
||||||
|
|
||||||
|
private double _distanciaDesdeAtualizacaoEma_m;
|
||||||
|
|
||||||
|
public double DistanciaHistoricoGlobal_m => _somaDistanciaOperacao_m;
|
||||||
|
public double TempoHistoricoGlobal_s => _somaTempoOperacao_s;
|
||||||
|
public double EnergiaHistoricoGlobal_Wh => _somaEnergiaOperacao_Wh;
|
||||||
|
public double DistanciaHistoricoJanela_m => _somaDistanciaJanela_m;
|
||||||
|
public double TempoHistoricoJanela_s => _somaTempoJanela_s;
|
||||||
|
public double EnergiaHistoricoJanela_Wh => _somaEnergiaJanela_Wh;
|
||||||
|
|
||||||
|
public double WhPorMetroGlobal { get; private set; }
|
||||||
|
|
||||||
|
public double WhPorMetroRecenteEma { get; private set; }
|
||||||
|
|
||||||
|
public double WhPorMetroCombinado { get; private set; }
|
||||||
|
|
||||||
|
public double PesoRecenteAplicado { get; private set; }
|
||||||
|
|
||||||
|
public bool HistoricoGlobalValido { get; private set; }
|
||||||
|
|
||||||
|
public bool HistoricoRecenteValido { get; private set; }
|
||||||
|
|
||||||
private class FonteEnergiaSnapshot
|
private class FonteEnergiaSnapshot
|
||||||
{
|
{
|
||||||
public string Nome { get; set; } = "";
|
public string Nome { get; set; } = "";
|
||||||
|
|
@ -84,7 +138,7 @@ namespace AgroBase.Services
|
||||||
double max_Ah,
|
double max_Ah,
|
||||||
double wh_m_base = 0.30,
|
double wh_m_base = 0.30,
|
||||||
double tensao_nominal_V = 36.0,
|
double tensao_nominal_V = 36.0,
|
||||||
double daly_corrente_descarga_sinal = -1.0,
|
double daly_corrente_descarga_sinal = 1.0,
|
||||||
double sensor_corrente_descarga_sinal = 1.0,
|
double sensor_corrente_descarga_sinal = 1.0,
|
||||||
double timeout_bms_s = 6.0,
|
double timeout_bms_s = 6.0,
|
||||||
double timeout_sensor_s = 4.0)
|
double timeout_sensor_s = 4.0)
|
||||||
|
|
@ -1113,10 +1167,15 @@ namespace AgroBase.Services
|
||||||
_distanciaPendenteAutonomia_m += deltaDist_m;
|
_distanciaPendenteAutonomia_m += deltaDist_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FecharIntervaloAutonomia(
|
private void FecharIntervaloAutonomia(double deltaTempo_s, double deltaEnergia_Wh)
|
||||||
double deltaTempo_s,
|
|
||||||
double deltaEnergia_Wh)
|
|
||||||
{
|
{
|
||||||
|
if (!OperacaoAtiva || OperacaoFinalizada)
|
||||||
|
{
|
||||||
|
DescartarIntervaloPendente("fora_de_operacao");
|
||||||
|
RecalcularAutonomia();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_aguardandoBaselineAutonomia)
|
if (_aguardandoBaselineAutonomia)
|
||||||
{
|
{
|
||||||
DescartarIntervaloPendente("baseline_autonomia");
|
DescartarIntervaloPendente("baseline_autonomia");
|
||||||
|
|
@ -1136,7 +1195,7 @@ namespace AgroBase.Services
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AtualizarJanelaAutonomia(
|
AtualizarHistoricoAutonomia(
|
||||||
_distanciaPendenteAutonomia_m,
|
_distanciaPendenteAutonomia_m,
|
||||||
deltaTempo_s,
|
deltaTempo_s,
|
||||||
deltaEnergia_Wh
|
deltaEnergia_Wh
|
||||||
|
|
@ -1191,44 +1250,146 @@ namespace AgroBase.Services
|
||||||
return deltaDist_m;
|
return deltaDist_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AtualizarJanelaAutonomia(double deltaDist_m, double deltaTempo_s, double deltaEnergia_Wh)
|
private void AtualizarHistoricoAutonomia(double deltaDist_m, double deltaTempo_s, double deltaEnergia_Wh)
|
||||||
{
|
{
|
||||||
|
if (!ValorFinito(deltaDist_m) ||
|
||||||
|
!ValorFinito(deltaTempo_s) ||
|
||||||
|
!ValorFinito(deltaEnergia_Wh))
|
||||||
|
{
|
||||||
|
EstadoIntervaloAutonomia = "amostra_nao_finita";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
deltaDist_m = Math.Max(0, deltaDist_m);
|
deltaDist_m = Math.Max(0, deltaDist_m);
|
||||||
deltaTempo_s = Math.Max(0, deltaTempo_s);
|
deltaTempo_s = Math.Max(0, deltaTempo_s);
|
||||||
deltaEnergia_Wh = Math.Max(0, deltaEnergia_Wh);
|
deltaEnergia_Wh = Math.Max(0, deltaEnergia_Wh);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mesmo com distância zero, a energia entra na janela.
|
* O histórico global e a janela recebem exatamente os mesmos
|
||||||
* Assim espera, calibração, manobras paradas e processamento
|
* intervalos elétricos considerados confiáveis.
|
||||||
* continuam compondo o custo real da operação.
|
*
|
||||||
|
* Energia gasta parado também entra. Quando o robô voltar a andar,
|
||||||
|
* esse custo fará parte do Wh/m real da operação.
|
||||||
*/
|
*/
|
||||||
_janelaAutonomia.Enqueue((deltaDist_m, deltaTempo_s, deltaEnergia_Wh));
|
_somaDistanciaOperacao_m += deltaDist_m;
|
||||||
|
_somaTempoOperacao_s += deltaTempo_s;
|
||||||
|
_somaEnergiaOperacao_Wh += deltaEnergia_Wh;
|
||||||
|
|
||||||
|
_janelaAutonomia.Enqueue(
|
||||||
|
(deltaDist_m, deltaTempo_s, deltaEnergia_Wh)
|
||||||
|
);
|
||||||
|
|
||||||
_somaDistanciaJanela_m += deltaDist_m;
|
_somaDistanciaJanela_m += deltaDist_m;
|
||||||
_somaTempoJanela_s += deltaTempo_s;
|
_somaTempoJanela_s += deltaTempo_s;
|
||||||
_somaEnergiaJanela_Wh += deltaEnergia_Wh;
|
_somaEnergiaJanela_Wh += deltaEnergia_Wh;
|
||||||
|
|
||||||
while ((_somaDistanciaJanela_m > JanelaAutonomia_m || _somaTempoJanela_s > JanelaAutonomiaTempo_s) && _janelaAutonomia.Count > 0)
|
/*
|
||||||
|
* Remove amostras antigas até a janela voltar aos limites.
|
||||||
|
* Ela representa no máximo os últimos 150 m ou 300 s.
|
||||||
|
*/
|
||||||
|
while ((_somaDistanciaJanela_m > JanelaAutonomia_m ||
|
||||||
|
_somaTempoJanela_s > JanelaAutonomiaTempo_s) &&
|
||||||
|
_janelaAutonomia.Count > 1)
|
||||||
{
|
{
|
||||||
var old = _janelaAutonomia.Dequeue();
|
var antiga = _janelaAutonomia.Dequeue();
|
||||||
_somaDistanciaJanela_m -= old.dist_m;
|
|
||||||
_somaTempoJanela_s -= old.tempo_s;
|
_somaDistanciaJanela_m -= antiga.dist_m;
|
||||||
_somaEnergiaJanela_Wh -= old.energia_Wh;
|
_somaTempoJanela_s -= antiga.tempo_s;
|
||||||
|
_somaEnergiaJanela_Wh -= antiga.energia_Wh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_somaDistanciaJanela_m > MinDistanciaTaxa_m)
|
/*
|
||||||
|
* Corrige pequenos resíduos negativos de ponto flutuante.
|
||||||
|
*/
|
||||||
|
_somaDistanciaJanela_m =
|
||||||
|
Math.Max(0, _somaDistanciaJanela_m);
|
||||||
|
|
||||||
|
_somaTempoJanela_s =
|
||||||
|
Math.Max(0, _somaTempoJanela_s);
|
||||||
|
|
||||||
|
_somaEnergiaJanela_Wh =
|
||||||
|
Math.Max(0, _somaEnergiaJanela_Wh);
|
||||||
|
|
||||||
|
if (_somaDistanciaOperacao_m >= MinDistanciaTaxa_m)
|
||||||
{
|
{
|
||||||
WhPorMetroJanela = _somaEnergiaJanela_Wh / _somaDistanciaJanela_m;
|
double taxaGlobal =
|
||||||
|
_somaEnergiaOperacao_Wh /
|
||||||
|
_somaDistanciaOperacao_m;
|
||||||
|
|
||||||
|
if (TaxaWhPorMetroValida(taxaGlobal))
|
||||||
|
{
|
||||||
|
WhPorMetroGlobal = taxaGlobal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_somaDistanciaJanela_m >= MinDistanciaHistorico_m && WhPorMetroJanela > 0)
|
if (_somaDistanciaJanela_m >= MinDistanciaTaxa_m)
|
||||||
{
|
{
|
||||||
WhPorMetroHistorico = WhPorMetroHistorico <= 0 ? WhPorMetroJanela : WhPorMetroHistorico * (1.0 - AlphaHistorico) + WhPorMetroJanela * AlphaHistorico;
|
double taxaJanela =
|
||||||
|
_somaEnergiaJanela_Wh /
|
||||||
|
_somaDistanciaJanela_m;
|
||||||
|
|
||||||
AutonomiaComHistorico = true;
|
if (TaxaWhPorMetroValida(taxaJanela))
|
||||||
|
{
|
||||||
|
WhPorMetroJanela = taxaJanela;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HistoricoGlobalValido =
|
||||||
|
_somaDistanciaOperacao_m >= MinDistanciaHistorico_m &&
|
||||||
|
TaxaWhPorMetroValida(WhPorMetroGlobal);
|
||||||
|
|
||||||
|
HistoricoRecenteValido =
|
||||||
|
_somaDistanciaJanela_m >= MinDistanciaHistorico_m &&
|
||||||
|
TaxaWhPorMetroValida(WhPorMetroJanela);
|
||||||
|
|
||||||
|
_distanciaDesdeAtualizacaoEma_m += deltaDist_m;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Atualiza a EMA a cada 5 m, e não a cada frame.
|
||||||
|
* Isso evita dar peso repetido à mesma janela sobreposta.
|
||||||
|
*/
|
||||||
|
if (HistoricoRecenteValido &&
|
||||||
|
(_distanciaDesdeAtualizacaoEma_m >= PassoAtualizacaoEma_m ||
|
||||||
|
WhPorMetroRecenteEma <= 0))
|
||||||
|
{
|
||||||
|
AtualizarEmaRecente(WhPorMetroJanela);
|
||||||
|
_distanciaDesdeAtualizacaoEma_m = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutonomiaComHistorico =
|
||||||
|
HistoricoGlobalValido &&
|
||||||
|
HistoricoRecenteValido &&
|
||||||
|
TaxaWhPorMetroValida(WhPorMetroRecenteEma);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AtualizarEmaRecente(double taxaAtual)
|
||||||
|
{
|
||||||
|
if (!TaxaWhPorMetroValida(taxaAtual))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TaxaWhPorMetroValida(WhPorMetroRecenteEma))
|
||||||
|
{
|
||||||
|
WhPorMetroRecenteEma = taxaAtual;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double alpha =
|
||||||
|
taxaAtual > WhPorMetroRecenteEma
|
||||||
|
? AlphaEmaSubida
|
||||||
|
: AlphaEmaDescida;
|
||||||
|
|
||||||
|
WhPorMetroRecenteEma =
|
||||||
|
WhPorMetroRecenteEma * (1.0 - alpha) +
|
||||||
|
taxaAtual * alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TaxaWhPorMetroValida(double taxa)
|
||||||
|
{
|
||||||
|
return ValorFinito(taxa) &&
|
||||||
|
taxa >= MinWhPorMetroValido &&
|
||||||
|
taxa <= MaxWhPorMetroValido;
|
||||||
|
}
|
||||||
|
|
||||||
private void RecalcularAutonomia()
|
private void RecalcularAutonomia()
|
||||||
{
|
{
|
||||||
if (!Iniciado || !_estadoInicializado)
|
if (!Iniciado || !_estadoInicializado)
|
||||||
|
|
@ -1237,38 +1398,115 @@ namespace AgroBase.Services
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AutonomiaComHistorico && WhPorMetroHistorico > 0)
|
if (AutonomiaComHistorico)
|
||||||
{
|
{
|
||||||
FonteAutonomia = "historico_operacional";
|
FonteAutonomia = "historico_global_recente";
|
||||||
|
|
||||||
double consumoObservado = Math.Max(WhPorMetroJanela, WhPorMetroHistorico);
|
/*
|
||||||
|
* Normalmente o recente recebe 30%.
|
||||||
|
*
|
||||||
|
* Se o recente estiver consumindo mais que a média global,
|
||||||
|
* seu peso cresce progressivamente até 60%.
|
||||||
|
*
|
||||||
|
* Se estiver consumindo menos, permanece em 30%, evitando que
|
||||||
|
* um pequeno trecho leve gere uma autonomia otimista demais.
|
||||||
|
*/
|
||||||
|
double razaoRecenteGlobal =
|
||||||
|
WhPorMetroRecenteEma /
|
||||||
|
Math.Max(WhPorMetroGlobal, MinWhPorMetroValido);
|
||||||
|
|
||||||
WhPorMetroReferencia = consumoObservado * FatorConservadorHistorico;
|
if (razaoRecenteGlobal > 1.0)
|
||||||
|
{
|
||||||
|
double agravamento =
|
||||||
|
Clamp(razaoRecenteGlobal - 1.0, 0, 1.0);
|
||||||
|
|
||||||
|
PesoRecenteAplicado =
|
||||||
|
PesoRecenteNormal +
|
||||||
|
agravamento *
|
||||||
|
(PesoRecenteMaximo - PesoRecenteNormal);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PesoRecenteAplicado = PesoRecenteNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
double pesoGlobal = 1.0 - PesoRecenteAplicado;
|
||||||
|
|
||||||
|
WhPorMetroCombinado =
|
||||||
|
WhPorMetroGlobal * pesoGlobal +
|
||||||
|
WhPorMetroRecenteEma * PesoRecenteAplicado;
|
||||||
|
|
||||||
|
WhPorMetroHistorico = WhPorMetroCombinado;
|
||||||
|
|
||||||
|
WhPorMetroReferencia =
|
||||||
|
WhPorMetroCombinado *
|
||||||
|
FatorConservadorHistorico;
|
||||||
|
}
|
||||||
|
else if (HistoricoGlobalValido)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* A operação já tem distância global suficiente,
|
||||||
|
* mas a janela/EMA recente ainda está amadurecendo.
|
||||||
|
*/
|
||||||
|
FonteAutonomia = "historico_global_inicial";
|
||||||
|
|
||||||
|
PesoRecenteAplicado = 0;
|
||||||
|
WhPorMetroCombinado = WhPorMetroGlobal;
|
||||||
|
WhPorMetroHistorico = WhPorMetroGlobal;
|
||||||
|
|
||||||
|
WhPorMetroReferencia =
|
||||||
|
WhPorMetroGlobal *
|
||||||
|
FatorConservadorHistorico;
|
||||||
}
|
}
|
||||||
else if (WhPorMetroJanela > 0)
|
else if (WhPorMetroJanela > 0)
|
||||||
{
|
{
|
||||||
FonteAutonomia = "janela_inicial";
|
FonteAutonomia = "janela_inicial";
|
||||||
|
|
||||||
WhPorMetroReferencia = Math.Max(WhPorMetroBase, WhPorMetroJanela);
|
PesoRecenteAplicado = 0;
|
||||||
|
WhPorMetroCombinado = 0;
|
||||||
|
WhPorMetroHistorico = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Antes de 20 m, o fallback ainda funciona como piso.
|
||||||
|
*/
|
||||||
|
WhPorMetroReferencia =
|
||||||
|
Math.Max(
|
||||||
|
WhPorMetroBase,
|
||||||
|
WhPorMetroJanela
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FonteAutonomia = "fallback_campo";
|
FonteAutonomia = "fallback_campo";
|
||||||
|
|
||||||
|
PesoRecenteAplicado = 0;
|
||||||
|
WhPorMetroCombinado = 0;
|
||||||
|
WhPorMetroHistorico = 0;
|
||||||
WhPorMetroReferencia = WhPorMetroBase;
|
WhPorMetroReferencia = WhPorMetroBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
WhPorMetro = WhPorMetroReferencia;
|
WhPorMetro = WhPorMetroReferencia;
|
||||||
|
|
||||||
if (WhUtilRestante > 0 && WhPorMetroReferencia > 0)
|
if (WhUtilRestante > 0 &&
|
||||||
|
WhPorMetroReferencia > 0)
|
||||||
{
|
{
|
||||||
DistanciaMaximaTeorica_m = WhUtilRestante / WhPorMetroReferencia;
|
DistanciaMaximaTeorica_m =
|
||||||
|
WhUtilRestante /
|
||||||
|
WhPorMetroReferencia;
|
||||||
|
|
||||||
double margem = Clamp(MargemSegurancaAutonomia, 0, 0.90);
|
double margem =
|
||||||
|
Clamp(
|
||||||
|
MargemSegurancaAutonomia,
|
||||||
|
0,
|
||||||
|
0.90
|
||||||
|
);
|
||||||
|
|
||||||
DistanciaMaximaSegura_m = DistanciaMaximaTeorica_m * (1.0 - margem);
|
DistanciaMaximaSegura_m =
|
||||||
|
DistanciaMaximaTeorica_m *
|
||||||
|
(1.0 - margem);
|
||||||
|
|
||||||
DistanciaEstimadaRestante_m = DistanciaMaximaSegura_m;
|
DistanciaEstimadaRestante_m =
|
||||||
|
DistanciaMaximaSegura_m;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1438,6 +1676,21 @@ namespace AgroBase.Services
|
||||||
DistanciaMaximaTeorica_m = 0;
|
DistanciaMaximaTeorica_m = 0;
|
||||||
DistanciaMaximaSegura_m = 0;
|
DistanciaMaximaSegura_m = 0;
|
||||||
DistanciaEstimadaRestante_m = 0;
|
DistanciaEstimadaRestante_m = 0;
|
||||||
|
|
||||||
|
_somaDistanciaOperacao_m = 0;
|
||||||
|
_somaTempoOperacao_s = 0;
|
||||||
|
_somaEnergiaOperacao_Wh = 0;
|
||||||
|
|
||||||
|
_distanciaDesdeAtualizacaoEma_m = 0;
|
||||||
|
|
||||||
|
WhPorMetroGlobal = 0;
|
||||||
|
WhPorMetroRecenteEma = 0;
|
||||||
|
WhPorMetroCombinado = 0;
|
||||||
|
|
||||||
|
PesoRecenteAplicado = 0;
|
||||||
|
|
||||||
|
HistoricoGlobalValido = false;
|
||||||
|
HistoricoRecenteValido = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetarTudo()
|
private void ResetarTudo()
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,15 @@ namespace AgroBase.Services
|
||||||
private double _somaTempoPulverizacao_s;
|
private double _somaTempoPulverizacao_s;
|
||||||
private double _somaVolumePulverizacaoTempo_L;
|
private double _somaVolumePulverizacaoTempo_L;
|
||||||
|
|
||||||
private double _somaDistanciaOperacional_m;
|
private double _somaDistanciaOperacionalJanela_m;
|
||||||
private double _somaTempoOperacional_s;
|
private double _somaTempoOperacionalJanela_s;
|
||||||
private double _somaVolumeOperacional_L;
|
private double _somaVolumeOperacionalJanela_L;
|
||||||
|
|
||||||
|
private double _somaDistanciaOperacionalGlobal_m;
|
||||||
|
private double _somaTempoOperacionalGlobal_s;
|
||||||
|
private double _somaVolumeOperacionalGlobal_L;
|
||||||
|
|
||||||
|
private double _distanciaDesdeAtualizacaoEmaOperacional_m;
|
||||||
|
|
||||||
private double _volumeAnterior_L;
|
private double _volumeAnterior_L;
|
||||||
private double _distanciaTotalAnterior_m;
|
private double _distanciaTotalAnterior_m;
|
||||||
|
|
@ -329,6 +335,42 @@ namespace AgroBase.Services
|
||||||
|
|
||||||
private int _atuacoesInicioOperacao;
|
private int _atuacoesInicioOperacao;
|
||||||
|
|
||||||
|
private const double PassoAtualizacaoEmaOperacional_m = 5.0;
|
||||||
|
|
||||||
|
private const double AlphaEmaOperacionalSubida = 0.35;
|
||||||
|
private const double AlphaEmaOperacionalDescida = 0.08;
|
||||||
|
|
||||||
|
private const double PesoRecenteOperacionalNormal = 0.30;
|
||||||
|
private const double PesoRecenteOperacionalMaximo = 0.60;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Limites amplos para rejeitar corrupção de dados.
|
||||||
|
* Não devem limitar condições reais de campo.
|
||||||
|
*/
|
||||||
|
private const double MinLPorMetroOperacionalValido = 0.00001;
|
||||||
|
private const double MaxLPorMetroOperacionalValido = 1.00;
|
||||||
|
|
||||||
|
public double DistanciaOperacionalGlobal_m => _somaDistanciaOperacionalGlobal_m;
|
||||||
|
public double TempoOperacionalGlobal_s => _somaTempoOperacionalGlobal_s;
|
||||||
|
public double VolumeOperacionalGlobal_L => _somaVolumeOperacionalGlobal_L;
|
||||||
|
public double DistanciaOperacionalJanela_m => _somaDistanciaOperacionalJanela_m;
|
||||||
|
public double TempoOperacionalJanela_s => _somaTempoOperacionalJanela_s;
|
||||||
|
public double VolumeOperacionalJanela_L => _somaVolumeOperacionalJanela_L;
|
||||||
|
|
||||||
|
public double LPorMetroOperacionalGlobal { get; private set; }
|
||||||
|
|
||||||
|
public double LPorMetroOperacionalJanela { get; private set; }
|
||||||
|
|
||||||
|
public double LPorMetroOperacionalRecenteEma { get; private set; }
|
||||||
|
|
||||||
|
public double LPorMetroOperacionalCombinado { get; private set; }
|
||||||
|
|
||||||
|
public double PesoRecenteOperacionalAplicado { get; private set; }
|
||||||
|
|
||||||
|
public bool HistoricoOperacionalGlobalValido { get; private set; }
|
||||||
|
|
||||||
|
public bool HistoricoOperacionalRecenteValido { get; private set; }
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Ciclo de vida
|
// Ciclo de vida
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -1132,20 +1174,42 @@ namespace AgroBase.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AtualizarJanelaOperacional(
|
private void AtualizarJanelaOperacional(double deltaDist_m, double deltaTempo_s, double deltaVolume_L)
|
||||||
double deltaDist_m,
|
|
||||||
double deltaTempo_s,
|
|
||||||
double deltaVolume_L)
|
|
||||||
{
|
{
|
||||||
deltaDist_m =
|
if (!OperacaoAtiva || OperacaoFinalizada)
|
||||||
Math.Max(0, deltaDist_m);
|
return;
|
||||||
|
|
||||||
deltaTempo_s =
|
if (!ValorFinito(deltaDist_m) ||
|
||||||
Math.Max(0, deltaTempo_s);
|
!ValorFinito(deltaTempo_s) ||
|
||||||
|
!ValorFinito(deltaVolume_L))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
deltaVolume_L =
|
deltaDist_m = Math.Max(0, deltaDist_m);
|
||||||
Math.Max(0, deltaVolume_L);
|
deltaTempo_s = Math.Max(0, deltaTempo_s);
|
||||||
|
deltaVolume_L = Math.Max(0, deltaVolume_L);
|
||||||
|
|
||||||
|
if (deltaDist_m <= 0 &&
|
||||||
|
deltaTempo_s <= 0 &&
|
||||||
|
deltaVolume_L <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Histórico global da operação corrente.
|
||||||
|
*
|
||||||
|
* Todo deslocamento entra, mesmo sem pulverização.
|
||||||
|
* O volume entra somente quando o consumo foi validado.
|
||||||
|
*/
|
||||||
|
_somaDistanciaOperacionalGlobal_m += deltaDist_m;
|
||||||
|
_somaTempoOperacionalGlobal_s += deltaTempo_s;
|
||||||
|
_somaVolumeOperacionalGlobal_L += deltaVolume_L;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Janela operacional recente.
|
||||||
|
*/
|
||||||
_janelaOperacional.Enqueue(
|
_janelaOperacional.Enqueue(
|
||||||
(
|
(
|
||||||
deltaDist_m,
|
deltaDist_m,
|
||||||
|
|
@ -1154,66 +1218,161 @@ namespace AgroBase.Services
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
_somaDistanciaOperacional_m +=
|
_somaDistanciaOperacionalJanela_m += deltaDist_m;
|
||||||
deltaDist_m;
|
_somaTempoOperacionalJanela_s += deltaTempo_s;
|
||||||
|
_somaVolumeOperacionalJanela_L += deltaVolume_L;
|
||||||
_somaTempoOperacional_s +=
|
|
||||||
deltaTempo_s;
|
|
||||||
|
|
||||||
_somaVolumeOperacional_L +=
|
|
||||||
deltaVolume_L;
|
|
||||||
|
|
||||||
while (
|
while (
|
||||||
(
|
(
|
||||||
_somaDistanciaOperacional_m >
|
_somaDistanciaOperacionalJanela_m >
|
||||||
JanelaOperacional_m ||
|
JanelaOperacional_m ||
|
||||||
_somaTempoOperacional_s >
|
_somaTempoOperacionalJanela_s >
|
||||||
JanelaOperacionalTempo_s
|
JanelaOperacionalTempo_s
|
||||||
) &&
|
) &&
|
||||||
_janelaOperacional.Count > 0
|
_janelaOperacional.Count > 1
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var old =
|
var antiga = _janelaOperacional.Dequeue();
|
||||||
_janelaOperacional.Dequeue();
|
|
||||||
|
|
||||||
_somaDistanciaOperacional_m -=
|
_somaDistanciaOperacionalJanela_m -= antiga.dist_m;
|
||||||
old.dist_m;
|
_somaTempoOperacionalJanela_s -= antiga.tempo_s;
|
||||||
|
_somaVolumeOperacionalJanela_L -= antiga.vol_L;
|
||||||
_somaTempoOperacional_s -=
|
|
||||||
old.tempo_s;
|
|
||||||
|
|
||||||
_somaVolumeOperacional_L -=
|
|
||||||
old.vol_L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
_somaDistanciaOperacionalJanela_m =
|
||||||
_somaDistanciaOperacional_m >
|
Math.Max(0, _somaDistanciaOperacionalJanela_m);
|
||||||
MinDistanciaTaxa_m
|
|
||||||
)
|
_somaTempoOperacionalJanela_s =
|
||||||
|
Math.Max(0, _somaTempoOperacionalJanela_s);
|
||||||
|
|
||||||
|
_somaVolumeOperacionalJanela_L =
|
||||||
|
Math.Max(0, _somaVolumeOperacionalJanela_L);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Média global da operação.
|
||||||
|
*/
|
||||||
|
if (_somaDistanciaOperacionalGlobal_m >=
|
||||||
|
MinDistanciaTaxa_m)
|
||||||
{
|
{
|
||||||
LPorMetroOperacional =
|
double taxaGlobal =
|
||||||
_somaVolumeOperacional_L /
|
_somaVolumeOperacionalGlobal_L /
|
||||||
_somaDistanciaOperacional_m;
|
_somaDistanciaOperacionalGlobal_m;
|
||||||
|
|
||||||
|
if (TaxaLPorMetroOperacionalValida(taxaGlobal))
|
||||||
|
{
|
||||||
|
LPorMetroOperacionalGlobal = taxaGlobal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
/*
|
||||||
deltaDist_m > 0 &&
|
* Média da janela recente.
|
||||||
_somaDistanciaOperacional_m >=
|
*/
|
||||||
|
if (_somaDistanciaOperacionalJanela_m >=
|
||||||
|
MinDistanciaTaxa_m)
|
||||||
|
{
|
||||||
|
double taxaJanela =
|
||||||
|
_somaVolumeOperacionalJanela_L /
|
||||||
|
_somaDistanciaOperacionalJanela_m;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A propriedade antiga continua representando a janela.
|
||||||
|
*/
|
||||||
|
LPorMetroOperacional = taxaJanela;
|
||||||
|
LPorMetroOperacionalJanela = taxaJanela;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoricoOperacionalGlobalValido =
|
||||||
|
_somaDistanciaOperacionalGlobal_m >=
|
||||||
MinDistanciaHistoricoOperacional_m &&
|
MinDistanciaHistoricoOperacional_m &&
|
||||||
LPorMetroOperacional > 0
|
TaxaLPorMetroOperacionalValida(
|
||||||
|
LPorMetroOperacionalGlobal
|
||||||
|
);
|
||||||
|
|
||||||
|
HistoricoOperacionalRecenteValido =
|
||||||
|
_somaDistanciaOperacionalJanela_m >=
|
||||||
|
MinDistanciaHistoricoOperacional_m &&
|
||||||
|
TaxaLPorMetroOperacionalValida(
|
||||||
|
LPorMetroOperacionalJanela
|
||||||
|
);
|
||||||
|
|
||||||
|
_distanciaDesdeAtualizacaoEmaOperacional_m +=
|
||||||
|
deltaDist_m;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Atualiza a EMA apenas a cada 5 m.
|
||||||
|
* Isso evita aplicar peso repetido à mesma janela sobreposta.
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
HistoricoOperacionalRecenteValido &&
|
||||||
|
(
|
||||||
|
_distanciaDesdeAtualizacaoEmaOperacional_m >=
|
||||||
|
PassoAtualizacaoEmaOperacional_m ||
|
||||||
|
!TaxaLPorMetroOperacionalValida(
|
||||||
|
LPorMetroOperacionalRecenteEma
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LPorMetroOperacionalHistorico =
|
AtualizarEmaOperacionalRecente(
|
||||||
LPorMetroOperacionalHistorico <= 0
|
LPorMetroOperacionalJanela
|
||||||
? LPorMetroOperacional
|
);
|
||||||
: LPorMetroOperacionalHistorico *
|
|
||||||
(1.0 - AlphaHistorico) +
|
_distanciaDesdeAtualizacaoEmaOperacional_m = 0;
|
||||||
LPorMetroOperacional *
|
}
|
||||||
AlphaHistorico;
|
|
||||||
|
|
||||||
AutonomiaOperacionalComHistorico =
|
AutonomiaOperacionalComHistorico =
|
||||||
true;
|
HistoricoOperacionalGlobalValido &&
|
||||||
|
HistoricoOperacionalRecenteValido &&
|
||||||
|
TaxaLPorMetroOperacionalValida(
|
||||||
|
LPorMetroOperacionalRecenteEma
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AtualizarEmaOperacionalRecente(double taxaAtual_LPorMetro)
|
||||||
|
{
|
||||||
|
if (!TaxaLPorMetroOperacionalValida(
|
||||||
|
taxaAtual_LPorMetro))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TaxaLPorMetroOperacionalValida(
|
||||||
|
LPorMetroOperacionalRecenteEma))
|
||||||
|
{
|
||||||
|
LPorMetroOperacionalRecenteEma =
|
||||||
|
taxaAtual_LPorMetro;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double alpha =
|
||||||
|
taxaAtual_LPorMetro >
|
||||||
|
LPorMetroOperacionalRecenteEma
|
||||||
|
? AlphaEmaOperacionalSubida
|
||||||
|
: AlphaEmaOperacionalDescida;
|
||||||
|
|
||||||
|
LPorMetroOperacionalRecenteEma =
|
||||||
|
LPorMetroOperacionalRecenteEma *
|
||||||
|
(1.0 - alpha) +
|
||||||
|
taxaAtual_LPorMetro *
|
||||||
|
alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TaxaLPorMetroOperacionalValida(double taxa_LPorMetro)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
ValorFinito(taxa_LPorMetro) &&
|
||||||
|
taxa_LPorMetro >=
|
||||||
|
MinLPorMetroOperacionalValido &&
|
||||||
|
taxa_LPorMetro <=
|
||||||
|
MaxLPorMetroOperacionalValido;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ValorFinito(double valor)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
!double.IsNaN(valor) &&
|
||||||
|
!double.IsInfinity(valor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -1247,33 +1406,100 @@ namespace AgroBase.Services
|
||||||
|
|
||||||
private double CalcularLPorMetroOperacionalReferencia()
|
private double CalcularLPorMetroOperacionalReferencia()
|
||||||
{
|
{
|
||||||
if (
|
if (AutonomiaOperacionalComHistorico)
|
||||||
AutonomiaOperacionalComHistorico &&
|
|
||||||
LPorMetroOperacionalHistorico > 0
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
FonteAutonomiaOperacional =
|
FonteAutonomiaOperacional =
|
||||||
"historico_operacional";
|
"historico_global_recente";
|
||||||
|
|
||||||
|
double global =
|
||||||
|
LPorMetroOperacionalGlobal;
|
||||||
|
|
||||||
|
double recente =
|
||||||
|
LPorMetroOperacionalRecenteEma;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Usa o maior entre a janela recente e o histórico suavizado.
|
* Se a situação recente estiver pior, ela ganha mais peso.
|
||||||
* Assim uma região recente mais carregada de ervas eleva
|
* O aumento é progressivo e limitado a 60%.
|
||||||
* imediatamente a estimativa, mas uma região limpa não
|
|
||||||
* derruba a autonomia de forma otimista demais.
|
|
||||||
*/
|
*/
|
||||||
|
double razaoRecenteGlobal =
|
||||||
|
recente /
|
||||||
|
Math.Max(
|
||||||
|
global,
|
||||||
|
MinLPorMetroOperacionalValido
|
||||||
|
);
|
||||||
|
|
||||||
|
if (razaoRecenteGlobal > 1.0)
|
||||||
|
{
|
||||||
|
double agravamento =
|
||||||
|
Clamp(
|
||||||
|
razaoRecenteGlobal - 1.0,
|
||||||
|
0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
PesoRecenteOperacionalAplicado =
|
||||||
|
PesoRecenteOperacionalNormal +
|
||||||
|
agravamento *
|
||||||
|
(
|
||||||
|
PesoRecenteOperacionalMaximo -
|
||||||
|
PesoRecenteOperacionalNormal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Um trecho recente mais limpo pode melhorar a autonomia,
|
||||||
|
* mas recebe apenas 30% e a EMA já desce lentamente.
|
||||||
|
*/
|
||||||
|
PesoRecenteOperacionalAplicado =
|
||||||
|
PesoRecenteOperacionalNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
double pesoGlobal =
|
||||||
|
1.0 -
|
||||||
|
PesoRecenteOperacionalAplicado;
|
||||||
|
|
||||||
|
LPorMetroOperacionalCombinado =
|
||||||
|
global * pesoGlobal +
|
||||||
|
recente * PesoRecenteOperacionalAplicado;
|
||||||
|
|
||||||
|
LPorMetroOperacionalHistorico =
|
||||||
|
LPorMetroOperacionalCombinado;
|
||||||
|
|
||||||
|
return LPorMetroOperacionalCombinado;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HistoricoOperacionalGlobalValido)
|
||||||
|
{
|
||||||
|
FonteAutonomiaOperacional =
|
||||||
|
"historico_global_inicial";
|
||||||
|
|
||||||
|
PesoRecenteOperacionalAplicado = 0;
|
||||||
|
|
||||||
|
LPorMetroOperacionalCombinado =
|
||||||
|
LPorMetroOperacionalGlobal;
|
||||||
|
|
||||||
|
LPorMetroOperacionalHistorico =
|
||||||
|
LPorMetroOperacionalGlobal;
|
||||||
|
|
||||||
return Math.Max(
|
return Math.Max(
|
||||||
LPorMetroOperacional,
|
LPorMetroOperacionalGlobal,
|
||||||
LPorMetroOperacionalHistorico
|
LPorMetroBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LPorMetroOperacional > 0)
|
if (TaxaLPorMetroOperacionalValida(
|
||||||
|
LPorMetroOperacionalJanela))
|
||||||
{
|
{
|
||||||
FonteAutonomiaOperacional =
|
FonteAutonomiaOperacional =
|
||||||
"janela_operacional_inicial";
|
"janela_operacional_inicial";
|
||||||
|
|
||||||
|
PesoRecenteOperacionalAplicado = 0;
|
||||||
|
LPorMetroOperacionalCombinado = 0;
|
||||||
|
LPorMetroOperacionalHistorico = 0;
|
||||||
|
|
||||||
return Math.Max(
|
return Math.Max(
|
||||||
LPorMetroOperacional,
|
LPorMetroOperacionalJanela,
|
||||||
LPorMetroBase
|
LPorMetroBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1281,6 +1507,10 @@ namespace AgroBase.Services
|
||||||
FonteAutonomiaOperacional =
|
FonteAutonomiaOperacional =
|
||||||
"fallback_campo";
|
"fallback_campo";
|
||||||
|
|
||||||
|
PesoRecenteOperacionalAplicado = 0;
|
||||||
|
LPorMetroOperacionalCombinado = 0;
|
||||||
|
LPorMetroOperacionalHistorico = 0;
|
||||||
|
|
||||||
return LPorMetroBase;
|
return LPorMetroBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1567,23 +1797,38 @@ namespace AgroBase.Services
|
||||||
_somaTempoPulverizacao_s = 0;
|
_somaTempoPulverizacao_s = 0;
|
||||||
_somaVolumePulverizacaoTempo_L = 0;
|
_somaVolumePulverizacaoTempo_L = 0;
|
||||||
|
|
||||||
_somaDistanciaOperacional_m = 0;
|
_somaDistanciaOperacionalGlobal_m = 0;
|
||||||
_somaTempoOperacional_s = 0;
|
_somaTempoOperacionalGlobal_s = 0;
|
||||||
_somaVolumeOperacional_L = 0;
|
_somaVolumeOperacionalGlobal_L = 0;
|
||||||
|
|
||||||
|
_somaDistanciaOperacionalJanela_m = 0;
|
||||||
|
_somaTempoOperacionalJanela_s = 0;
|
||||||
|
_somaVolumeOperacionalJanela_L = 0;
|
||||||
|
|
||||||
|
_distanciaDesdeAtualizacaoEmaOperacional_m = 0;
|
||||||
|
|
||||||
|
LPorMetroOperacional = 0;
|
||||||
|
LPorMetroOperacionalGlobal = 0;
|
||||||
|
LPorMetroOperacionalJanela = 0;
|
||||||
|
LPorMetroOperacionalRecenteEma = 0;
|
||||||
|
LPorMetroOperacionalCombinado = 0;
|
||||||
|
LPorMetroOperacionalHistorico = 0;
|
||||||
|
|
||||||
|
PesoRecenteOperacionalAplicado = 0;
|
||||||
|
|
||||||
|
HistoricoOperacionalGlobalValido = false;
|
||||||
|
HistoricoOperacionalRecenteValido = false;
|
||||||
|
|
||||||
|
AutonomiaOperacionalComHistorico = false;
|
||||||
|
FonteAutonomiaOperacional = "fallback_campo";
|
||||||
|
|
||||||
LPorMetro = 0;
|
LPorMetro = 0;
|
||||||
LPorMetroHistorico = 0;
|
LPorMetroHistorico = 0;
|
||||||
|
|
||||||
LPorMetroOperacional = 0;
|
|
||||||
LPorMetroOperacionalHistorico = 0;
|
|
||||||
|
|
||||||
LPorMetroReferenciaOperacional = 0;
|
LPorMetroReferenciaOperacional = 0;
|
||||||
LPorMetroReferenciaPulverizacaoContinua = 0;
|
LPorMetroReferenciaPulverizacaoContinua = 0;
|
||||||
|
|
||||||
LPorMinuto = 0;
|
LPorMinuto = 0;
|
||||||
|
|
||||||
AutonomiaOperacionalComHistorico = false;
|
|
||||||
FonteAutonomiaOperacional = "fallback_campo";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double Clamp(
|
private static double Clamp(
|
||||||
|
|
|
||||||
|
|
@ -223,11 +223,12 @@ namespace AgroBase.Services
|
||||||
* Mantemos a compatibilidade com o fluxo atual:
|
* Mantemos a compatibilidade com o fluxo atual:
|
||||||
* a seleção usa IDs internos começando em zero.
|
* a seleção usa IDs internos começando em zero.
|
||||||
*/
|
*/
|
||||||
feature.geometry.id = i.ToString();
|
string id = (i + 1).ToString();
|
||||||
|
feature.geometry.id = id;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(feature.properties.Id))
|
if (string.IsNullOrWhiteSpace(feature.properties.Id))
|
||||||
{
|
{
|
||||||
feature.properties.Id = (i + 1).ToString();
|
feature.properties.Id = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,7 @@ namespace AgroBase.Services.Operadores
|
||||||
("configurado", true),
|
("configurado", true),
|
||||||
("modo", op.Parametros?.Modo ?? ModoOperacao.NaoDefinido),
|
("modo", op.Parametros?.Modo ?? ModoOperacao.NaoDefinido),
|
||||||
("tempo_aguardar_inicio_operacao", op.TempoIniciarOperacao),
|
("tempo_aguardar_inicio_operacao", op.TempoIniciarOperacao),
|
||||||
|
("tempo_aguardar_retomada", op.TempoAguardarRetomada),
|
||||||
("modulos_mandatorios", _Parametros.ModulosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()),
|
("modulos_mandatorios", _Parametros.ModulosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()),
|
||||||
("modulos_opcionais", _Parametros.ModulosMandatorios.Where(x => !x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()),
|
("modulos_opcionais", _Parametros.ModulosMandatorios.Where(x => !x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()),
|
||||||
("parametros_mandatorios", _Parametros.ParametrosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Parametro).ToArray()),
|
("parametros_mandatorios", _Parametros.ParametrosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Parametro).ToArray()),
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,18 @@ namespace AgroBase.Services
|
||||||
private float positionX = 0.0f;
|
private float positionX = 0.0f;
|
||||||
private float positionY = 0.0f;
|
private float positionY = 0.0f;
|
||||||
private bool isMiddleButtonDragging = false;
|
private bool isMiddleButtonDragging = false;
|
||||||
private double AnguloX { get; set; }
|
|
||||||
private double AnguloY { get; set; }
|
private double AnguloY { get; set; }
|
||||||
|
private double AnguloX { get; set; }
|
||||||
private double AnguloZ { get; set; }
|
private double AnguloZ { get; set; }
|
||||||
private string Path3D { get; set; }
|
private string Path3D { get; set; }
|
||||||
|
|
||||||
public void AtualizarAngulos(double X, double Y, double Z, double Tolerance = 1.0)
|
public void AtualizarAngulos(double Lateral, double Frontal, double Rotacao, double Tolerance = 1.0)
|
||||||
{
|
{
|
||||||
if (Math.Abs(AnguloX - X) > Tolerance || Math.Abs(AnguloY - Y) > Tolerance || Math.Abs(AnguloZ - Z) > Tolerance)
|
if (Math.Abs(AnguloX - Frontal) > Tolerance || Math.Abs(AnguloY - Lateral) > Tolerance || Math.Abs(AnguloZ - Rotacao) > Tolerance)
|
||||||
{
|
{
|
||||||
AnguloX = X;
|
AnguloX = Frontal;
|
||||||
AnguloY = Y;
|
AnguloY = -Lateral;
|
||||||
AnguloZ = Z;
|
AnguloZ = -Rotacao;
|
||||||
glControl.Invalidate();
|
glControl.Invalidate();
|
||||||
glControl.SwapBuffers();
|
glControl.SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
@ -158,8 +158,8 @@ namespace AgroBase.Services
|
||||||
DrawText("O", -10.0f, 0.0f, -0.05f);
|
DrawText("O", -10.0f, 0.0f, -0.05f);
|
||||||
|
|
||||||
// Aplicar inclinação ao objeto 3D
|
// Aplicar inclinação ao objeto 3D
|
||||||
GL.Rotate(AnguloX, 0.0, 0.0, 1.0);
|
GL.Rotate(AnguloY, 0.0, 0.0, 1.0);
|
||||||
GL.Rotate(AnguloY, 1.0, 0.0, 0.0);
|
GL.Rotate(AnguloX, 1.0, 0.0, 0.0);
|
||||||
GL.Rotate(AnguloZ - 180, 0.0, 1.0, 0.0);
|
GL.Rotate(AnguloZ - 180, 0.0, 1.0, 0.0);
|
||||||
|
|
||||||
// Desenhar o objeto 3D
|
// Desenhar o objeto 3D
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
raw_acc_alpha=0.04,
|
raw_acc_alpha=0.04,
|
||||||
raw_max_acc_erro_g=0.18,
|
raw_max_acc_erro_g=0.18,
|
||||||
referencial_robo="r,p,y",
|
referencial_robo="r,p,y",
|
||||||
|
sinal_roll=1.0,
|
||||||
|
sinal_pitch=1.0,
|
||||||
|
sinal_yaw=1.0,
|
||||||
):
|
):
|
||||||
self.mx_id = mx_id
|
self.mx_id = mx_id
|
||||||
self.imu_queue = queue
|
self.imu_queue = queue
|
||||||
|
|
@ -139,6 +142,9 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
self._calib_pitch_hist = deque(maxlen=200)
|
self._calib_pitch_hist = deque(maxlen=200)
|
||||||
|
|
||||||
self.referencial_robo = str(referencial_robo or "r,p,y").lower().replace(" ", "")
|
self.referencial_robo = str(referencial_robo or "r,p,y").lower().replace(" ", "")
|
||||||
|
self.sinal_roll = -1.0 if float(sinal_roll) < 0 else 1.0
|
||||||
|
self.sinal_pitch = -1.0 if float(sinal_pitch) < 0 else 1.0
|
||||||
|
self.sinal_yaw = -1.0 if float(sinal_yaw) < 0 else 1.0
|
||||||
self.matriz_sensor_para_robo = (self._criar_matriz_sensor_para_robo(self.referencial_robo))
|
self.matriz_sensor_para_robo = (self._criar_matriz_sensor_para_robo(self.referencial_robo))
|
||||||
|
|
||||||
if queue is None:
|
if queue is None:
|
||||||
|
|
@ -455,8 +461,10 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
self.yaw_filtrado_deg = yaw
|
self.yaw_filtrado_deg = yaw
|
||||||
return
|
return
|
||||||
|
|
||||||
self.roll_filtrado_deg = (1.0 - a) * self.roll_filtrado_deg + a * roll
|
droll = self._wrap_angle_delta_deg(roll, self.roll_filtrado_deg)
|
||||||
self.pitch_filtrado_deg = (1.0 - a) * self.pitch_filtrado_deg + a * pitch
|
dpitch = self._wrap_angle_delta_deg(pitch, self.pitch_filtrado_deg)
|
||||||
|
self.roll_filtrado_deg = self._normalizar_angulo_deg(self.roll_filtrado_deg + a * droll)
|
||||||
|
self.pitch_filtrado_deg = self._normalizar_angulo_deg(self.pitch_filtrado_deg + a * dpitch)
|
||||||
|
|
||||||
# Yaw com wrap
|
# Yaw com wrap
|
||||||
dyaw = self._wrap_angle_delta_deg(yaw, self.yaw_filtrado_deg)
|
dyaw = self._wrap_angle_delta_deg(yaw, self.yaw_filtrado_deg)
|
||||||
|
|
@ -575,6 +583,9 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
return R.from_matrix(matriz_robo)
|
return R.from_matrix(matriz_robo)
|
||||||
|
|
||||||
|
def _normalizar_angulo_deg(self, angulo):
|
||||||
|
return (float(angulo) + 180.0) % 360.0 - 180.0
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# Calibração
|
# Calibração
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
@ -928,27 +939,34 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
# Correção por accelerômetro apenas quando parece gravidade.
|
# Correção por accelerômetro apenas quando parece gravidade.
|
||||||
if self._raw_acc_confiavel(a):
|
if self._raw_acc_confiavel(a):
|
||||||
alpha_acc = self.raw_acc_alpha
|
alpha_acc = self.raw_acc_alpha
|
||||||
self.raw_roll_abs_deg = (1.0 - alpha_acc) * roll_pred + alpha_acc * roll_acc_abs
|
|
||||||
self.raw_pitch_abs_deg = (1.0 - alpha_acc) * pitch_pred + alpha_acc * pitch_acc_abs
|
erro_roll = self._wrap_angle_delta_deg(roll_acc_abs, roll_pred)
|
||||||
|
self.raw_roll_abs_deg = self._normalizar_angulo_deg(roll_pred + alpha_acc * erro_roll)
|
||||||
|
|
||||||
|
erro_pitch = self._wrap_angle_delta_deg(pitch_acc_abs, pitch_pred)
|
||||||
|
self.raw_pitch_abs_deg = self._normalizar_angulo_deg(pitch_pred + alpha_acc * erro_pitch)
|
||||||
else:
|
else:
|
||||||
self.raw_roll_abs_deg = roll_pred
|
self.raw_roll_abs_deg = roll_pred
|
||||||
self.raw_pitch_abs_deg = pitch_pred
|
self.raw_pitch_abs_deg = pitch_pred
|
||||||
|
|
||||||
self.raw_yaw_abs_deg = (yaw_pred + 180.0) % 360.0 - 180.0
|
self.raw_yaw_abs_deg = (yaw_pred + 180.0) % 360.0 - 180.0
|
||||||
|
|
||||||
roll = self.raw_roll_abs_deg - self.raw_roll_offset_deg
|
roll = self._wrap_angle_delta_deg(self.raw_roll_abs_deg, self.raw_roll_offset_deg)
|
||||||
pitch = self.raw_pitch_abs_deg - self.raw_pitch_offset_deg
|
pitch = self._wrap_angle_delta_deg(self.raw_pitch_abs_deg, self.raw_pitch_offset_deg)
|
||||||
yaw = self.raw_yaw_abs_deg - self.raw_yaw_offset_deg
|
yaw = self._wrap_angle_delta_deg(self.raw_yaw_abs_deg, self.raw_yaw_offset_deg)
|
||||||
yaw = (yaw + 180.0) % 360.0 - 180.0
|
|
||||||
|
roll = self._normalizar_angulo_deg(roll * self.sinal_roll)
|
||||||
|
pitch = self._normalizar_angulo_deg(pitch * self.sinal_pitch)
|
||||||
|
yaw = self._normalizar_angulo_deg(yaw * self.sinal_yaw)
|
||||||
|
|
||||||
self.roll_deg = float(roll)
|
self.roll_deg = float(roll)
|
||||||
self.pitch_deg = float(pitch)
|
self.pitch_deg = float(pitch)
|
||||||
self.yaw_deg = float(yaw)
|
self.yaw_deg = float(yaw)
|
||||||
|
|
||||||
# Para raw, é melhor usar o gyro como rate direto.
|
# Para raw, é melhor usar o gyro como rate direto.
|
||||||
self.roll_rate_dps = gx
|
self.roll_rate_dps = gx * self.sinal_roll
|
||||||
self.pitch_rate_dps = gy
|
self.pitch_rate_dps = gy * self.sinal_pitch
|
||||||
self.yaw_rate_dps = gz
|
self.yaw_rate_dps = gz * self.sinal_yaw
|
||||||
|
|
||||||
self._last_angle_ts = ts_integracao
|
self._last_angle_ts = ts_integracao
|
||||||
self._last_roll_deg = roll
|
self._last_roll_deg = roll
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,7 @@ class CameraMultispectral:
|
||||||
|
|
||||||
self.imu_freq_hz = 200
|
self.imu_freq_hz = 200
|
||||||
self.imu_publish_hz = 50
|
self.imu_publish_hz = 50
|
||||||
self.imu_sensor_type = "GAME_ROTATION_VECTOR"
|
self.imu_modo = "raw_6axis" # rotation_vector
|
||||||
self.imu_modo = "rotation_vector"
|
|
||||||
|
|
||||||
self.iniciado = False
|
self.iniciado = False
|
||||||
self.rodando = False
|
self.rodando = False
|
||||||
|
|
@ -149,6 +148,9 @@ class CameraMultispectral:
|
||||||
module_calibration_json=self.module_calibration_json,
|
module_calibration_json=self.module_calibration_json,
|
||||||
sync_mode="best",
|
sync_mode="best",
|
||||||
sync_tolerance_ms=25.0,
|
sync_tolerance_ms=25.0,
|
||||||
|
|
||||||
|
imu_modo=self.imu_modo,
|
||||||
|
imu_freq_hz=self.imu_freq_hz,
|
||||||
)
|
)
|
||||||
|
|
||||||
resp = self.client.start(print_debug=False)
|
resp = self.client.start(print_debug=False)
|
||||||
|
|
@ -178,8 +180,11 @@ class CameraMultispectral:
|
||||||
max_queue_drain=20,
|
max_queue_drain=20,
|
||||||
nome_sensor=f"{self.modelo}_imu",
|
nome_sensor=f"{self.modelo}_imu",
|
||||||
modo=self.imu_modo,
|
modo=self.imu_modo,
|
||||||
gyro_unidade="deg_s",
|
gyro_unidade="rad_s",
|
||||||
referencial_robo="-r,p,-y",
|
referencial_robo="r,p,y",
|
||||||
|
sinal_roll=1.0,
|
||||||
|
sinal_pitch=-1.0,
|
||||||
|
sinal_yaw=-1.0,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.tem_imu = False
|
self.tem_imu = False
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,12 @@ class CameraOak:
|
||||||
self.timestamp_ultima_segmentacao = None
|
self.timestamp_ultima_segmentacao = None
|
||||||
self.tem_depth = False
|
self.tem_depth = False
|
||||||
self.tem_imu = False
|
self.tem_imu = False
|
||||||
|
self.iniciar_imu = bool(iniciar_imu)
|
||||||
self.imu = None
|
self.imu = None
|
||||||
self.imu_freq_hz = 200
|
self.imu_freq_hz = 200
|
||||||
self.imu_publish_hz = 50
|
self.imu_publish_hz = 50
|
||||||
self.imu_sensor_type = "GAME_ROTATION_VECTOR"
|
self.imu_modo = "raw_6axis"
|
||||||
|
self.imu_sensor_type = None
|
||||||
self.rodando = False
|
self.rodando = False
|
||||||
self.iniciado = False
|
self.iniciado = False
|
||||||
|
|
||||||
|
|
@ -139,6 +141,9 @@ class CameraOak:
|
||||||
modo=self.imu_modo,
|
modo=self.imu_modo,
|
||||||
gyro_unidade="rad_s",
|
gyro_unidade="rad_s",
|
||||||
referencial_robo="y,r,p",
|
referencial_robo="y,r,p",
|
||||||
|
sinal_roll=1.0,
|
||||||
|
sinal_pitch=1.0,
|
||||||
|
sinal_yaw=-1.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.modelo_ia_seg is not None:
|
if self.modelo_ia_seg is not None:
|
||||||
|
|
@ -795,30 +800,57 @@ class CameraOak:
|
||||||
self.mostrar_log(f"[WARN] Falha ao montar pipeline depth: {e}")
|
self.mostrar_log(f"[WARN] Falha ao montar pipeline depth: {e}")
|
||||||
|
|
||||||
# IMU (somente se presente)
|
# IMU (somente se presente)
|
||||||
if self.tem_imu:
|
if self.tem_imu and self.iniciar_imu:
|
||||||
try:
|
try:
|
||||||
imu = pipeline.create(dai.node.IMU)
|
imu = pipeline.create(dai.node.IMU)
|
||||||
|
|
||||||
imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, self.imu_freq_hz)
|
if self.imu_modo == "raw_6axis":
|
||||||
imu.enableIMUSensor(dai.IMUSensor.GYROSCOPE_RAW, self.imu_freq_hz)
|
imu.enableIMUSensor(
|
||||||
|
dai.IMUSensor.ACCELEROMETER_RAW,
|
||||||
|
self.imu_freq_hz,
|
||||||
|
)
|
||||||
|
imu.enableIMUSensor(
|
||||||
|
dai.IMUSensor.GYROSCOPE_RAW,
|
||||||
|
self.imu_freq_hz,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.imu_sensor_type = (
|
||||||
|
"ACCELEROMETER_RAW_GYROSCOPE_RAW"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif self.imu_modo == "rotation_vector":
|
||||||
|
imu.enableIMUSensor(
|
||||||
|
dai.IMUSensor.GAME_ROTATION_VECTOR,
|
||||||
|
self.imu_freq_hz,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.imu_sensor_type = "GAME_ROTATION_VECTOR"
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"imu_modo inválido: {self.imu_modo!r}. "
|
||||||
|
"Use 'raw_6axis' ou 'rotation_vector'."
|
||||||
|
)
|
||||||
|
|
||||||
imu.setBatchReportThreshold(1)
|
imu.setBatchReportThreshold(1)
|
||||||
imu.setMaxBatchReports(5)
|
imu.setMaxBatchReports(5)
|
||||||
|
|
||||||
xoutImu = pipeline.create(dai.node.XLinkOut)
|
xout_imu = pipeline.create(dai.node.XLinkOut)
|
||||||
xoutImu.setStreamName("imu")
|
xout_imu.setStreamName("imu")
|
||||||
imu.out.link(xoutImu.input)
|
imu.out.link(xout_imu.input)
|
||||||
|
|
||||||
self.imu_sensor_type = "ACCELEROMETER_RAW_GYROSCOPE_RAW"
|
|
||||||
self.imu_modo = "raw_6axis"
|
|
||||||
|
|
||||||
self.mostrar_log(
|
self.mostrar_log(
|
||||||
f"Pipeline imu criado | sensor={self.imu_sensor_type} | freq={self.imu_freq_hz}Hz"
|
"Pipeline IMU criado"
|
||||||
|
f" | modo={self.imu_modo}"
|
||||||
|
f" | sensor={self.imu_sensor_type}"
|
||||||
|
f" | freq={self.imu_freq_hz}Hz"
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.tem_imu = False
|
self.tem_imu = False
|
||||||
self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}")
|
self.mostrar_log(
|
||||||
|
f"[WARN] Falha ao montar pipeline IMU: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
script = None
|
script = None
|
||||||
if self.modelo_ia_seg is not None or self.modelo_ia_det is not None:
|
if self.modelo_ia_seg is not None or self.modelo_ia_det is not None:
|
||||||
|
|
|
||||||
|
|
@ -393,12 +393,12 @@ def main():
|
||||||
|
|
||||||
ap.add_argument(
|
ap.add_argument(
|
||||||
"--module_params",
|
"--module_params",
|
||||||
default=r"C:\ZendionInc\agrobot_base\Python\OAK\datasets\oak-fcc-3\calibration\module_params.json",
|
default=r"C:\AgroBaseModels\Ervas\modelmp-4_0.json",
|
||||||
help="Caminho do module_params.json.",
|
help="Caminho do module_params.json.",
|
||||||
)
|
)
|
||||||
|
|
||||||
ap.add_argument("--width", type=int, default=1024, help="Largura final do tensor.")
|
ap.add_argument("--width", type=int, default=640, help="Largura final do tensor.")
|
||||||
ap.add_argument("--height", type=int, default=640, help="Altura final do tensor.")
|
ap.add_argument("--height", type=int, default=400, help="Altura final do tensor.")
|
||||||
ap.add_argument("--fps", type=float, default=30.0)
|
ap.add_argument("--fps", type=float, default=30.0)
|
||||||
ap.add_argument("--seconds", type=float, default=20.0)
|
ap.add_argument("--seconds", type=float, default=20.0)
|
||||||
ap.add_argument("--timeout", type=float, default=3.0)
|
ap.add_argument("--timeout", type=float, default=3.0)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ class OakFcc3Client:
|
||||||
sync_mode="best",
|
sync_mode="best",
|
||||||
sync_tolerance_ms=25.0,
|
sync_tolerance_ms=25.0,
|
||||||
mx_id=None,
|
mx_id=None,
|
||||||
|
imu_modo="rotation_vector",
|
||||||
|
imu_freq_hz=200,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
self.width = width
|
self.width = width
|
||||||
|
|
@ -38,6 +40,9 @@ class OakFcc3Client:
|
||||||
self.module_params = self._load_module_params(module_calibration_json)
|
self.module_params = self._load_module_params(module_calibration_json)
|
||||||
self.fusion_config = self.module_params.get("fusion_config", {}) or {}
|
self.fusion_config = self.module_params.get("fusion_config", {}) or {}
|
||||||
|
|
||||||
|
self.imu_modo = str(imu_modo).strip().lower()
|
||||||
|
self.imu_freq_hz = int(imu_freq_hz)
|
||||||
|
|
||||||
self.mx_id = str(mx_id) if mx_id else None
|
self.mx_id = str(mx_id) if mx_id else None
|
||||||
|
|
||||||
self.svc = OakFcc3Service(
|
self.svc = OakFcc3Service(
|
||||||
|
|
@ -53,6 +58,10 @@ class OakFcc3Client:
|
||||||
sync_tolerance_ms=sync_tolerance_ms,
|
sync_tolerance_ms=sync_tolerance_ms,
|
||||||
mx_id=self.mx_id,
|
mx_id=self.mx_id,
|
||||||
module_calibration_json=module_calibration_json,
|
module_calibration_json=module_calibration_json,
|
||||||
|
|
||||||
|
imu_modo=self.imu_modo,
|
||||||
|
imu_freq_hz=self.imu_freq_hz,
|
||||||
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ class OakFcc3Manager:
|
||||||
mx_id=None,
|
mx_id=None,
|
||||||
module_calibration_json=None,
|
module_calibration_json=None,
|
||||||
module_params=None,
|
module_params=None,
|
||||||
|
imu_modo="rotation_vector",
|
||||||
|
imu_freq_hz=200,
|
||||||
):
|
):
|
||||||
self.fps = fps
|
self.fps = fps
|
||||||
|
|
||||||
|
|
@ -86,6 +88,16 @@ class OakFcc3Manager:
|
||||||
self.buffers = {}
|
self.buffers = {}
|
||||||
self.camera_info = {}
|
self.camera_info = {}
|
||||||
|
|
||||||
|
self.imu_modo = self._validate_imu_modo(imu_modo)
|
||||||
|
self.imu_freq_hz = int(imu_freq_hz)
|
||||||
|
|
||||||
|
if self.imu_freq_hz <= 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"imu_freq_hz deve ser maior que zero: {self.imu_freq_hz}"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.imu_sensor_type = None
|
||||||
|
|
||||||
self.has_imu_pipeline = False
|
self.has_imu_pipeline = False
|
||||||
self.tem_imu = False
|
self.tem_imu = False
|
||||||
self.q_imu = None
|
self.q_imu = None
|
||||||
|
|
@ -335,19 +347,40 @@ class OakFcc3Manager:
|
||||||
|
|
||||||
def _create_imu_node(self, pipeline):
|
def _create_imu_node(self, pipeline):
|
||||||
self.has_imu_pipeline = False
|
self.has_imu_pipeline = False
|
||||||
|
self.imu_sensor_type = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
imu = pipeline.create(dai.node.IMU)
|
imu = pipeline.create(dai.node.IMU)
|
||||||
|
|
||||||
# Para inclinação do rover:
|
if self.imu_modo == "raw_6axis":
|
||||||
# GAME_ROTATION_VECTOR entrega quaternion pronto com roll/pitch
|
imu.enableIMUSensor(
|
||||||
# referenciados à gravidade e sem depender de magnetômetro.
|
dai.IMUSensor.ACCELEROMETER_RAW,
|
||||||
imu.enableIMUSensor(dai.IMUSensor.GAME_ROTATION_VECTOR, 200)
|
self.imu_freq_hz,
|
||||||
|
)
|
||||||
|
imu.enableIMUSensor(
|
||||||
|
dai.IMUSensor.GYROSCOPE_RAW,
|
||||||
|
self.imu_freq_hz,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.imu_sensor_type = (
|
||||||
|
"ACCELEROMETER_RAW_GYROSCOPE_RAW"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif self.imu_modo == "rotation_vector":
|
||||||
|
imu.enableIMUSensor(
|
||||||
|
dai.IMUSensor.GAME_ROTATION_VECTOR,
|
||||||
|
self.imu_freq_hz,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.imu_sensor_type = "GAME_ROTATION_VECTOR"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Proteção adicional; normalmente o __init__ já impede isso.
|
||||||
|
raise RuntimeError(
|
||||||
|
f"imu_modo não suportado: {self.imu_modo!r}"
|
||||||
|
)
|
||||||
|
|
||||||
# Envia assim que tiver amostra.
|
|
||||||
imu.setBatchReportThreshold(1)
|
imu.setBatchReportThreshold(1)
|
||||||
|
|
||||||
# Evita rajadas grandes e reduz backlog.
|
|
||||||
imu.setMaxBatchReports(5)
|
imu.setMaxBatchReports(5)
|
||||||
|
|
||||||
xout_imu = pipeline.create(dai.node.XLinkOut)
|
xout_imu = pipeline.create(dai.node.XLinkOut)
|
||||||
|
|
@ -355,11 +388,45 @@ class OakFcc3Manager:
|
||||||
imu.out.link(xout_imu.input)
|
imu.out.link(xout_imu.input)
|
||||||
|
|
||||||
self.has_imu_pipeline = True
|
self.has_imu_pipeline = True
|
||||||
print("[OAK] Pipeline IMU criado | GAME_ROTATION_VECTOR | 200Hz")
|
|
||||||
|
print(
|
||||||
|
"[OAK] Pipeline IMU criado"
|
||||||
|
f" | modo={self.imu_modo}"
|
||||||
|
f" | sensor={self.imu_sensor_type}"
|
||||||
|
f" | freq={self.imu_freq_hz}Hz"
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.has_imu_pipeline = False
|
self.has_imu_pipeline = False
|
||||||
print(f"[WARN] IMU indisponível no pipeline: {e}")
|
self.imu_sensor_type = None
|
||||||
|
print(
|
||||||
|
"[WARN] IMU indisponível no pipeline"
|
||||||
|
f" | modo={self.imu_modo}"
|
||||||
|
f" | freq={self.imu_freq_hz}Hz"
|
||||||
|
f" | erro={e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _validate_imu_modo(self, modo):
|
||||||
|
modo = str(modo).strip().lower()
|
||||||
|
|
||||||
|
aliases = {
|
||||||
|
"raw": "raw_6axis",
|
||||||
|
"raw_6_axis": "raw_6axis",
|
||||||
|
"raw_6axis": "raw_6axis",
|
||||||
|
"rotation": "rotation_vector",
|
||||||
|
"game_rotation_vector": "rotation_vector",
|
||||||
|
"rotation_vector": "rotation_vector",
|
||||||
|
}
|
||||||
|
|
||||||
|
modo_normalizado = aliases.get(modo)
|
||||||
|
|
||||||
|
if modo_normalizado is None:
|
||||||
|
raise ValueError(
|
||||||
|
f"imu_modo inválido: {modo!r}. "
|
||||||
|
"Use 'raw_6axis' ou 'rotation_vector'."
|
||||||
|
)
|
||||||
|
|
||||||
|
return modo_normalizado
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Pipeline creation, MULTISPEC aligned on OAK
|
# Pipeline creation, MULTISPEC aligned on OAK
|
||||||
|
|
@ -1037,6 +1104,9 @@ class OakFcc3Manager:
|
||||||
"async_capture": self.get_async_capture_status(),
|
"async_capture": self.get_async_capture_status(),
|
||||||
"tem_imu": bool(getattr(self, "tem_imu", False)),
|
"tem_imu": bool(getattr(self, "tem_imu", False)),
|
||||||
"has_imu_pipeline": bool(getattr(self, "has_imu_pipeline", False)),
|
"has_imu_pipeline": bool(getattr(self, "has_imu_pipeline", False)),
|
||||||
|
"imu_modo": self.imu_modo,
|
||||||
|
"imu_freq_hz": self.imu_freq_hz,
|
||||||
|
"imu_sensor_type": self.imu_sensor_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _serializable_aligned_geometry(self):
|
def _serializable_aligned_geometry(self):
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ class OakFcc3Service:
|
||||||
"raw_policy": self.manager.raw_policy,
|
"raw_policy": self.manager.raw_policy,
|
||||||
"sync_mode": getattr(self.manager, "sync_mode", "best"),
|
"sync_mode": getattr(self.manager, "sync_mode", "best"),
|
||||||
"sync_tolerance_ms": self.manager.sync_tolerance_ms,
|
"sync_tolerance_ms": self.manager.sync_tolerance_ms,
|
||||||
|
"imu_modo": self.manager.imu_modo,
|
||||||
|
"imu_freq_hz": self.manager.imu_freq_hz,
|
||||||
|
"imu_sensor_type": self.manager.imu_sensor_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
def set_fps(self, fps):
|
def set_fps(self, fps):
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,12 @@ def main():
|
||||||
ContextoGlobalRedis()._atualizar_mpc(do_manager=True, iniciar_operacao=False)
|
ContextoGlobalRedis()._atualizar_mpc(do_manager=True, iniciar_operacao=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
mostrar_log(f"Erro ao Iniciar MPC: {e}")
|
mostrar_log(f"Erro ao Iniciar MPC: {e}")
|
||||||
|
elif acao == ManagerWorkerCommandType.ReiniciarMPC:
|
||||||
|
try:
|
||||||
|
from manager_worker.modulos.mpc import resetar as resetar_mpc
|
||||||
|
_mpc_iniciado = resetar_mpc()
|
||||||
|
except Exception as e:
|
||||||
|
mostrar_log(f"Erro ao Resetar MPC: {e}")
|
||||||
elif acao == ManagerWorkerCommandType.AtualizarDadosControle:
|
elif acao == ManagerWorkerCommandType.AtualizarDadosControle:
|
||||||
#mostrar_log("Adicionado na fila")
|
#mostrar_log("Adicionado na fila")
|
||||||
fila.adicionar(acao)
|
fila.adicionar(acao)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import time
|
||||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import copy
|
||||||
|
|
||||||
from shared.enums import StatusCarroMapa, TipoMovimentoDirecional
|
from shared.enums import StatusCarroMapa, TipoMovimentoDirecional
|
||||||
from manager_worker.config import mostrar_log
|
from manager_worker.config import mostrar_log
|
||||||
|
|
@ -102,32 +103,128 @@ def _safe_motivos(motivos: Any) -> list:
|
||||||
_mpc = None
|
_mpc = None
|
||||||
_iniciado = False
|
_iniciado = False
|
||||||
|
|
||||||
def inicializar(parametros, mapa, p_ref, forcar):
|
def inicializar(parametros, mapa, p_ref, forcar=False):
|
||||||
"""Inicializa ou recria o controlador MPC sem derrubar a instância anterior em caso de erro."""
|
|
||||||
global _mpc, _iniciado
|
global _mpc, _iniciado
|
||||||
|
|
||||||
mapa = list(mapa or [])
|
mapa = list(mapa or [])
|
||||||
parametros = _as_dict(parametros)
|
parametros = _as_dict(parametros)
|
||||||
|
|
||||||
len_atual = _safe_len(getattr(_mpc, "pontos_info", [])) if _mpc is not None else 0
|
assinatura_nova = _assinatura_mapa(mapa, p_ref)
|
||||||
len_mapa = _safe_len(mapa)
|
|
||||||
precisa_recriar = (not _iniciado) or (_mpc is not None and len_atual != len_mapa) or bool(forcar)
|
|
||||||
|
|
||||||
_log(f"[MPC] Inicializar chamado. iniciado: {_iniciado}, len_pontosinfo: {len_atual}, len_mapa: {len_mapa}, forcar: {forcar}")
|
assinatura_atual = None
|
||||||
|
if _mpc is not None:
|
||||||
|
assinatura_atual = _assinatura_mapa(
|
||||||
|
_mpc.pontos_mapa,
|
||||||
|
_mpc.p_ref,
|
||||||
|
)
|
||||||
|
|
||||||
|
mapa_mudou = assinatura_atual != assinatura_nova
|
||||||
|
|
||||||
|
precisa_recriar = (
|
||||||
|
not _iniciado
|
||||||
|
or _mpc is None
|
||||||
|
or mapa_mudou
|
||||||
|
or bool(forcar)
|
||||||
|
)
|
||||||
|
|
||||||
|
_log(
|
||||||
|
f"[MPC] Inicializar chamado | "
|
||||||
|
f"iniciado={_iniciado} | "
|
||||||
|
f"forcar={bool(forcar)} | "
|
||||||
|
f"mapa_mudou={mapa_mudou} | "
|
||||||
|
f"pontos_novos={len(mapa)}"
|
||||||
|
)
|
||||||
|
|
||||||
if not precisa_recriar:
|
if not precisa_recriar:
|
||||||
return
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
novo_mpc = ControladorMPC(parametros_mpc=parametros, pontos_mapa=mapa, p_ref=p_ref)
|
novo_mpc = ControladorMPC(
|
||||||
|
parametros_mpc=parametros,
|
||||||
|
pontos_mapa=mapa,
|
||||||
|
p_ref=p_ref,
|
||||||
|
)
|
||||||
|
|
||||||
_mpc = novo_mpc
|
_mpc = novo_mpc
|
||||||
_iniciado = True
|
_iniciado = True
|
||||||
_log(f"[MPC] Iniciado. Trajetoria com {_safe_len(_mpc.pontos_info)} pontos")
|
|
||||||
|
_log(
|
||||||
|
f"[MPC] Nova instância criada | "
|
||||||
|
f"pontos={len(_mpc.pontos_info)} | "
|
||||||
|
f"visitados={np.count_nonzero(_mpc.visitados_execucao)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_log(f"❌ Erro ao inicializar MPC. Instância anterior preservada: {e}")
|
_log(
|
||||||
|
f"❌ Erro ao inicializar MPC. "
|
||||||
|
f"Instância anterior preservada: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
if _mpc is None:
|
if _mpc is None:
|
||||||
_iniciado = False
|
_iniciado = False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def resetar() -> bool:
|
||||||
|
global _mpc, _iniciado
|
||||||
|
|
||||||
|
mpc_atual = _mpc
|
||||||
|
|
||||||
|
if mpc_atual is None or not _iniciado:
|
||||||
|
_log("[MPC] Reset ignorado: MPC ainda não foi inicializado.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
parametros = copy.deepcopy(mpc_atual.parametros_mpc)
|
||||||
|
mapa = copy.deepcopy(mpc_atual.pontos_mapa)
|
||||||
|
p_ref = tuple(mpc_atual.p_ref)
|
||||||
|
except Exception as e:
|
||||||
|
_log(f"❌ Não foi possível obter os dados para resetar o MPC: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
inicializar(
|
||||||
|
parametros=parametros,
|
||||||
|
mapa=mapa,
|
||||||
|
p_ref=p_ref,
|
||||||
|
forcar=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
reiniciado = _mpc is not mpc_atual
|
||||||
|
|
||||||
|
if reiniciado:
|
||||||
|
_log(
|
||||||
|
f"[MPC] Reset concluído. Nova instância criada com "
|
||||||
|
f"{_safe_len(_mpc.pontos_info)} pontos."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_log("❌ Reset do MPC falhou. Instância anterior preservada.")
|
||||||
|
|
||||||
|
return reiniciado
|
||||||
|
|
||||||
|
def _assinatura_mapa(mapa, p_ref):
|
||||||
|
pontos = []
|
||||||
|
|
||||||
|
for ponto in mapa or []:
|
||||||
|
xy = ponto.get("xy", (0.0, 0.0))
|
||||||
|
|
||||||
|
pontos.append((
|
||||||
|
round(_safe_float(xy[0]), 3),
|
||||||
|
round(_safe_float(xy[1]), 3),
|
||||||
|
_safe_int(ponto.get("tipo", 0)),
|
||||||
|
round(_safe_float(
|
||||||
|
ponto.get("distanciaMargem", 0.7)
|
||||||
|
), 3),
|
||||||
|
))
|
||||||
|
|
||||||
|
referencia = (
|
||||||
|
round(_safe_float(p_ref[0]), 8),
|
||||||
|
round(_safe_float(p_ref[1]), 8),
|
||||||
|
)
|
||||||
|
|
||||||
|
return referencia, tuple(pontos)
|
||||||
|
|
||||||
def get_mpc():
|
def get_mpc():
|
||||||
global _mpc
|
global _mpc
|
||||||
return _mpc
|
return _mpc
|
||||||
|
|
@ -149,13 +246,23 @@ class ControladorMPC:
|
||||||
- mesma estrutura de `pontos_info` esperada pelo MPC.
|
- mesma estrutura de `pontos_info` esperada pelo MPC.
|
||||||
"""
|
"""
|
||||||
parametros_mpc = _as_dict(parametros_mpc)
|
parametros_mpc = _as_dict(parametros_mpc)
|
||||||
self.pontos_info = list(pontos_mapa or [])
|
|
||||||
|
|
||||||
if not isinstance(p_ref, (list, tuple)) or len(p_ref) < 2:
|
if not isinstance(p_ref, (list, tuple)) or len(p_ref) < 2:
|
||||||
raise ValueError("p_ref inválido. Esperado (lat0, lon0).")
|
raise ValueError("p_ref inválido. Esperado (lat0, lon0).")
|
||||||
|
|
||||||
self.lat0 = _safe_float(p_ref[0])
|
# Fotografia da configuração usada para criar esta instância.
|
||||||
self.lon0 = _safe_float(p_ref[1])
|
self.parametros_mpc = copy.deepcopy(parametros_mpc)
|
||||||
|
self.pontos_mapa = copy.deepcopy(list(pontos_mapa or []))
|
||||||
|
self.p_ref = (
|
||||||
|
_safe_float(p_ref[0]),
|
||||||
|
_safe_float(p_ref[1]),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dados de trabalho desta instância.
|
||||||
|
self.pontos_info = copy.deepcopy(self.pontos_mapa)
|
||||||
|
|
||||||
|
self.lat0 = self.p_ref[0]
|
||||||
|
self.lon0 = self.p_ref[1]
|
||||||
self.gps_handler = GPSHandler(self.lat0, self.lon0)
|
self.gps_handler = GPSHandler(self.lat0, self.lon0)
|
||||||
|
|
||||||
self.ultima_atualizacao = None
|
self.ultima_atualizacao = None
|
||||||
|
|
|
||||||
|
|
@ -1127,9 +1127,7 @@ class ContextoGlobalRedis:
|
||||||
if bloqueio_pendente_desde > 0:
|
if bloqueio_pendente_desde > 0:
|
||||||
updates_base.update({
|
updates_base.update({
|
||||||
"ultimo_bloqueio_transitorio_em": agora,
|
"ultimo_bloqueio_transitorio_em": agora,
|
||||||
"ultimo_bloqueio_transitorio_motivo": str(
|
"ultimo_bloqueio_transitorio_motivo": str(operacao.get("bloqueio_pendente_motivo", "")),
|
||||||
operacao.get("bloqueio_pendente_motivo", "")
|
|
||||||
),
|
|
||||||
"bloqueio_pendente_desde": 0.0,
|
"bloqueio_pendente_desde": 0.0,
|
||||||
"bloqueio_pendente_motivo": "",
|
"bloqueio_pendente_motivo": "",
|
||||||
})
|
})
|
||||||
|
|
@ -1187,6 +1185,7 @@ class ContextoGlobalRedis:
|
||||||
|
|
||||||
restante = (retomada_desde + tempo_retomada) - agora
|
restante = (retomada_desde + tempo_retomada) - agora
|
||||||
if restante > 0:
|
if restante > 0:
|
||||||
|
updates_base["tempo_aguardando"] = agora
|
||||||
_commit(
|
_commit(
|
||||||
StatusOperacao.Aguardando,
|
StatusOperacao.Aguardando,
|
||||||
f"Bloqueio removido; retomada segura em {restante:.1f}s",
|
f"Bloqueio removido; retomada segura em {restante:.1f}s",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class ManagerWorkerCommandType(IntEnum):
|
||||||
AtualizarDadosControle = 3
|
AtualizarDadosControle = 3
|
||||||
EnviarDadosControle = 4
|
EnviarDadosControle = 4
|
||||||
FinalizarOperacao = 5
|
FinalizarOperacao = 5
|
||||||
|
ReiniciarMPC = 6
|
||||||
|
|
||||||
class WeedWorkerCommandType(IntEnum):
|
class WeedWorkerCommandType(IntEnum):
|
||||||
ScriptCarregado = 1
|
ScriptCarregado = 1
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,13 @@ class CameraManager:
|
||||||
|
|
||||||
self._ultimo_config_update_ts = 0.0
|
self._ultimo_config_update_ts = 0.0
|
||||||
|
|
||||||
|
# Diagnostico fino do pipeline. Estes campos sao somente observabilidade:
|
||||||
|
# nao alteram frequencias, caches, gates ou comandos dos bicos.
|
||||||
|
self._diag_tensor_overwrites = 0
|
||||||
|
self._diag_pred_overwrites = 0
|
||||||
|
self._diag_last_tensor_lock_wait_ms = 0.0
|
||||||
|
self._diag_last_pred_lock_wait_ms = 0.0
|
||||||
|
|
||||||
self._dbg_img = None
|
self._dbg_img = None
|
||||||
self._dbg_layer = None
|
self._dbg_layer = None
|
||||||
self._dbg_shape = (706, 560)
|
self._dbg_shape = (706, 560)
|
||||||
|
|
@ -854,18 +861,20 @@ class CameraManager:
|
||||||
self._ultima_saude_ts = time.time()
|
self._ultima_saude_ts = time.time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Mantém a referência estável durante leitura/publicação da saúde.
|
# O lock protege somente a identidade da sessão da câmera.
|
||||||
# fechar_camera_manager aguarda esta seção curta terminar.
|
|
||||||
with self._vida_lock:
|
with self._vida_lock:
|
||||||
camera_atual = self.camera
|
camera_atual = self.camera
|
||||||
|
generation = self._camera_generation
|
||||||
|
|
||||||
if camera_atual is not None:
|
if camera_atual is not None:
|
||||||
if pipeline_ia is None:
|
if pipeline_ia is None:
|
||||||
pipeline_ia = self._avaliar_saude_pipeline_ia()
|
pipeline_ia = self._avaliar_saude_pipeline_ia()
|
||||||
|
|
||||||
camera_atual.atualizar_saude(pipeline_ia=pipeline_ia)
|
camera_atual.atualizar_saude(pipeline_ia=pipeline_ia)
|
||||||
|
|
||||||
elif self.mx_id is not None:
|
elif self.mx_id is not None:
|
||||||
from camera_worker.manager import definir_saude_camera
|
from camera_worker.manager import definir_saude_camera
|
||||||
|
|
||||||
definir_saude_camera(
|
definir_saude_camera(
|
||||||
self.mx_id,
|
self.mx_id,
|
||||||
StatusModulo.DESCONECTADO,
|
StatusModulo.DESCONECTADO,
|
||||||
|
|
@ -987,7 +996,11 @@ class CameraManager:
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
def _get_tensor_novo_para_inferencia(self):
|
def _get_tensor_novo_para_inferencia(self):
|
||||||
|
t_lock0 = time.perf_counter()
|
||||||
with self._lock_tensor:
|
with self._lock_tensor:
|
||||||
|
self._diag_last_tensor_lock_wait_ms = (
|
||||||
|
time.perf_counter() - t_lock0
|
||||||
|
) * 1000.0
|
||||||
tensor5 = self._tensor_pronto
|
tensor5 = self._tensor_pronto
|
||||||
res = self._tensor_res
|
res = self._tensor_res
|
||||||
ts_tensor = self._tensor_ts
|
ts_tensor = self._tensor_ts
|
||||||
|
|
@ -1006,7 +1019,11 @@ class CameraManager:
|
||||||
return tensor5, ts_tensor, res, generation
|
return tensor5, ts_tensor, res, generation
|
||||||
|
|
||||||
def _get_prediction_nova_para_deteccao(self):
|
def _get_prediction_nova_para_deteccao(self):
|
||||||
|
t_lock0 = time.perf_counter()
|
||||||
with self._pred_lock:
|
with self._pred_lock:
|
||||||
|
self._diag_last_pred_lock_wait_ms = (
|
||||||
|
time.perf_counter() - t_lock0
|
||||||
|
) * 1000.0
|
||||||
cache = dict(self._pred_cache)
|
cache = dict(self._pred_cache)
|
||||||
|
|
||||||
pred_ts = float(cache.get("ts", 0.0) or 0.0)
|
pred_ts = float(cache.get("ts", 0.0) or 0.0)
|
||||||
|
|
@ -1323,23 +1340,39 @@ class CameraManager:
|
||||||
def _iniciar_loop_captura_tensor(self, freq=25.0):
|
def _iniciar_loop_captura_tensor(self, freq=25.0):
|
||||||
def loop():
|
def loop():
|
||||||
periodo = 1.0 / max(float(freq), 0.1)
|
periodo = 1.0 / max(float(freq), 0.1)
|
||||||
|
ultimo_inicio = None
|
||||||
|
sleep_req_ms_ant = 0.0
|
||||||
|
sleep_real_ms_ant = 0.0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
t0_wall = time.time()
|
t0_wall = time.time()
|
||||||
t0_perf = time.perf_counter()
|
t0_perf = time.perf_counter()
|
||||||
|
t0_cpu = time.thread_time()
|
||||||
|
loop_gap_ms = (
|
||||||
|
(t0_perf - ultimo_inicio) * 1000.0
|
||||||
|
if ultimo_inicio is not None else 0.0
|
||||||
|
)
|
||||||
|
ultimo_inicio = t0_perf
|
||||||
camera_atual = None
|
camera_atual = None
|
||||||
generation = -1
|
generation = -1
|
||||||
|
vida_lock_wait_ms = 0.0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.iniciando or self._em_warmup or not self.operante or self.camera is None:
|
if self.iniciando or self._em_warmup or not self.operante or self.camera is None:
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
t_lock0 = time.perf_counter()
|
||||||
with self._vida_lock:
|
with self._vida_lock:
|
||||||
|
vida_lock_wait_ms = (time.perf_counter() - t_lock0) * 1000.0
|
||||||
camera_atual = self.camera
|
camera_atual = self.camera
|
||||||
generation = self._camera_generation
|
generation = self._camera_generation
|
||||||
|
|
||||||
|
t_request0 = time.perf_counter()
|
||||||
|
t_request_cpu0 = time.thread_time()
|
||||||
tensor5, res = camera_atual.requisitar_tensor_multispec(force=True)
|
tensor5, res = camera_atual.requisitar_tensor_multispec(force=True)
|
||||||
|
request_wall_ms = (time.perf_counter() - t_request0) * 1000.0
|
||||||
|
request_cpu_ms = (time.thread_time() - t_request_cpu0) * 1000.0
|
||||||
erro = res.get("erro") if isinstance(res, dict) else None
|
erro = res.get("erro") if isinstance(res, dict) else None
|
||||||
|
|
||||||
if erro:
|
if erro:
|
||||||
|
|
@ -1367,6 +1400,11 @@ class CameraManager:
|
||||||
perf = res.get("perf", {}) if isinstance(res, dict) else {}
|
perf = res.get("perf", {}) if isinstance(res, dict) else {}
|
||||||
|
|
||||||
with self._lock_tensor:
|
with self._lock_tensor:
|
||||||
|
if (
|
||||||
|
self._tensor_pronto is not None
|
||||||
|
and self._tensor_ts > self._tensor_consumido_ts
|
||||||
|
):
|
||||||
|
self._diag_tensor_overwrites += 1
|
||||||
self._tensor_pronto = tensor5
|
self._tensor_pronto = tensor5
|
||||||
self._tensor_res = res
|
self._tensor_res = res
|
||||||
self._tensor_ts = agora
|
self._tensor_ts = agora
|
||||||
|
|
@ -1374,10 +1412,12 @@ class CameraManager:
|
||||||
|
|
||||||
self._ultimo_tensor_ok_ts = agora
|
self._ultimo_tensor_ok_ts = agora
|
||||||
t1_perf = time.perf_counter()
|
t1_perf = time.perf_counter()
|
||||||
|
thread_cpu_ms = (time.thread_time() - t0_cpu) * 1000.0
|
||||||
|
wall_ms = (t1_perf - t0_perf) * 1000.0
|
||||||
|
|
||||||
self.perf.tick(
|
self.perf.tick(
|
||||||
"tensor",
|
"tensor",
|
||||||
latencia_ms=(t1_perf - t0_perf) * 1000.0,
|
latencia_ms=wall_ms,
|
||||||
tensor_core_ms=float(perf.get("total_ms", 0.0) or 0.0),
|
tensor_core_ms=float(perf.get("total_ms", 0.0) or 0.0),
|
||||||
get_decoded_ms=float(perf.get("get_decoded_ms", 0.0) or 0.0),
|
get_decoded_ms=float(perf.get("get_decoded_ms", 0.0) or 0.0),
|
||||||
build_ms=float(perf.get("build_ms", 0.0) or 0.0),
|
build_ms=float(perf.get("build_ms", 0.0) or 0.0),
|
||||||
|
|
@ -1387,6 +1427,17 @@ class CameraManager:
|
||||||
frame_ts=agora,
|
frame_ts=agora,
|
||||||
idade_frame_ms=0.0,
|
idade_frame_ms=0.0,
|
||||||
generation=generation,
|
generation=generation,
|
||||||
|
loop_gap_ms=loop_gap_ms,
|
||||||
|
thread_cpu_ms=thread_cpu_ms,
|
||||||
|
off_cpu_ms=max(0.0, wall_ms - thread_cpu_ms),
|
||||||
|
vida_lock_wait_ms=vida_lock_wait_ms,
|
||||||
|
request_wall_ms=request_wall_ms,
|
||||||
|
request_cpu_ms=request_cpu_ms,
|
||||||
|
request_off_cpu_ms=max(0.0, request_wall_ms - request_cpu_ms),
|
||||||
|
sleep_req_ms=sleep_req_ms_ant,
|
||||||
|
sleep_real_ms=sleep_real_ms_ant,
|
||||||
|
sleep_overshoot_ms=max(0.0, sleep_real_ms_ant - sleep_req_ms_ant),
|
||||||
|
tensor_overwrites=self._diag_tensor_overwrites,
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -1403,17 +1454,31 @@ class CameraManager:
|
||||||
finally:
|
finally:
|
||||||
gasto = time.time() - t0_wall
|
gasto = time.time() - t0_wall
|
||||||
restante = periodo - gasto
|
restante = periodo - gasto
|
||||||
|
sleep_req_ms_ant = max(0.0, restante) * 1000.0
|
||||||
|
t_sleep0 = time.perf_counter()
|
||||||
if restante > 0:
|
if restante > 0:
|
||||||
time.sleep(restante)
|
time.sleep(restante)
|
||||||
|
sleep_real_ms_ant = (time.perf_counter() - t_sleep0) * 1000.0
|
||||||
|
|
||||||
threading.Thread(target=loop, name="WeedTensorCapture", daemon=True).start()
|
threading.Thread(target=loop, name="WeedTensorCapture", daemon=True).start()
|
||||||
|
|
||||||
def _iniciar_loop_inferencia(self, freq=25.0):
|
def _iniciar_loop_inferencia(self, freq=25.0):
|
||||||
def loop():
|
def loop():
|
||||||
periodo = 1.0 / max(float(freq), 0.1)
|
periodo = 1.0 / max(float(freq), 0.1)
|
||||||
|
ultimo_inicio = None
|
||||||
|
ultima_inferencia_inicio = None
|
||||||
|
polls_sem_tensor = 0
|
||||||
|
sleep_req_ms_ant = 0.0
|
||||||
|
sleep_real_ms_ant = 0.0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
t_loop0 = time.perf_counter()
|
t_loop0 = time.perf_counter()
|
||||||
|
t_cpu0 = time.thread_time()
|
||||||
|
loop_gap_ms = (
|
||||||
|
(t_loop0 - ultimo_inicio) * 1000.0
|
||||||
|
if ultimo_inicio is not None else 0.0
|
||||||
|
)
|
||||||
|
ultimo_inicio = t_loop0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.iniciando:
|
if self.iniciando:
|
||||||
|
|
@ -1433,6 +1498,7 @@ class CameraManager:
|
||||||
|
|
||||||
if tensor5 is None or tensor_ts is None:
|
if tensor5 is None or tensor_ts is None:
|
||||||
self.perf.inc("infer_sem_tensor_novo")
|
self.perf.inc("infer_sem_tensor_novo")
|
||||||
|
polls_sem_tensor += 1
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -1441,11 +1507,13 @@ class CameraManager:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
t_inf0 = time.perf_counter()
|
t_inf0 = time.perf_counter()
|
||||||
|
infer_cpu0 = time.thread_time()
|
||||||
predictions = self.model_svc.infer_tensor_fast(
|
predictions = self.model_svc.infer_tensor_fast(
|
||||||
tensor5,
|
tensor5,
|
||||||
keep_probs=False,
|
keep_probs=False,
|
||||||
)
|
)
|
||||||
t_inf1 = time.perf_counter()
|
t_inf1 = time.perf_counter()
|
||||||
|
infer_cpu_ms = (time.thread_time() - infer_cpu0) * 1000.0
|
||||||
|
|
||||||
if not self._sessao_camera_valida(camera_atual, generation):
|
if not self._sessao_camera_valida(camera_atual, generation):
|
||||||
self.perf.inc("infer_descartada_geracao_depois")
|
self.perf.inc("infer_descartada_geracao_depois")
|
||||||
|
|
@ -1469,6 +1537,12 @@ class CameraManager:
|
||||||
pred_ts = time.time()
|
pred_ts = time.time()
|
||||||
|
|
||||||
with self._pred_lock:
|
with self._pred_lock:
|
||||||
|
if (
|
||||||
|
self._pred_cache.get("predictions") is not None
|
||||||
|
and float(self._pred_cache.get("ts", 0.0) or 0.0)
|
||||||
|
> self._pred_consumido_ts
|
||||||
|
):
|
||||||
|
self._diag_pred_overwrites += 1
|
||||||
self._pred_cache = {
|
self._pred_cache = {
|
||||||
"ts": pred_ts,
|
"ts": pred_ts,
|
||||||
"tensor_ts": tensor_ts,
|
"tensor_ts": tensor_ts,
|
||||||
|
|
@ -1489,9 +1563,16 @@ class CameraManager:
|
||||||
self._ultimo_predictions_full = infer_full
|
self._ultimo_predictions_full = infer_full
|
||||||
|
|
||||||
t_loop1 = time.perf_counter()
|
t_loop1 = time.perf_counter()
|
||||||
|
thread_cpu_ms = (time.thread_time() - t_cpu0) * 1000.0
|
||||||
|
wall_ms = (t_loop1 - t_loop0) * 1000.0
|
||||||
|
infer_start_gap_ms = (
|
||||||
|
(t_inf0 - ultima_inferencia_inicio) * 1000.0
|
||||||
|
if ultima_inferencia_inicio is not None else 0.0
|
||||||
|
)
|
||||||
|
ultima_inferencia_inicio = t_inf0
|
||||||
self.perf.tick(
|
self.perf.tick(
|
||||||
"inferencia",
|
"inferencia",
|
||||||
latencia_ms=(t_loop1 - t_loop0) * 1000.0,
|
latencia_ms=wall_ms,
|
||||||
get_tensor_ms=(t_get1 - t_get0) * 1000.0,
|
get_tensor_ms=(t_get1 - t_get0) * 1000.0,
|
||||||
infer_ms=infer_ms,
|
infer_ms=infer_ms,
|
||||||
infer_forward_ms=float(infer_forward_ms or 0.0),
|
infer_forward_ms=float(infer_forward_ms or 0.0),
|
||||||
|
|
@ -1502,12 +1583,28 @@ class CameraManager:
|
||||||
frame_ts=pred_ts,
|
frame_ts=pred_ts,
|
||||||
idade_tensor_ms=(time.time() - tensor_ts) * 1000.0 if tensor_ts else None,
|
idade_tensor_ms=(time.time() - tensor_ts) * 1000.0 if tensor_ts else None,
|
||||||
generation=generation,
|
generation=generation,
|
||||||
|
loop_gap_ms=loop_gap_ms,
|
||||||
|
infer_start_gap_ms=infer_start_gap_ms,
|
||||||
|
thread_cpu_ms=thread_cpu_ms,
|
||||||
|
off_cpu_ms=max(0.0, wall_ms - thread_cpu_ms),
|
||||||
|
infer_cpu_ms=infer_cpu_ms,
|
||||||
|
infer_off_cpu_ms=max(0.0, infer_ms - infer_cpu_ms),
|
||||||
|
tensor_lock_wait_ms=self._diag_last_tensor_lock_wait_ms,
|
||||||
|
polls_sem_tensor=polls_sem_tensor,
|
||||||
|
sleep_req_ms=sleep_req_ms_ant,
|
||||||
|
sleep_real_ms=sleep_real_ms_ant,
|
||||||
|
sleep_overshoot_ms=max(0.0, sleep_real_ms_ant - sleep_req_ms_ant),
|
||||||
|
pred_overwrites=self._diag_pred_overwrites,
|
||||||
)
|
)
|
||||||
|
polls_sem_tensor = 0
|
||||||
|
|
||||||
gasto = time.perf_counter() - t_loop0
|
gasto = time.perf_counter() - t_loop0
|
||||||
restante = periodo - gasto
|
restante = periodo - gasto
|
||||||
|
sleep_req_ms_ant = max(0.0, restante) * 1000.0
|
||||||
|
t_sleep0 = time.perf_counter()
|
||||||
if restante > 0:
|
if restante > 0:
|
||||||
time.sleep(restante)
|
time.sleep(restante)
|
||||||
|
sleep_real_ms_ant = (time.perf_counter() - t_sleep0) * 1000.0
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"❌ Erro no loop_inferencia weed: {e}")
|
self.mostrar_log(f"❌ Erro no loop_inferencia weed: {e}")
|
||||||
|
|
@ -1517,9 +1614,20 @@ class CameraManager:
|
||||||
|
|
||||||
def _iniciar_loop_deteccao_weed(self, freq=25.0):
|
def _iniciar_loop_deteccao_weed(self, freq=25.0):
|
||||||
def loop():
|
def loop():
|
||||||
|
ultimo_inicio = None
|
||||||
|
ultima_deteccao_inicio = None
|
||||||
|
polls_sem_pred = 0
|
||||||
|
sleep_req_ms_ant = 0.0
|
||||||
|
sleep_real_ms_ant = 0.0
|
||||||
while True:
|
while True:
|
||||||
t0_wall = time.time()
|
t0_wall = time.time()
|
||||||
t_loop0 = time.perf_counter()
|
t_loop0 = time.perf_counter()
|
||||||
|
t_cpu0 = time.thread_time()
|
||||||
|
loop_gap_ms = (
|
||||||
|
(t_loop0 - ultimo_inicio) * 1000.0
|
||||||
|
if ultimo_inicio is not None else 0.0
|
||||||
|
)
|
||||||
|
ultimo_inicio = t_loop0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.iniciando:
|
if self.iniciando:
|
||||||
|
|
@ -1532,7 +1640,12 @@ class CameraManager:
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
t_cfg0 = time.perf_counter()
|
||||||
|
cfg_cpu0 = time.thread_time()
|
||||||
self._atualizar_config_dinamica_detector(intervalo_s=0.20)
|
self._atualizar_config_dinamica_detector(intervalo_s=0.20)
|
||||||
|
t_cfg1 = time.perf_counter()
|
||||||
|
config_ms = (t_cfg1 - t_cfg0) * 1000.0
|
||||||
|
config_cpu_ms = (time.thread_time() - cfg_cpu0) * 1000.0
|
||||||
|
|
||||||
t_pred0 = time.perf_counter()
|
t_pred0 = time.perf_counter()
|
||||||
pred_cache = self._get_prediction_nova_para_deteccao()
|
pred_cache = self._get_prediction_nova_para_deteccao()
|
||||||
|
|
@ -1540,6 +1653,7 @@ class CameraManager:
|
||||||
|
|
||||||
if pred_cache is None:
|
if pred_cache is None:
|
||||||
self.perf.inc("det_sem_prediction_nova")
|
self.perf.inc("det_sem_prediction_nova")
|
||||||
|
polls_sem_pred += 1
|
||||||
|
|
||||||
# Se o estado operacional mudou para não permitido, não espera prediction nova
|
# Se o estado operacional mudou para não permitido, não espera prediction nova
|
||||||
# para desligar bicos.
|
# para desligar bicos.
|
||||||
|
|
@ -1566,8 +1680,10 @@ class CameraManager:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
t_det0 = time.perf_counter()
|
t_det0 = time.perf_counter()
|
||||||
|
det_cpu0 = time.thread_time()
|
||||||
analise_completa = self.detectar_ervas(predictions)
|
analise_completa = self.detectar_ervas(predictions)
|
||||||
t_det1 = time.perf_counter()
|
t_det1 = time.perf_counter()
|
||||||
|
detector_cpu_ms = (time.thread_time() - det_cpu0) * 1000.0
|
||||||
|
|
||||||
if not isinstance(analise_completa, dict):
|
if not isinstance(analise_completa, dict):
|
||||||
self.mostrar_log("[WEED] análise inválida retornada pelo WeedDetector")
|
self.mostrar_log("[WEED] análise inválida retornada pelo WeedDetector")
|
||||||
|
|
@ -1637,6 +1753,12 @@ class CameraManager:
|
||||||
|
|
||||||
t_loop1 = time.perf_counter()
|
t_loop1 = time.perf_counter()
|
||||||
total_ms = (t_loop1 - t_loop0) * 1000.0
|
total_ms = (t_loop1 - t_loop0) * 1000.0
|
||||||
|
thread_cpu_ms = (time.thread_time() - t_cpu0) * 1000.0
|
||||||
|
det_start_gap_ms = (
|
||||||
|
(t_det0 - ultima_deteccao_inicio) * 1000.0
|
||||||
|
if ultima_deteccao_inicio is not None else 0.0
|
||||||
|
)
|
||||||
|
ultima_deteccao_inicio = t_det0
|
||||||
self._ultimo_loop_analise_fps = 1000.0 / max(total_ms, 1e-6)
|
self._ultimo_loop_analise_fps = 1000.0 / max(total_ms, 1e-6)
|
||||||
|
|
||||||
self._perf_weed = {
|
self._perf_weed = {
|
||||||
|
|
@ -1673,6 +1795,9 @@ class CameraManager:
|
||||||
"deteccao",
|
"deteccao",
|
||||||
latencia_ms=total_ms,
|
latencia_ms=total_ms,
|
||||||
get_pred_ms=(t_pred1 - t_pred0) * 1000.0,
|
get_pred_ms=(t_pred1 - t_pred0) * 1000.0,
|
||||||
|
config_ms=config_ms,
|
||||||
|
config_cpu_ms=config_cpu_ms,
|
||||||
|
config_off_cpu_ms=max(0.0, config_ms - config_cpu_ms),
|
||||||
detector_ms=detector_ms,
|
detector_ms=detector_ms,
|
||||||
convert_ms=(t_conv1 - t_conv0) * 1000.0,
|
convert_ms=(t_conv1 - t_conv0) * 1000.0,
|
||||||
ctx_read_ms=float(debug_pulverizacao.get("ctx_read_ms", 0.0) or 0.0),
|
ctx_read_ms=float(debug_pulverizacao.get("ctx_read_ms", 0.0) or 0.0),
|
||||||
|
|
@ -1688,7 +1813,19 @@ class CameraManager:
|
||||||
frame_ts=pred_ts,
|
frame_ts=pred_ts,
|
||||||
idade_frame_ms=(time.time() - pred_ts) * 1000.0 if pred_ts else None,
|
idade_frame_ms=(time.time() - pred_ts) * 1000.0 if pred_ts else None,
|
||||||
idade_tensor_ms=(time.time() - tensor_ts) * 1000.0 if tensor_ts else None,
|
idade_tensor_ms=(time.time() - tensor_ts) * 1000.0 if tensor_ts else None,
|
||||||
|
loop_gap_ms=loop_gap_ms,
|
||||||
|
det_start_gap_ms=det_start_gap_ms,
|
||||||
|
thread_cpu_ms=thread_cpu_ms,
|
||||||
|
off_cpu_ms=max(0.0, total_ms - thread_cpu_ms),
|
||||||
|
detector_cpu_ms=detector_cpu_ms,
|
||||||
|
detector_off_cpu_ms=max(0.0, detector_ms - detector_cpu_ms),
|
||||||
|
pred_lock_wait_ms=self._diag_last_pred_lock_wait_ms,
|
||||||
|
polls_sem_pred=polls_sem_pred,
|
||||||
|
sleep_req_ms=sleep_req_ms_ant,
|
||||||
|
sleep_real_ms=sleep_real_ms_ant,
|
||||||
|
sleep_overshoot_ms=max(0.0, sleep_real_ms_ant - sleep_req_ms_ant),
|
||||||
)
|
)
|
||||||
|
polls_sem_pred = 0
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"❌ Erro no loop_deteccao_weed: {e}")
|
self.mostrar_log(f"❌ Erro no loop_deteccao_weed: {e}")
|
||||||
|
|
@ -1700,7 +1837,11 @@ class CameraManager:
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
lat = time.time() - t0_wall
|
lat = time.time() - t0_wall
|
||||||
time.sleep(max(0.0, (1.0 / freq) - lat))
|
sleep_s = max(0.0, (1.0 / freq) - lat)
|
||||||
|
sleep_req_ms_ant = sleep_s * 1000.0
|
||||||
|
t_sleep0 = time.perf_counter()
|
||||||
|
time.sleep(sleep_s)
|
||||||
|
sleep_real_ms_ant = (time.perf_counter() - t_sleep0) * 1000.0
|
||||||
|
|
||||||
threading.Thread(target=loop, daemon=True).start()
|
threading.Thread(target=loop, daemon=True).start()
|
||||||
|
|
||||||
|
|
@ -2406,6 +2547,7 @@ class CameraManager:
|
||||||
f"post={self._fmt(self._m(inf, 'infer_post_ms'))} "
|
f"post={self._fmt(self._m(inf, 'infer_post_ms'))} "
|
||||||
f"gpu={self._fmt(self._m(inf, 'infer_gpu_ms'))} | "
|
f"gpu={self._fmt(self._m(inf, 'infer_gpu_ms'))} | "
|
||||||
f"DET total={self._fmt(self._lat(det))} "
|
f"DET total={self._fmt(self._lat(det))} "
|
||||||
|
f"config={self._fmt(self._m(det, 'config_ms'))} "
|
||||||
f"get_pred={self._fmt(self._m(det, 'get_pred_ms'))} "
|
f"get_pred={self._fmt(self._m(det, 'get_pred_ms'))} "
|
||||||
f"detector={self._fmt(self._m(det, 'detector_ms'))} "
|
f"detector={self._fmt(self._m(det, 'detector_ms'))} "
|
||||||
f"ctx={self._fmt(self._m(det, 'ctx_read_ms'))} "
|
f"ctx={self._fmt(self._m(det, 'ctx_read_ms'))} "
|
||||||
|
|
@ -2420,6 +2562,34 @@ class CameraManager:
|
||||||
f"redis_dbg={pub_dbg.get('redis_ms', 0):.1f}ms"
|
f"redis_dbg={pub_dbg.get('redis_ms', 0):.1f}ms"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.mostrar_log(
|
||||||
|
"[WEED_SCHED] "
|
||||||
|
f"TENSOR gap={self._fmt(self._m(tensor, 'loop_gap_ms'))} "
|
||||||
|
f"cpu={self._fmt(self._m(tensor, 'thread_cpu_ms'))} "
|
||||||
|
f"offcpu={self._fmt(self._m(tensor, 'off_cpu_ms'))} "
|
||||||
|
f"req_wall={self._fmt(self._m(tensor, 'request_wall_ms'))} "
|
||||||
|
f"req_cpu={self._fmt(self._m(tensor, 'request_cpu_ms'))} "
|
||||||
|
f"lock={self._fmt(self._m(tensor, 'vida_lock_wait_ms'))} "
|
||||||
|
f"sleep={self._fmt(self._m(tensor, 'sleep_req_ms'))}/"
|
||||||
|
f"{self._fmt(self._m(tensor, 'sleep_real_ms'))} | "
|
||||||
|
f"INF gap={self._fmt(self._m(inf, 'infer_start_gap_ms'))} "
|
||||||
|
f"cpu={self._fmt(self._m(inf, 'thread_cpu_ms'))} "
|
||||||
|
f"offcpu={self._fmt(self._m(inf, 'off_cpu_ms'))} "
|
||||||
|
f"fwd_cpu={self._fmt(self._m(inf, 'infer_cpu_ms'))} "
|
||||||
|
f"lock={self._fmt(self._m(inf, 'tensor_lock_wait_ms'))} "
|
||||||
|
f"poll={self._fmt(self._m(inf, 'polls_sem_tensor'), casas=0)} "
|
||||||
|
f"sleep={self._fmt(self._m(inf, 'sleep_req_ms'))}/"
|
||||||
|
f"{self._fmt(self._m(inf, 'sleep_real_ms'))} | "
|
||||||
|
f"DET gap={self._fmt(self._m(det, 'det_start_gap_ms'))} "
|
||||||
|
f"cpu={self._fmt(self._m(det, 'thread_cpu_ms'))} "
|
||||||
|
f"offcpu={self._fmt(self._m(det, 'off_cpu_ms'))} "
|
||||||
|
f"det_cpu={self._fmt(self._m(det, 'detector_cpu_ms'))} "
|
||||||
|
f"lock={self._fmt(self._m(det, 'pred_lock_wait_ms'))} "
|
||||||
|
f"poll={self._fmt(self._m(det, 'polls_sem_pred'), casas=0)} "
|
||||||
|
f"sleep={self._fmt(self._m(det, 'sleep_req_ms'))}/"
|
||||||
|
f"{self._fmt(self._m(det, 'sleep_real_ms'))}"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _fmt(v, casas=1, default=0.0):
|
def _fmt(v, casas=1, default=0.0):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue