diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.Designer.cs b/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.Designer.cs index b402f433b..ff4909c27 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.Designer.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.Designer.cs @@ -48,6 +48,7 @@ this.nudTemposalvarlog = new System.Windows.Forms.NumericUpDown(); this.lblTemposalvarlog = new System.Windows.Forms.Label(); this.gpbP_Atu = new System.Windows.Forms.GroupBox(); + this.cmbAgitadorModo = new System.Windows.Forms.ComboBox(); this.chbPulverizadorAutomatico = new System.Windows.Forms.CheckBox(); this.nudAtuarem = new System.Windows.Forms.NumericUpDown(); this.lblAtuarem = new System.Windows.Forms.Label(); @@ -93,7 +94,7 @@ this.nudCapacidadereservatorio = new System.Windows.Forms.NumericUpDown(); this.lblUnCapcaidadereservatorio = new System.Windows.Forms.Label(); this.lblCapacidadereservatorio = new System.Windows.Forms.Label(); - this.cmbAgitadorModo = new System.Windows.Forms.ComboBox(); + this.chbGravarDadosPosProcessamento = new System.Windows.Forms.CheckBox(); this.gpbModulos.SuspendLayout(); this.gpbP_Knt.SuspendLayout(); this.gpbP_Sen.SuspendLayout(); @@ -363,6 +364,17 @@ this.gpbP_Atu.TabStop = false; this.gpbP_Atu.Text = "Ajustes"; // + // cmbAgitadorModo + // + this.cmbAgitadorModo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbAgitadorModo.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cmbAgitadorModo.FormattingEnabled = true; + this.cmbAgitadorModo.Location = new System.Drawing.Point(9, 132); + this.cmbAgitadorModo.Margin = new System.Windows.Forms.Padding(6); + this.cmbAgitadorModo.Name = "cmbAgitadorModo"; + this.cmbAgitadorModo.Size = new System.Drawing.Size(175, 32); + this.cmbAgitadorModo.TabIndex = 9; + // // chbPulverizadorAutomatico // this.chbPulverizadorAutomatico.AutoSize = true; @@ -802,6 +814,7 @@ // // gpbConfiguracoesgerais // + this.gpbConfiguracoesgerais.Controls.Add(this.chbGravarDadosPosProcessamento); this.gpbConfiguracoesgerais.Controls.Add(this.txtDensidadeherbicida); this.gpbConfiguracoesgerais.Controls.Add(this.lblUnDensidadeherbicida); this.gpbConfiguracoesgerais.Controls.Add(this.label2); @@ -872,16 +885,16 @@ this.lblCapacidadereservatorio.TabIndex = 9; this.lblCapacidadereservatorio.Text = "Capacidade reservatório"; // - // cmbAgitadorModo + // chbGravarDadosPosProcessamento // - this.cmbAgitadorModo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbAgitadorModo.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.cmbAgitadorModo.FormattingEnabled = true; - this.cmbAgitadorModo.Location = new System.Drawing.Point(9, 132); - this.cmbAgitadorModo.Margin = new System.Windows.Forms.Padding(6); - this.cmbAgitadorModo.Name = "cmbAgitadorModo"; - this.cmbAgitadorModo.Size = new System.Drawing.Size(175, 32); - this.cmbAgitadorModo.TabIndex = 9; + this.chbGravarDadosPosProcessamento.AutoSize = true; + this.chbGravarDadosPosProcessamento.Location = new System.Drawing.Point(14, 219); + this.chbGravarDadosPosProcessamento.Margin = new System.Windows.Forms.Padding(6); + this.chbGravarDadosPosProcessamento.Name = "chbGravarDadosPosProcessamento"; + this.chbGravarDadosPosProcessamento.Size = new System.Drawing.Size(354, 28); + this.chbGravarDadosPosProcessamento.TabIndex = 15; + this.chbGravarDadosPosProcessamento.Text = "Gravar dados para pós processamento"; + this.chbGravarDadosPosProcessamento.UseVisualStyleBackColor = true; // // frmParametrizacaoOperacao // @@ -998,5 +1011,6 @@ private System.Windows.Forms.CheckBox chbFrenagem; private System.Windows.Forms.CheckBox chbMovAuxilioSonar; private System.Windows.Forms.ComboBox cmbAgitadorModo; + private System.Windows.Forms.CheckBox chbGravarDadosPosProcessamento; } } \ No newline at end of file diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.cs b/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.cs index a83d0d192..b900fc900 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmParametrizacaoOperacao.cs @@ -88,6 +88,8 @@ namespace AgroBase.Forms.Operacoes txtAnteciparManobraM.Text = _Parametros.Controle.AnteciparManobraCorredorM.ToString(); cmbAgitadorModo.SelectedIndex = (int)_Parametros.Controle.AtuAgitadorModo; + + chbGravarDadosPosProcessamento.Checked = _Parametros.Controle.RegistrarDadosPosProcessamento; } private void chbD_CheckedChanged(object sender, EventArgs e) @@ -148,6 +150,7 @@ namespace AgroBase.Forms.Operacoes Variaveis.OperacaoEmAndamento.Parametros.Controle.MovAuxilioSonar = chbMovAuxilioSonar.Checked; Variaveis.OperacaoEmAndamento.Parametros.Controle.FrenagemAutomaticaAoParar = chbFrenagem.Checked; Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuAgitadorModo = (ModoAgitadorCalda)cmbAgitadorModo.SelectedIndex; + Variaveis.OperacaoEmAndamento.Parametros.Controle.RegistrarDadosPosProcessamento = chbGravarDadosPosProcessamento.Checked; Variaveis.OperacaoEmAndamento.Parametros.ModulosMandatorios = ModulosMandatorios.Where(x => x.Utilizar).ToList(); Variaveis.OperacaoEmAndamento.Parametros.Controle.PulverizadorAutomatico = chbPulverizadorAutomatico.Checked; diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs b/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs index 3453fb959..f9e4823a4 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs @@ -25,6 +25,8 @@ namespace AgroBase.Forms.Operacoes List LogsAtuador = new List(); List LogsSensoriamento = new List(); List> LogsCam_Solo = new List>(); + List LogsVisualWorker = new List(); + List LogsWeedWorker = new List(); List pathImagensSolo = new List(); List picsCamSolo = new List(); List picsCamSoloSeg = new List(); @@ -160,6 +162,8 @@ namespace AgroBase.Forms.Operacoes LogsSensoriamento = JsonConvert.DeserializeObject(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, data.sen ?? Enums.T_Code.Sen.ToString())).ToList(); pathImagensCaminho = ofd.FileName.Replace("data.lgop", data.cam_caminho + "\\"); LogsCam_Caminho = JsonConvert.DeserializeObject>(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, data.cam_caminho ?? Enums.T_Code.Snr.ToString())).ToList(); + LogsVisualWorker = JsonConvert.DeserializeObject>(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, "VisualWorker")).ToList(); + LogsWeedWorker = JsonConvert.DeserializeObject>(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, "WeedWorker")).ToList(); pathImagensSolo = new List(); picsCamSolo = new List(); @@ -843,7 +847,7 @@ namespace AgroBase.Forms.Operacoes { new GraficoInterativoSerieModel() { - Titulo = $"{camNome} RAM %", + Titulo = $"{camNome} RAM Mb", Valores = camSolo.Select(x => (double)(x?.performance?.memoria_usada ?? 0)).ToList(), Visivel = true, MostrarValor = true @@ -865,7 +869,7 @@ namespace AgroBase.Forms.Operacoes { new GraficoInterativoSerieModel() { - Titulo = $"{camNome} RAM %", + Titulo = $"{camNome} RAM Mb", Valores = camCaminho.Select(x => (double)(x?.performance?.memoria_usada ?? 0)).ToList(), Visivel = true, MostrarValor = true @@ -895,10 +899,8 @@ namespace AgroBase.Forms.Operacoes dtComandosDir.Columns.Add("clTipoMovimento", "Tipo Movimento"); dtComandosAtu.Columns.Clear(); - dtComandosAtu.Columns.Add("clB0", "B0"); - dtComandosAtu.Columns.Add("clB1", "B1"); - dtComandosAtu.Columns.Add("clB2", "B2"); - dtComandosAtu.Columns.Add("clB3", "B3"); + for (int i = 1; i <= VariaveisEquipamento.QuantidadeBicosPulverizadores; i++) + dtComandosAtu.Columns.Add($"clB{i}", $"B{i}"); } private void InicializarCombosBusca() @@ -1029,10 +1031,10 @@ namespace AgroBase.Forms.Operacoes private Bitmap CarregarImagemCamera(string Caminho, string complemento = "") { - string caminhoCompleto = Caminho + trbMomento.Value + ".jpeg"; + string caminhoCompleto = Caminho + trbMomento.Value + ".jpg"; if (!string.IsNullOrEmpty(complemento)) { - caminhoCompleto = caminhoCompleto.Replace(".jpeg", $"{complemento}.jpeg"); + caminhoCompleto = caminhoCompleto.Replace(".jpg", $"{complemento}.jpg"); } Bitmap frame = new Bitmap(1, 1); using (Graphics g = Graphics.FromImage(frame)) @@ -1126,6 +1128,7 @@ namespace AgroBase.Forms.Operacoes { var Log = LogsOperacao[idxMomentoAtual]; var LogAtu = LogsAtuador[idxMomentoAtual]; + var LogWw = LogsWeedWorker[idxMomentoAtual]; lblPercentualBateria.Text = $"Bateria {Log.Bateria.PercentualBateria.ToString("0.00")}%"; FuncoesGlobais.PreencheValorProgressBar(pgbPercentualBateria, Log.Bateria.PercentualBateria); @@ -1145,7 +1148,7 @@ namespace AgroBase.Forms.Operacoes txtHerbicidaPorErva.Text = LogAtu.HerbicidaPorAtuacao.ToString("0.00") + " ml"; txtDentroRua.Text = Log.Trajetoria.CorredorAtual.Dentro ? "Sim" : "Não"; txtDirecaoCarro.Text = Log.Trajetoria.DirecaoCaminho.ToString() + " (" + Log.Trajetoria.CorredorAtual.Idx + " - " + Log.Trajetoria.ProximoPonto.idxPonto + " - " + Log.Trajetoria.ProximoPonto.idxPontoCorredor + ")"; - txtErvasNoRadar.Text = Log.Atuador.ErvasNoRadar.ToString() + " (" + (Log.Atuador.PercentualErvasNoRadar * 100).ToString("0.00") + "%)"; + txtErvasNoRadar.Text = LogWw.Analise.ervas_no_radar + " (" + (LogWw.Analise.percentual_ervas_no_radar).ToString("0.00") + "%)"; txtErvasTerreno.Text = Log.Atuador.PercentualErvasTerreno.ToString("0.00"); txtTipoMovimento.Text = Log.Controle.TipoMovimento.ToString(); txtVelocidade.Text = (Log.Movimentacao.VelocidadeMedia * 3.6).ToString("0.00") + " km/h (" + Log.Movimentacao.VelocidadeMedia.ToString("0.00") + " m/s)"; diff --git a/AgroBase/AgroBase/Forms/frmOperarioSonar.cs b/AgroBase/AgroBase/Forms/frmOperarioSonar.cs index 3ae4f6b7f..0dfb9317e 100644 --- a/AgroBase/AgroBase/Forms/frmOperarioSonar.cs +++ b/AgroBase/AgroBase/Forms/frmOperarioSonar.cs @@ -155,7 +155,7 @@ namespace AgroBase.Forms lblLeituraDebug.Text = $"Leitura em: " + (analise?.timestamp ?? DateTime.MinValue).ToString("dd/MM/yyyy HH:mm:ss"); OAKCameraFrameModel frame = VisualWorkerService.DadosLeitura.CameraFrames[TipoFrameCamera.Heatmap]; picDebug.Image?.Dispose(); - picDebug.Image = analise.PlotarAnalise(frame); + //picDebug.Image = analise.PlotarAnalise(frame); //PreencherGridAnomalias(analise.anomalias ?? new List()); //PreencherGridSolo(analise.solo?.bbox_list ?? new List()); @@ -254,7 +254,7 @@ namespace AgroBase.Forms chartPerfilSolo.Legends[0].Docking = Docking.Bottom; } - void PlotarGraficoPerfilCorredor(List perfilCorredor) + /*void PlotarGraficoPerfilCorredor(List perfilCorredor) { chartCorredor.Series.Clear(); @@ -303,7 +303,7 @@ namespace AgroBase.Forms chartCorredor.ChartAreas[0].AxisX.Crossing = 0; // 🔥 Eixo X cruza no centro (0 lateral) - } + }*/ private void pnlInclinacao_Paint(object sender, PaintEventArgs e) { diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 48a06aba8..92148ec9a 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -405,6 +405,7 @@ namespace AgroBase.Models AtuPercentualInicioPulverizacao = 70, AtuPressaoLinha = 85, AtuAgitadorModo = ModoAgitadorCalda.SemAgitacao, + AtuModoControle = ModoControleBomba.OnOff }; p.ModulosMandatorios = new List() { @@ -515,7 +516,7 @@ namespace AgroBase.Models AtuPercentualInicioPulverizacao = 70, AtuPressaoLinha = 85, AtuAgitadorModo = ModoAgitadorCalda.Continuo, - AtuModoControle = ModoControleBomba.PID, + AtuModoControle = ModoControleBomba.OnOff, }; p.ModulosMandatorios = new List() { @@ -2087,7 +2088,9 @@ namespace AgroBase.Models SalvarLogLine(T_Code.Sen.ToString(), logSen); SalvarLogCameraSolo(); + SalvarLogLine("WeedWorker", Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorErvas); SalvarLogCameraCaminho(); + SalvarLogLine("VisualWorker", Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual); string ArquivoOp = Variaveis.CaminhoOperacoes + Variaveis.OperacaoEmAndamento.ID + "/data.lgop"; if (!File.Exists(ArquivoOp)) diff --git a/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs b/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs index f5babfda0..eb83f5057 100644 --- a/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs +++ b/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs @@ -1,7 +1,8 @@ -using System; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; -using System.Drawing; using System.Linq; +using Newtonsoft.Json; using static AgroBase.Models.Enums; namespace AgroBase.Models.Operadores @@ -13,12 +14,15 @@ namespace AgroBase.Models.Operadores public bool Pronto { get; set; } public DateTime ProntoEm { get; set; } public DateTime UltimaMensagem { get; set; } + [JsonIgnore] public Dictionary CameraFrames { get; set; } = new Dictionary(); public VisualWorkerMessageAnaliseModel Analises { get; set; } = new VisualWorkerMessageAnaliseModel(); public VisualWorkerMessageIMUModel Imu { get; set; } = new VisualWorkerMessageIMUModel(); public StatusModulo Status { get; set; } public bool StatusValido => new List() { StatusModulo.Operante, StatusModulo.Alerta }.Contains(Status); public VisualWorkerResumoModel Resumo { get; set; } + public VisualWorkerPerformanceModel Performance { get; set; } = new VisualWorkerPerformanceModel(); + public VisualWorkerRedisTimestampsModel Timestamps { get; set; } = new VisualWorkerRedisTimestampsModel(); public VisualWorkerModel Clone() { @@ -33,7 +37,9 @@ namespace AgroBase.Models.Operadores Imu = Imu?.Clone(), UltimaMensagem = UltimaMensagem, Status = Status, - Resumo = Resumo?.Clone() + Resumo = Resumo?.Clone(), + Performance = Performance?.Clone() ?? new VisualWorkerPerformanceModel(), + Timestamps = Timestamps?.Clone() ?? new VisualWorkerRedisTimestampsModel(), }; } @@ -48,6 +54,22 @@ namespace AgroBase.Models.Operadores Imu = new VisualWorkerMessageIMUModel(); Resumo = new VisualWorkerResumoModel(); Status = StatusModulo.Desconectado; + Performance = new VisualWorkerPerformanceModel(); + Timestamps = new VisualWorkerRedisTimestampsModel(); + } + } + + public class VisualWorkerRedisTimestampsModel + { + public double analise { get; set; } + public double segmentacao { get; set; } + public double deteccao { get; set; } + public double matriz_confianca { get; set; } + public double performance_visual { get; set; } + + public VisualWorkerRedisTimestampsModel Clone() + { + return (VisualWorkerRedisTimestampsModel)MemberwiseClone(); } } @@ -124,130 +146,48 @@ namespace AgroBase.Models.Operadores } } - public class VisualWorkerDadosModel - { - public bool DesvioNecessario { get; set; } - public Enums.Direcao DirecaoDesvio { get; set; } - public Obstaculo ObstaculoCritico { get; set; } - public List Obstaculos { get; set; } - - public VisualWorkerDadosModel Clone() - { - return new VisualWorkerDadosModel() - { - DesvioNecessario = DesvioNecessario, - DirecaoDesvio = DirecaoDesvio, - ObstaculoCritico = ObstaculoCritico?.Clone(), - Obstaculos = new List(Obstaculos ?? new List()), - }; - } - } - - - public class VisualWorkerMessageResponseModel - { - public VisualWorkerCommandType cmd { get; set; } - public VisualWorkerMessageResponseSaveFramesModel save_frames { get; set; } - - public VisualWorkerMessageResponseModel Clone() - { - return new VisualWorkerMessageResponseModel() - { - cmd = cmd, - save_frames = save_frames.Clone() - }; - } - } - - public class VisualWorkerMessageResponseSaveFramesModel - { - public long timestamp { get; set; } - public List frames { get; set; } - - private bool _newData = false; - public bool newData - { - get - { - if (_newData) - { - _newData = false; - return true; - } - else - { - return false; - } - } - set - { - _newData = value; - } - } - - public VisualWorkerMessageResponseSaveFramesModel Clone() - { - return new VisualWorkerMessageResponseSaveFramesModel() - { - timestamp = timestamp, - frames = new List(frames), - newData = true - }; - } - } - public class VisualWorkerMessageIMUModel { - public long timestamp { get; set; } + public bool valido { get; set; } + public double timestamp { get; set; } + public double roll { get; set; } public double pitch { get; set; } public double yaw { get; set; } public double yaw_fixed { get; set; } + public double frequencia_hz { get; set; } + public double latencia_ms { get; set; } + + public double rugosidade { get; set; } + public double impacto { get; set; } + public double estabilidade { get; set; } + + public bool movimento_detectado { get; set; } + + [JsonExtensionData] + public IDictionary Extra { get; set; } = new Dictionary(); + public VisualWorkerMessageIMUModel Clone() { - return new VisualWorkerMessageIMUModel() + return new VisualWorkerMessageIMUModel { - timestamp = timestamp, + valido = valido, + timestamp = timestamp, roll = roll, pitch = pitch, yaw = yaw, - }; - } - } - - public class VisualWorkerMessageSetupModel - { - public long timestamp { get; set; } - public bool status { get; set; } - - private bool _newData = false; - public bool newData - { - get - { - if (_newData) - { - _newData = false; - return true; - } - else - { - return false; - } - } - set - { - _newData = value; - } - } - public VisualWorkerMessageSetupModel Clone() - { - return new VisualWorkerMessageSetupModel() - { - timestamp = timestamp, - status = status, - newData = true + yaw_fixed = yaw_fixed, + frequencia_hz = frequencia_hz, + latencia_ms = latencia_ms, + rugosidade = rugosidade, + impacto = impacto, + estabilidade = estabilidade, + movimento_detectado = movimento_detectado, + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() }; } } @@ -255,160 +195,20 @@ namespace AgroBase.Models.Operadores public class VisualWorkerMessageAnaliseModel { public DateTime timestamp { get; set; } - //public List anomalias { get; set; } - //public List sombras { get; set; } - //public VisualWorkerMessageAnaliseSoloModel solo { get; set; } - //public List corredor_prof { get; set; } - //public List corredor_seg { get; set; } - public VisualWorkerMessageSegmentacaoSemanticaModel segmentacao { get; set; } - public VisualWorkerMessageMatrizConfiancaModel matriz_confianca { get; set; } - public List deteccao { get; set; } + public VisualWorkerMessageSegmentacaoSemanticaModel segmentacao { get; set; } = new VisualWorkerMessageSegmentacaoSemanticaModel(); + public VisualWorkerMessageMatrizConfiancaModel matriz_confianca { get; set; } = new VisualWorkerMessageMatrizConfiancaModel(); + public List deteccao { get; set; } = new List(); public VisualWorkerMessageAnaliseModel Clone() { return new VisualWorkerMessageAnaliseModel() { timestamp = timestamp, - //anomalias = new List(anomalias ?? new List()), - //sombras = new List(sombras ?? new List()), - //solo = solo?.Clone(), - //corredor_prof = new List(corredor_prof ?? new List()), - //corredor_seg = new List(corredor_seg ?? new List()), segmentacao = segmentacao?.Clone() ?? new VisualWorkerMessageSegmentacaoSemanticaModel(), matriz_confianca = matriz_confianca?.Clone() ?? new VisualWorkerMessageMatrizConfiancaModel(), deteccao = deteccao?.Select(x => x.Clone()).ToList() ?? new List() }; } - - public Bitmap PlotarAnalise(OAKCameraFrameModel frame) - { - return null; - /*Bitmap img = frame?.image(); - if (img == null) return null; - - using (Graphics g = Graphics.FromImage(img)) - { - Pen penObstaculo = new Pen(Color.Red, 2); - Pen penErosao = new Pen(Color.Blue, 2); - Pen penLombada = new Pen(Color.Orange, 2); - Pen penFaixaSolo = new Pen(Color.Black, 2); - Pen penGrafico = new Pen(Color.DarkGreen, 2); - - Font font = new Font("Arial", 8); - Brush brush = Brushes.Black; - - // 🔸 Definir brushes semi-transparentes para preenchimento - Brush brushObstaculo = new SolidBrush(Color.FromArgb(60, Color.Red)); - Brush brushErosao = new SolidBrush(Color.FromArgb(60, Color.Blue)); - Brush brushLombada = new SolidBrush(Color.FromArgb(60, Color.Orange)); - - int imgWidth = img.Width; - int imgHeight = img.Height; - - // 🔸 Desenhar Bounding Boxes (Anomalias + Solo) - List bboxes = new List(); - if (anomalias != null) bboxes.AddRange(anomalias); - //if (solo?.bbox_list != null) bboxes.AddRange(solo.bbox_list); - - foreach (var bbox in bboxes) - { - Pen pen = penObstaculo; - Brush fill = brushObstaculo; - - if (bbox.tipo == "erosao") - { - pen = penErosao; - fill = brushErosao; - } - if (bbox.tipo == "lombada") - { - pen = penLombada; - fill = brushLombada; - } - - int x = (int)(bbox.bbox[0] * imgWidth); - int y = (int)(bbox.bbox[1] * imgHeight); - int w = (int)(bbox.bbox[2] * imgWidth); - int h = (int)(bbox.bbox[3] * imgHeight); - var rect = new Rectangle(x, y, w, h); - - // 🔸 Preencher com opacidade - g.FillRectangle(fill, rect); - - // 🔸 Contorno - g.DrawRectangle(pen, rect); - - // 🔸 Label - string label = $"{bbox.tipo} {bbox.distancia_m}m Δ{bbox.variacao}mm"; - g.DrawString(label, font, brush, rect.X, rect.Y - 12); - } - - // 🔸 Linha da faixa do solo - if (solo != null) - { - int yInicio = imgHeight - (int)(solo.percent_y * imgHeight); - g.DrawLine(penFaixaSolo, 0, yInicio, imgWidth, yInicio); - } - - // 🔸 Gráfico do perfil de solo - if (solo?.perfil != null && solo.perfil.Count > 1) - { - var perfil = solo.perfil; - int setores = solo.perfil.Count; - int yInicio = imgHeight - (int)(solo.percent_y * imgHeight); - int alturaRegiao = imgHeight / solo.perfil.Count; - - double maxZ = perfil.Max(); - double minZ = perfil.Min(); - if (maxZ == minZ) maxZ += 1; // evita divisão por zero - - for (int i = 0; i < perfil.Count - 1; i++) - { - float x1 = (float)(i * imgWidth / setores + imgWidth / (2.0 * setores)); - float x2 = (float)((i + 1) * imgWidth / setores + imgWidth / (2.0 * setores)); - - float y1 = (float)(yInicio + alturaRegiao - ((perfil[i] - minZ) / (maxZ - minZ)) * alturaRegiao); - float y2 = (float)(yInicio + alturaRegiao - ((perfil[i + 1] - minZ) / (maxZ - minZ)) * alturaRegiao); - - g.DrawLine(penGrafico, x1, y1, x2, y2); - g.FillEllipse(Brushes.DarkGreen, x1 - 3, y1 - 3, 6, 6); - } - - // Desenha o último ponto - float xLast = (float)((perfil.Count - 1) * imgWidth / setores + imgWidth / (2.0 * setores)); - float yLast = (float)(yInicio + alturaRegiao - ((perfil.Last() - minZ) / (maxZ - minZ)) * alturaRegiao); - g.FillEllipse(Brushes.DarkGreen, xLast - 3, yLast - 3, 6, 6); - } - } - - return img;*/ - } - - } - - public class VisualWorkerMessageAnaliseSoloModel - { - public List perfil { get; set; } - public double percent_y { get; set; } - public double inclinacao { get; set; } - //public List bbox_list { get; set; } - //public int y_inicio { get; set; } - //public int altura_regiao { get; set; } - //public int n_setores { get; set; } - - public VisualWorkerMessageAnaliseSoloModel Clone() - { - return new VisualWorkerMessageAnaliseSoloModel() - { - perfil = new List(perfil), - percent_y = percent_y, - inclinacao = inclinacao, - //bbox_list = new List(bbox_list), - //y_inicio = y_inicio, - //altura_regiao = altura_regiao, - //n_setores = n_setores, - }; - } } public class VisualWorkerMessageAnaliseBBoxModel @@ -423,142 +223,6 @@ namespace AgroBase.Models.Operadores public double? variacao { get; set; } // delta Z ou null } - public class VisualWorkerMessageRadar2DModel - { - public OAKCameraFrameModel frame { get; set; } - public VisualWorkerMessageRadar2DAnaliseModel analise { get; set; } - - private bool _newData = false; - public bool newData - { - get - { - if (_newData) - { - _newData = false; - return true; - } - else - { - return false; - } - } - set - { - _newData = value; - } - } - public VisualWorkerMessageRadar2DModel Clone() - { - return new VisualWorkerMessageRadar2DModel() - { - frame = frame.Clone(), - analise = analise.Clone(), - newData = true - }; - } - - public Bitmap PlotarAnalsie(Bitmap img) - { - /*try - { - using (Graphics g = Graphics.FromImage(img)) - { - var grid = analise.grid_info; - - double cellSize = grid.cell_size; - double escala = grid.escala; - int imgSize = (int)grid.img_size; - int centerX = (int)grid.center_x; - int centerY = (int)grid.center_y; - int gridX = (int)(2 * grid.distancia_max / cellSize); - int gridZ = (int)(grid.distancia_max / cellSize); - - foreach (var bbox in analise.bboxes_sombra) - { - int xGrid = bbox.bbox[0]; - int zGrid = bbox.bbox[1]; - int wGrid = bbox.bbox[2]; - int hGrid = bbox.bbox[3]; - - // 🔸 Converte X (grid → metros → pixels com margem) - int pixelX = (int)(centerX + (xGrid - gridX / 2.0) * cellSize * escala); - - // 🔸 Converte Z/Y (grid → metros → pixels com inversão vertical + margem) - int pixelY = (int)((centerY / 3) + zGrid * cellSize * escala); - - // 🔸 Tamanho da bbox em pixels - int pixelW = (int)(wGrid * cellSize * escala); - int pixelH = (int)(hGrid * cellSize * escala); - - // 🔸 Desenha a bbox - var rect = new Rectangle(pixelX, pixelY, pixelW, pixelH); - g.DrawRectangle(new Pen(Color.Red, 2), rect); - - // 🔸 Desenha o centro da bbox - int centroX = pixelX + pixelW / 2; - int centroY = pixelY + pixelH / 2; - g.FillEllipse(Brushes.Red, centroX - 3, centroY - 3, 6, 6); - } - } - } - catch - { - - }*/ - return img; - } - } - - public class VisualWorkerMessageRadar2DAnaliseModel - { - public List bboxes_sombra { get; set; } - public List corredor_perfil { get; set; } - public VisualWorkerMessageRadar2DGridInfoModel grid_info { get; set; } - - public VisualWorkerMessageRadar2DAnaliseModel Clone() - { - return new VisualWorkerMessageRadar2DAnaliseModel() - { - bboxes_sombra = new List(bboxes_sombra), - corredor_perfil = new List(corredor_perfil), - grid_info = grid_info.Clone() - }; - } - } - - public class VisualWorkerMessageRadar2DPerfilModel - { - public double distancia_m { get; set; } - public double esquerda_m { get; set; } - public double direita_m { get; set; } - public double largura_m { get; set; } - } - - public class VisualWorkerMessageRadar2DGridInfoModel - { - public double escala { get; set; } - public double img_size { get; set; } - public double center_x { get; set; } - public double center_y { get; set; } - public double cell_size { get; set; } - public double distancia_max { get; set; } - public double largura_max { get; set; } - public VisualWorkerMessageRadar2DGridInfoModel Clone() - { - return new VisualWorkerMessageRadar2DGridInfoModel() - { - escala = escala, - img_size = img_size, - center_x = center_x, - center_y = center_y, - cell_size = cell_size, - distancia_max = distancia_max, - largura_max = largura_max - }; - } - } - public class VisualWorkerMessageSegmentacaoSemanticaModel { public double timestamp { get; set; } @@ -569,8 +233,8 @@ namespace AgroBase.Models.Operadores public StatusCarroMapa status_corredor { get; set; } public StatusCarroMapa status_corredor_anterior { get; set; } public VisualWorkerMessageSegmentacaoSemanticaDebugCorredorModel status_corredor_debug { get; set; } - public List centros_corredor { get; set; } - public List larguras_px { get; set; } + public List centros_corredor { get; set; } = new List(); + public List larguras_px { get; set; } = new List(); public VisualWorkerMessageSegmentacaoSemanticaModel Clone() { @@ -582,9 +246,12 @@ namespace AgroBase.Models.Operadores erro_angular = erro_angular, erro_lateral_pct = erro_lateral_pct, status_corredor = status_corredor, - status_corredor_anterior = status_corredor_anterior, - status_corredor_debug = status_corredor_debug, - centros_corredor = new List(centros_corredor ?? new List()), + status_corredor_anterior = status_corredor_anterior, + status_corredor_debug = status_corredor_debug?.Clone(), + centros_corredor = centros_corredor? + .Select(x => x?.ToArray()) + .ToList() + ?? new List(), larguras_px = new List(larguras_px ?? new List()), }; } @@ -601,6 +268,30 @@ namespace AgroBase.Models.Operadores public StatusCarroMapa status_heuristico { get; set; } public string status_heuristico_nome { get; set; } public dynamic heuristica_debug { get; set; } + + public VisualWorkerMessageSegmentacaoSemanticaDebugCorredorModel Clone() + { + return new VisualWorkerMessageSegmentacaoSemanticaDebugCorredorModel + { + origem_status = origem_status, + status_modelo = status_modelo, + status_modelo_nome = status_modelo_nome, + status_modelo_label_name = status_modelo_label_name, + status_modelo_conf = status_modelo_conf, + status_modelo_probs = CloneDynamic(status_modelo_probs), + status_heuristico = status_heuristico, + status_heuristico_nome = status_heuristico_nome, + heuristica_debug = CloneDynamic(heuristica_debug) + }; + } + + private static object CloneDynamic(object valor) + { + if (valor is JToken token) + return token.DeepClone(); + + return valor; + } } public class VisualWorkerMessageMatrizConfiancaModel @@ -609,10 +300,10 @@ namespace AgroBase.Models.Operadores public int seq { get; set; } public int grid_w { get; set; } public int grid_h { get; set; } - public List y_range_m { get; set; } - public List row_dist_m { get; set; } - public List row_scale_x_m { get; set; } - public VisualWorkerMessageMatrizConfiancaBlockModel block { get; set; } + public List y_range_m { get; set; } = new List(); + public List row_dist_m { get; set; } = new List(); + public List row_scale_x_m { get; set; } = new List(); + public VisualWorkerMessageMatrizConfiancaBlockModel block { get; set; } = new VisualWorkerMessageMatrizConfiancaBlockModel(); public VisualWorkerMessageMatrizConfiancaModel Clone() { @@ -622,10 +313,10 @@ namespace AgroBase.Models.Operadores seq = seq, grid_w = grid_w, grid_h = grid_h, - y_range_m = y_range_m, + y_range_m = new List(y_range_m ?? new List()), row_dist_m = new List(row_dist_m ?? new List()), row_scale_x_m = new List(row_scale_x_m ?? new List()), - block = block?.Clone() ?? new VisualWorkerMessageMatrizConfiancaBlockModel() + block = block?.Clone() ?? new VisualWorkerMessageMatrizConfiancaBlockModel(), }; } } @@ -728,9 +419,9 @@ namespace AgroBase.Models.Operadores public string track_status { get; set; } public bool tem_tracker { get; set; } public List xyz_m { get; set; } - public double distancia_m { get; set; } - public double lateral_m { get; set; } - public double altura_relativa_m { get; set; } + public double? distancia_m { get; set; } + public double? lateral_m { get; set; } + public double? altura_relativa_m { get; set; } public List bbox_full { get; set; } public VisualWorkerMessageDeteccaoModel Clone() @@ -739,22 +430,158 @@ namespace AgroBase.Models.Operadores { label_id = label_id, label = label, - bbox_norm = bbox_norm, - bbox_px = bbox_px, + bbox_norm = bbox_norm != null + ? new List(bbox_norm) + : new List(), + bbox_px = bbox_px != null + ? new List(bbox_px) + : new List(), conf = conf, track_id = track_id, track_status = track_status, tem_tracker = tem_tracker, - xyz_m = xyz_m, + xyz_m = xyz_m != null + ? new List(xyz_m) + : new List(), distancia_m = distancia_m, lateral_m = lateral_m, altura_relativa_m = altura_relativa_m, - bbox_full = bbox_full, + bbox_full = bbox_full != null + ? new List(bbox_full) + : new List(), }; } } + public class VisualWorkerPerformanceModel + { + public double timestamp { get; set; } + + public Dictionary loops { get; set; } + = new Dictionary(); + + public JObject sync { get; set; } = new JObject(); + + public JObject camera_cache { get; set; } = new JObject(); + + public VisualWorkerPerformanceCacheModel visual_cache { get; set; } + = new VisualWorkerPerformanceCacheModel(); + + public VisualWorkerPerformancePublicacaoModel pub_debug { get; set; } + = new VisualWorkerPerformancePublicacaoModel(); + + public JObject contadores { get; set; } = new JObject(); + + [JsonExtensionData] + public IDictionary Extra { get; set; } + = new Dictionary(); + + public VisualWorkerPerformanceModel Clone() + { + return new VisualWorkerPerformanceModel + { + timestamp = timestamp, + loops = loops?.ToDictionary( + x => x.Key, + x => x.Value?.Clone() + ) ?? new Dictionary(), + + sync = sync != null ? (JObject)sync.DeepClone() : new JObject(), + camera_cache = camera_cache != null + ? (JObject)camera_cache.DeepClone() + : new JObject(), + + visual_cache = visual_cache?.Clone() + ?? new VisualWorkerPerformanceCacheModel(), + + pub_debug = pub_debug?.Clone() + ?? new VisualWorkerPerformancePublicacaoModel(), + + contadores = contadores != null + ? (JObject)contadores.DeepClone() + : new JObject(), + + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() + }; + } + } + + public class VisualWorkerPerformanceLoopModel + { + public double fps_real { get; set; } + + public JObject periodo_ms { get; set; } = new JObject(); + public JObject latencia_ms { get; set; } = new JObject(); + + public Dictionary metrics_ms { get; set; } + = new Dictionary(); + + [JsonExtensionData] + public IDictionary Extra { get; set; } + = new Dictionary(); + + public VisualWorkerPerformanceLoopModel Clone() + { + return new VisualWorkerPerformanceLoopModel + { + fps_real = fps_real, + periodo_ms = periodo_ms != null + ? (JObject)periodo_ms.DeepClone() + : new JObject(), + + latencia_ms = latencia_ms != null + ? (JObject)latencia_ms.DeepClone() + : new JObject(), + + metrics_ms = metrics_ms?.ToDictionary( + x => x.Key, + x => x.Value != null + ? (JObject)x.Value.DeepClone() + : new JObject() + ) ?? new Dictionary(), + + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() + }; + } + } + + public class VisualWorkerPerformanceCacheModel + { + public double seg_ts { get; set; } + public double det_ts { get; set; } + public double grid_ts { get; set; } + + public bool tem_seg { get; set; } + public bool tem_grid { get; set; } + + public VisualWorkerPerformanceCacheModel Clone() + { + return (VisualWorkerPerformanceCacheModel)MemberwiseClone(); + } + } + + public class VisualWorkerPerformancePublicacaoModel + { + public double ts { get; set; } + public int publicou { get; set; } + public int campos { get; set; } + public double redis_ms { get; set; } + + public VisualWorkerPerformancePublicacaoModel Clone() + { + return (VisualWorkerPerformancePublicacaoModel)MemberwiseClone(); + } + } + + + public enum VisualWorkerCommandType { ScriptCarregado = 1, diff --git a/AgroBase/AgroBase/Models/Operadores/WeedWorkerModel.cs b/AgroBase/AgroBase/Models/Operadores/WeedWorkerModel.cs index f6e267510..990b83105 100644 --- a/AgroBase/AgroBase/Models/Operadores/WeedWorkerModel.cs +++ b/AgroBase/AgroBase/Models/Operadores/WeedWorkerModel.cs @@ -1,5 +1,8 @@ -using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; +using System.Linq; using static AgroBase.Models.Enums; namespace AgroBase.Models.Operadores @@ -11,21 +14,35 @@ namespace AgroBase.Models.Operadores public bool Pronto { get; set; } public DateTime ProntoEm { get; set; } public DateTime UltimaMensagem { get; set; } + [JsonIgnore] public Dictionary CameraFrames { get; set; } = new Dictionary(); public WeedWorkerAnaliseModel Analise { get; set; } = new WeedWorkerAnaliseModel(); + public WeedWorkerPerformanceModel Performance { get; set; } = new WeedWorkerPerformanceModel(); + public WeedWorkerAnaliseMetricasModel MetricasAnalise { get; set; } = new WeedWorkerAnaliseMetricasModel(); + public WeedWorkerRedisTimestampsModel Timestamps { get; set; } = new WeedWorkerRedisTimestampsModel(); public WeedWorkerModel Clone() { - return new WeedWorkerModel() + return new WeedWorkerModel { Iniciado = Iniciado, IniciadoEm = IniciadoEm, Pronto = Pronto, ProntoEm = ProntoEm, - CameraFrames = new Dictionary(CameraFrames ?? new Dictionary()), - Analise = Analise.Clone(), UltimaMensagem = UltimaMensagem, + + Analise = Analise?.Clone() + ?? new WeedWorkerAnaliseModel(), + + MetricasAnalise = MetricasAnalise?.Clone() + ?? new WeedWorkerAnaliseMetricasModel(), + + Performance = Performance?.Clone() + ?? new WeedWorkerPerformanceModel(), + + Timestamps = Timestamps?.Clone() + ?? new WeedWorkerRedisTimestampsModel() }; } @@ -33,9 +50,73 @@ namespace AgroBase.Models.Operadores { Iniciado = false; IniciadoEm = DateTime.MinValue; + Pronto = false; ProntoEm = DateTime.MinValue; - CameraFrames = new Dictionary(); + + UltimaMensagem = DateTime.MinValue; + + CameraFrames = + new Dictionary(); + + Analise = new WeedWorkerAnaliseModel(); + MetricasAnalise = new WeedWorkerAnaliseMetricasModel(); + Performance = new WeedWorkerPerformanceModel(); + Timestamps = new WeedWorkerRedisTimestampsModel(); + } + } + + public class WeedWorkerAnaliseMetricasModel + { + public double ts_analise { get; set; } + + public double fps_model { get; set; } + public double fps_inferencia { get; set; } + + public double infer_ms { get; set; } + public double infer_gpu_ms { get; set; } + + public WeedWorkerAnaliseMetricasModel Clone() + { + return (WeedWorkerAnaliseMetricasModel)MemberwiseClone(); + } + } + + public class WeedWorkerAnaliseEnvelopeModel + { + public double ts_analise { get; set; } + + public double fps_model { get; set; } + public double fps_inferencia { get; set; } + + public double infer_ms { get; set; } + public double infer_gpu_ms { get; set; } + + public WeedWorkerAnaliseModel analise { get; set; } + = new WeedWorkerAnaliseModel(); + + [JsonExtensionData] + public IDictionary Extra { get; set; } + = new Dictionary(); + + public WeedWorkerAnaliseEnvelopeModel Clone() + { + return new WeedWorkerAnaliseEnvelopeModel + { + ts_analise = ts_analise, + fps_model = fps_model, + fps_inferencia = fps_inferencia, + infer_ms = infer_ms, + infer_gpu_ms = infer_gpu_ms, + + analise = analise?.Clone() + ?? new WeedWorkerAnaliseModel(), + + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() + }; } } @@ -51,9 +132,11 @@ namespace AgroBase.Models.Operadores public double timestamp { get; set; } public int height { get; set; } public int width { get; set; } - public Dictionary controle { get; set; } + public Dictionary controle { get; set; } = new Dictionary(); public bool ervas_no_radar { get; set; } - public WeedWorkerAnaliseEstatisticasModel estatisticas { get; set; } + public double fracao_ervas_no_radar { get; set; } + public double percentual_ervas_no_radar { get; set; } + public WeedWorkerAnaliseEstatisticasModel estatisticas { get; set; } = new WeedWorkerAnaliseEstatisticasModel(); public WeedWorkerAnaliseModel Clone() { @@ -64,28 +147,17 @@ namespace AgroBase.Models.Operadores height = height, width = width, ervas_no_radar = ervas_no_radar, + fracao_ervas_no_radar = fracao_ervas_no_radar, + percentual_ervas_no_radar = percentual_ervas_no_radar, estatisticas = estatisticas?.Clone() }; } } - public class WeedWorkerAnaliseDeteccaoModel - { - public int id { get; set; } - public double[] centro { get; set; } - public double[] bbox { get; set; } - public int class_id { get; set; } - public int ultimo_frame { get; set; } - public int frames_detectada { get; set; } - public string status { get; set; } - public string descricao { get; set; } - public double confianca { get; set; } - } - public class WeedWorkerAnaliseEstatisticasModel { - public WeedWorkerAnaliseEstatisticasErvasModel erva { get; set; } - public WeedWorkerAnaliseEstatisticasBicosModel bicos { get; set; } + public WeedWorkerAnaliseEstatisticasErvasModel erva { get; set; } = new WeedWorkerAnaliseEstatisticasErvasModel(); + public WeedWorkerAnaliseEstatisticasBicosModel bicos { get; set; } = new WeedWorkerAnaliseEstatisticasBicosModel(); public WeedWorkerAnaliseEstatisticasModel Clone() { @@ -120,19 +192,62 @@ namespace AgroBase.Models.Operadores public class WeedWorkerAnaliseEstatisticasBicosModel { - public List larguras { get; set; } - public List contagem_cana_px_por_bico { get; set; } - public List contagem_cana_frac_por_bico { get; set; } + public string motivo { get; set; } + + public List frac_erva_por_bico { get; set; } + = new List(); + + public List ema_frac_por_bico { get; set; } + = new List(); + public WeedWorkerAnaliseEstatisticasBicosFaixaModel faixa { get; set; } + = new WeedWorkerAnaliseEstatisticasBicosFaixaModel(); + + public List larguras { get; set; } + = new List(); + + public int shift_px { get; set; } + + public double thr_on_eff { get; set; } + public double thr_off_eff { get; set; } + + public List on_cnt { get; set; } + = new List(); + + public List off_cnt { get; set; } + = new List(); + + public List estado_bico { get; set; } + = new List(); + + [JsonExtensionData] + public IDictionary Extra { get; set; } + = new Dictionary(); public WeedWorkerAnaliseEstatisticasBicosModel Clone() { - return new WeedWorkerAnaliseEstatisticasBicosModel() + return new WeedWorkerAnaliseEstatisticasBicosModel { - larguras = new List(larguras), - contagem_cana_frac_por_bico = new List(contagem_cana_frac_por_bico ?? new List()), - contagem_cana_px_por_bico = new List(contagem_cana_px_por_bico ?? new List()), + motivo = motivo, + frac_erva_por_bico = + new List(frac_erva_por_bico ?? new List()), + ema_frac_por_bico = + new List(ema_frac_por_bico ?? new List()), faixa = faixa?.Clone() + ?? new WeedWorkerAnaliseEstatisticasBicosFaixaModel(), + larguras = + new List(larguras ?? new List()), + shift_px = shift_px, + thr_on_eff = thr_on_eff, + thr_off_eff = thr_off_eff, + on_cnt = new List(on_cnt ?? new List()), + off_cnt = new List(off_cnt ?? new List()), + estado_bico = + new List(estado_bico ?? new List()), + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() }; } } @@ -152,6 +267,144 @@ namespace AgroBase.Models.Operadores } } + + public class WeedWorkerPerformanceModel + { + public double timestamp { get; set; } + + public Dictionary loops { get; set; } + = new Dictionary(); + + public JObject sync { get; set; } = new JObject(); + + public WeedWorkerPerformanceCacheModel weed_cache { get; set; } + = new WeedWorkerPerformanceCacheModel(); + + public WeedWorkerPerformancePublicacaoModel pub_debug { get; set; } + = new WeedWorkerPerformancePublicacaoModel(); + + public JObject contadores { get; set; } = new JObject(); + + [JsonExtensionData] + public IDictionary Extra { get; set; } + = new Dictionary(); + + public WeedWorkerPerformanceModel Clone() + { + return new WeedWorkerPerformanceModel + { + timestamp = timestamp, + + loops = loops?.ToDictionary( + x => x.Key, + x => x.Value?.Clone() + ) ?? new Dictionary(), + + sync = sync != null + ? (JObject)sync.DeepClone() + : new JObject(), + + weed_cache = weed_cache?.Clone() + ?? new WeedWorkerPerformanceCacheModel(), + + pub_debug = pub_debug?.Clone() + ?? new WeedWorkerPerformancePublicacaoModel(), + + contadores = contadores != null + ? (JObject)contadores.DeepClone() + : new JObject(), + + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() + }; + } + } + + public class WeedWorkerPerformanceLoopModel + { + public double fps_real { get; set; } + + public JObject periodo_ms { get; set; } = new JObject(); + public JObject latencia_ms { get; set; } = new JObject(); + + public Dictionary metrics_ms { get; set; } + = new Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public IDictionary Extra { get; set; } + = new Dictionary(); + + public WeedWorkerPerformanceLoopModel Clone() + { + return new WeedWorkerPerformanceLoopModel + { + fps_real = fps_real, + + periodo_ms = periodo_ms != null + ? (JObject)periodo_ms.DeepClone() + : new JObject(), + + latencia_ms = latencia_ms != null + ? (JObject)latencia_ms.DeepClone() + : new JObject(), + + metrics_ms = metrics_ms?.ToDictionary( + x => x.Key, + x => x.Value != null + ? (JObject)x.Value.DeepClone() + : new JObject() + ) ?? new Dictionary(), + + Extra = Extra?.ToDictionary( + x => x.Key, + x => x.Value?.DeepClone() + ) ?? new Dictionary() + }; + } + } + + public class WeedWorkerPerformanceCacheModel + { + public double tensor_ts { get; set; } + public double? tensor_age_ms { get; set; } + public double tensor_consumido_ts { get; set; } + public bool tem_tensor { get; set; } + + public WeedWorkerPerformanceCacheModel Clone() + { + return (WeedWorkerPerformanceCacheModel)MemberwiseClone(); + } + } + + public class WeedWorkerPerformancePublicacaoModel + { + public double ts { get; set; } + public int publicou { get; set; } + public int campos { get; set; } + public double redis_ms { get; set; } + + public WeedWorkerPerformancePublicacaoModel Clone() + { + return (WeedWorkerPerformancePublicacaoModel)MemberwiseClone(); + } + } + + public class WeedWorkerRedisTimestampsModel + { + public double analise { get; set; } + public double analise_weed { get; set; } + public double performance_weed { get; set; } + + public WeedWorkerRedisTimestampsModel Clone() + { + return (WeedWorkerRedisTimestampsModel)MemberwiseClone(); + } + } + + + public enum WeedWorkerCommandType { ScriptCarregado = 1, diff --git a/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs index e50d7991e..217064b47 100644 --- a/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs @@ -18,7 +18,10 @@ namespace AgroBase.Services.Operadores private bool DebugMode = true; - static long _lastTsVW = -1; + private static double _lastTsSegmentacao = -1; + private static double _lastTsDeteccao = -1; + private static double _lastTsMatrizConfianca = -1; + private static double _lastTsPerformanceVisual = -1; public void MostrarLog(string message, int limite = 500) { @@ -143,92 +146,211 @@ namespace AgroBase.Services.Operadores public static void AtualizarAnalise() { - // IMU (simples e direto) - string imuJson = RedisService.Get(RedisService.ModKey(Enums.T_Code.Imu)); - if (!string.IsNullOrWhiteSpace(imuJson)) - { - try - { - DadosLeitura.Imu = JsonConvert.DeserializeObject(imuJson); - } - catch { /* opcional: logar erro */ } - } + AtualizarImu(); - DadosLeitura.Status = HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == Enums.T_Code.Snr)?.status ?? Enums.StatusModulo.Desconectado; + DadosLeitura.Status = + HealthWorkerService.ModulosSaude + .FirstOrDefault(x => x.modulo == Enums.T_Code.Snr)?.status + ?? Enums.StatusModulo.Desconectado; - // VisualWorker - var json = RedisService.Get(CtxKey.DadosVisualWorker); - if (string.IsNullOrWhiteSpace(json)) return; + string json = RedisService.Get(CtxKey.DadosVisualWorker); + + if (string.IsNullOrWhiteSpace(json)) + return; JObject root; + try { root = JObject.Parse(json); } + catch (Exception ex) + { + // Opcional: registrar payload inválido + return; + } + + double tsAnalise = + root["ts_analise"]?.Value() ?? 0.0; + + double tsSegmentacao = + root["ts_segmentacao"]?.Value() ?? 0.0; + + double tsDeteccao = + root["ts_deteccao"]?.Value() ?? 0.0; + + double tsMatriz = + root["ts_matriz_confianca"]?.Value() ?? 0.0; + + double tsPerformance = + root["ts_performance_visual"]?.Value() ?? 0.0; + + DadosLeitura.Timestamps.analise = tsAnalise; + DadosLeitura.Timestamps.segmentacao = tsSegmentacao; + DadosLeitura.Timestamps.deteccao = tsDeteccao; + DadosLeitura.Timestamps.matriz_confianca = tsMatriz; + DadosLeitura.Timestamps.performance_visual = tsPerformance; + + if (tsAnalise > 0) + { + DadosLeitura.Analises.timestamp = + FuncoesGlobais.UnixToDateTime(tsAnalise.ToString( + System.Globalization.CultureInfo.InvariantCulture + )); + } + + if (tsSegmentacao > 0 && + tsSegmentacao != _lastTsSegmentacao) + { + var token = root["segmentacao"]; + + if (token != null && + token.Type != JTokenType.Null) + { + try + { + var model = + token.ToObject(); + + if (model != null) + DadosLeitura.Analises.segmentacao = model; + } + catch + { + // Opcional: registrar erro de contrato + } + } + + _lastTsSegmentacao = tsSegmentacao; + } + + if (tsDeteccao > 0 && + tsDeteccao != _lastTsDeteccao) + { + var token = root["deteccao"]; + + if (token != null && + token.Type != JTokenType.Null) + { + try + { + var model = + token.ToObject>(); + + DadosLeitura.Analises.deteccao = + model ?? new List(); + } + catch + { + // Opcional: registrar erro de contrato + } + } + + _lastTsDeteccao = tsDeteccao; + } + + if (tsMatriz > 0 && + tsMatriz != _lastTsMatrizConfianca) + { + var token = root["matriz_confianca"]; + + if (token != null && + token.Type != JTokenType.Null) + { + try + { + var model = + token.ToObject(); + + if (model != null) + DadosLeitura.Analises.matriz_confianca = model; + } + catch + { + // Opcional: registrar erro de contrato + } + } + + _lastTsMatrizConfianca = tsMatriz; + } + + if (tsPerformance > 0 && + tsPerformance != _lastTsPerformanceVisual) + { + var token = root["performance_visual"]; + + if (token != null && + token.Type != JTokenType.Null) + { + try + { + var model = + token.ToObject(); + + if (model != null) + { + model.timestamp = tsPerformance; + DadosLeitura.Performance = model; + } + } + catch + { + // Opcional: registrar erro de contrato + } + } + + _lastTsPerformanceVisual = tsPerformance; + } + } + + private static void AtualizarImu() + { + try + { + string mxId = Variaveis.OperacaoEmAndamento?.DispSen?.Dados?.CameraCaminho?.Id; + + if (string.IsNullOrWhiteSpace(mxId)) + return; + + string cameraJson = RedisService.Get(RedisService.CamKey(mxId)); + + if (string.IsNullOrWhiteSpace(cameraJson)) + return; + + JObject root = JObject.Parse(cameraJson); + + JToken imuToken = root["imu"]; + + if (imuToken == null || + imuToken.Type == JTokenType.Null) + return; + + var imu = imuToken.ToObject(); + + if (imu != null) + DadosLeitura.Imu = imu; + } catch { - return; // payload inválido - } - - // ts_analise (Unix) - long tsUnix = root["ts_analise"]?.Value() ?? 0; - if (tsUnix != 0) - { - // atualiza o DateTime sempre que vier um ts válido - DadosLeitura.Analises.timestamp = FuncoesGlobais.UnixToDateTime(tsUnix.ToString()); - } - - // Se o timestamp não mudou, evita desserializar objetos grandes - if (tsUnix != 0 && tsUnix == _lastTsVW) - return; - - _lastTsVW = tsUnix; - - // segmentação (sem re-serializar o token) - var segTok = root["segmentacao"]; - if (segTok != null) - { - try - { - var seg = segTok.ToObject(); - if (seg != null) DadosLeitura.Analises.segmentacao = seg; - } - catch { /* opcional: log */ } - } - - // matriz de confiança - var mcTok = root["matriz_confianca"]; - if (mcTok != null) - { - try - { - var mc = mcTok.ToObject(); - if (mc != null) DadosLeitura.Analises.matriz_confianca = mc; - } - catch { /* opcional: log */ } - } - - // deteccao (sem re-serializar o token) - var detTok = root["deteccao"]; - if (detTok != null) - { - try - { - var det = detTok.ToObject>(); - if (det != null) DadosLeitura.Analises.deteccao = det; - } - catch { /* opcional: log */ } + // Mantém a última leitura válida. } } public static void ReiniciarLeituraAnalise() { RedisService.AtualizarCampos( - CtxKey.DadosWeedWorker, - ("analise.segmentacao", new VisualWorkerMessageSegmentacaoSemanticaModel()), - ("analise.matriz_confianca", new VisualWorkerMessageMatrizConfiancaModel()), - ("analise.deteccao", new List()) + CtxKey.DadosVisualWorker, + ("segmentacao", new VisualWorkerMessageSegmentacaoSemanticaModel()), + ("matriz_confianca", new VisualWorkerMessageMatrizConfiancaModel()), + ("deteccao", new List()), + ("ts_segmentacao", 0.0), + ("ts_matriz_confianca", 0.0), + ("ts_deteccao", 0.0) ); + + _lastTsSegmentacao = -1; + _lastTsMatrizConfianca = -1; + _lastTsDeteccao = -1; } } diff --git a/AgroBase/AgroBase/Services/Operadores/WeedWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/WeedWorkerService.cs index d1b0c909e..e76c0ad17 100644 --- a/AgroBase/AgroBase/Services/Operadores/WeedWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/WeedWorkerService.cs @@ -18,6 +18,9 @@ namespace AgroBase.Services.Operadores private bool DebugMode = true; + private static double _lastTsAnaliseWeed = -1; + private static double _lastTsPerformanceWeed = -1; + public void MostrarLog(string message, int limite = 500) { if (DebugMode) @@ -171,20 +174,106 @@ namespace AgroBase.Services.Operadores public static void AtualizarAnalise() { - var json = RedisService.Get(CtxKey.DadosWeedWorker); - if (string.IsNullOrWhiteSpace(json)) return; + string json = RedisService.Get(CtxKey.DadosWeedWorker); - var root = JObject.Parse(json); - var analise = root["analise"] as JObject; - if (analise == null) return; + if (string.IsNullOrWhiteSpace(json)) + return; + + JObject root; try { - DadosLeitura.Analise = JsonConvert.DeserializeObject(root["analise"].ToString()); + root = JObject.Parse(json); } catch (Exception ex) { - Console.WriteLine($"Erro ao deserializar analise de ervas: {ex.Message}"); + Console.WriteLine( + $"Erro ao interpretar DadosWeedWorker: {ex.Message}" + ); + return; + } + + double tsAnalise = + root["ts_analise"]?.Value() ?? 0.0; + + double tsAnaliseWeed = + root["ts_analise_weed"]?.Value() ?? 0.0; + + double tsPerformance = + root["ts_performance_weed"]?.Value() ?? 0.0; + + DadosLeitura.Timestamps.analise = tsAnalise; + DadosLeitura.Timestamps.analise_weed = tsAnaliseWeed; + DadosLeitura.Timestamps.performance_weed = tsPerformance; + + if (tsAnaliseWeed > 0 && + tsAnaliseWeed != _lastTsAnaliseWeed) + { + JToken envelopeToken = root["analise"]; + + if (envelopeToken != null && + envelopeToken.Type != JTokenType.Null) + { + try + { + var envelope = + envelopeToken.ToObject(); + + if (envelope != null) + { + DadosLeitura.MetricasAnalise = + new WeedWorkerAnaliseMetricasModel + { + ts_analise = envelope.ts_analise, + fps_model = envelope.fps_model, + fps_inferencia = envelope.fps_inferencia, + infer_ms = envelope.infer_ms, + infer_gpu_ms = envelope.infer_gpu_ms + }; + + DadosLeitura.Analise = + envelope.analise ?? new WeedWorkerAnaliseModel(); + } + } + catch (Exception ex) + { + Console.WriteLine( + $"Erro ao desserializar análise de ervas: {ex.Message}" + ); + } + } + + _lastTsAnaliseWeed = tsAnaliseWeed; + } + + if (tsPerformance > 0 && + tsPerformance != _lastTsPerformanceWeed) + { + JToken performanceToken = root["performance_weed"]; + + if (performanceToken != null && + performanceToken.Type != JTokenType.Null) + { + try + { + var performance = + performanceToken.ToObject(); + + if (performance != null) + { + performance.timestamp = tsPerformance; + DadosLeitura.Performance = performance; + } + } + catch (Exception ex) + { + Console.WriteLine( + $"Erro ao desserializar performance weed: {ex.Message}" + ); + } + } + + _lastTsPerformanceWeed = tsPerformance; } } @@ -192,11 +281,36 @@ namespace AgroBase.Services.Operadores { RedisService.AtualizarCampos( CtxKey.DadosWeedWorker, - ("analise.ervas_no_radar", false), - ("analise.percentual_ervas_no_radar", 0), - ("analise.controle", new Dictionary(Variaveis.OperacaoEmAndamento.DispAtu.Dados.BicosPulverizadores.ToDictionary(x => x.Posicao.ToString(), x => x.ComandoAtuar))), - ("analise.estatisticas", new WeedWorkerAnaliseEstatisticasModel()) + + ("analise.analise.ervas_no_radar", false), + + ("analise.analise.controle", + new Dictionary( + Variaveis.OperacaoEmAndamento + .DispAtu + .Dados + .BicosPulverizadores + .ToDictionary( + x => x.Posicao.ToString(), + x => false + ) + ) + ), + + ("analise.analise.estatisticas", + new WeedWorkerAnaliseEstatisticasModel() + ), + + ("ts_analise_weed", 0.0) ); + + _lastTsAnaliseWeed = -1; + + DadosLeitura.Analise = + new WeedWorkerAnaliseModel(); + + DadosLeitura.MetricasAnalise = + new WeedWorkerAnaliseMetricasModel(); } } diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/manager.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/manager.py index dd1b2a55f..964854f88 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/manager.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/manager.py @@ -8,6 +8,66 @@ class CameraManager: def __init__(self, mostrar_log): self.mostrar_log = mostrar_log self._ultimo_scan = 0 + self._resetar_estados_volateis_cameras() + + def _resetar_estados_volateis_cameras(self): + """ + Limpa estados que pertencem à execução anterior dos workers. + + Esses campos não podem ser confiados após o camera_worker reiniciar, + pois podem ter permanecido como True no Redis mesmo com os pipelines + já encerrados. + """ + try: + agora = time.time() + cameras_mapeadas = ContextoGlobalRedis.get_cameras() or {} + + if not cameras_mapeadas: + return + + for mx_id, camera_resumo in list(cameras_mapeadas.items()): + if not isinstance(camera_resumo, dict): + camera_resumo = {} + cameras_mapeadas[mx_id] = camera_resumo + + # Estado resumido dentro de DadosCameras + camera_resumo["iniciando"] = False + camera_resumo["iniciado"] = False + camera_resumo["rodando"] = False + camera_resumo["presente_scan"] = False + camera_resumo["status_scan"] = "aguardando_scan" + + # Não atualizar o timestamp de heartbeat. + # Se atualizarmos, uma câmera antiga parecerá recente. + camera_resumo["manager_reset_ts"] = agora + + # Estado detalhado da câmera em CamKey(mx_id) + camera_detalhada = ContextoGlobalRedis.get_camera(mx_id) or {} + + camera_detalhada["iniciando"] = False + camera_detalhada["iniciado"] = False + camera_detalhada["rodando"] = False + camera_detalhada["manager_reset_ts"] = agora + + ContextoGlobalRedis.set( + ContextoGlobalRedis.CamKey(mx_id), + camera_detalhada + ) + + ContextoGlobalRedis.set( + CtxKey.DadosCameras, + cameras_mapeadas + ) + + self.mostrar_log( + f"[CameraManager] Estados voláteis resetados para " + f"{len(cameras_mapeadas)} câmera(s)." + ) + + except Exception as e: + self.mostrar_log( + f"[CameraManager] Erro ao resetar estados das câmeras: {e}" + ) def atualizar_cameras(self): agora = time.time() @@ -88,6 +148,9 @@ class CameraManager: cam = ContextoGlobalRedis.get_camera(cam_id) or val timestamp = cam.get("timestamp", val.get("timestamp", 0)) + iniciando = cam.get("iniciando", val.get("iniciando", False)) + iniciado = cam.get("iniciado", val.get("iniciado", False)) + idade = agora - timestamp #self.mostrar_log(f"mx_id: {cam_id}, ts: {timestamp}, idade: {idade}") @@ -114,7 +177,7 @@ class CameraManager: val["status_scan"] = "provavelmente_em_uso" # Se passou muito tempo sem scan e sem saúde, remove - elif not vista_no_scan and idade > TIMEOUT_CAMERA_SUMIR: + elif not vista_no_scan and idade > TIMEOUT_CAMERA_SUMIR and not iniciando and not iniciado: ids_para_remover.append(cam_id) # Se apareceu no scan, mantém @@ -193,7 +256,7 @@ def definir_saude_camera(mx_id: str, status: StatusModulo, saude: int, motivos: "status": status.value, "saude": saude, "motivos": motivos, - "saude_idividual": [] + "saude_individual": [] } #if not conectado: # _cameras[mx_id] = {} diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py index bc722588a..dee483851 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py @@ -117,7 +117,7 @@ VISUAL_DEFAULT_CONFIG = { "debug_visual": False, # Publica logs de performance no console. - "debug_perf": True, + "debug_perf": False, # Tamanho dos frames de preview/debug enviados ao C#. "preview_size": (1280, 720), diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/main.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/main.py index df530f875..66930fbdc 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/main.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/main.py @@ -77,6 +77,22 @@ def main(): def inicializar(): mostrar_log("🚀 Iniciando Visual Worker...") + ContextoGlobalRedis.set( + CtxKey.DadosVisualWorker, + { + "ts_analise": time.time(), + + "ts_segmentacao": 0.0, + "ts_deteccao": 0.0, + "ts_matriz_confianca": 0.0, + "ts_performance_visual": 0.0, + + "segmentacao": None, + "deteccao": None, + "matriz_confianca": None, + "performance_visual": None, + } + ) ContextoGlobalRedis.ouvir_comandos(CmdKey.VisualWorkerRx, redis_callback) ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerTx, { "cmd": VisualWorkerCommandType.ScriptCarregado.value }) loop_ativo() diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py index 6d8f6c45b..903deb615 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py @@ -43,7 +43,7 @@ WEED_DEFAULT_CONFIG = { "debug_visual": False, # Publica logs de performance no console. - "debug_perf": True, + "debug_perf": False, # Inclui tempos internos do WeedDetector no payload de análise. # Barato e útil nesta fase; pode desligar na versão final final. diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py index 5e2a716d9..d9b1f9b9b 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py @@ -75,6 +75,16 @@ def main(): def inicializar(): mostrar_log("🚀 Iniciando Weed Worker...") + ContextoGlobalRedis.set( + CtxKey.DadosWeedWorker, + { + "ts_analise": time.time(), + "ts_analise_weed": 0.0, + "ts_performance_weed": 0.0, + "analise": None, + "performance_weed": None, + } + ) ContextoGlobalRedis.ouvir_comandos(CmdKey.WeedWorkerRx, redis_callback) ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerTx, { "cmd": WeedWorkerCommandType.ScriptCarregado.value }) loop_ativo() diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py index f9caaaad1..baecbf6c5 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py @@ -196,6 +196,8 @@ class WeedDetector: # Mantém o nome antigo para não quebrar payload/C# agora. # Semântica atual: True se há alvo pulverizável no radar. "ervas_no_radar": bool(target_no_radar), + "fracao_ervas_no_radar": float(estat_target.get("frac_global", 0.0)), + "percentual_ervas_no_radar": float(estat_target.get("frac_global", 0.0) * 100.0), "estatisticas": { # Mantém a chave "erva" para compatibilidade do payload.