diff --git a/AgroBase/AgroBase/Forms/Atuador/frmAtuCalibragem.cs b/AgroBase/AgroBase/Forms/Atuador/frmAtuCalibragem.cs index e4fbc7ae9..a6e0ecfe4 100644 --- a/AgroBase/AgroBase/Forms/Atuador/frmAtuCalibragem.cs +++ b/AgroBase/AgroBase/Forms/Atuador/frmAtuCalibragem.cs @@ -30,8 +30,8 @@ namespace AgroBase.Forms.Atuador nudPressaoSP.Value = Convert.ToInt32(Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha); txtPressaoVmin.Text = DispAtu.Dados._PressaoVmin.ToString(); txtPressaoVmax.Text = DispAtu.Dados._PressaoVmax.ToString(); - txtMLRef.Text = DispAtu.Dados._FluxoMlRef.ToString(); - txtPulsosRef.Text = DispAtu.Dados._FluxoPulsoRef.ToString(); + txtMLRef.Text = DispAtu.Dados.VazaoFluxoMLpSReferencia.ToString(); + //txtPulsosRef.Text = DispAtu.Dados._FluxoPulsoRef.ToString(); txtVolume.Text = DispAtu.Dados.VolumeVazaoAferido.ToString(); grdSensoriamento.Rows.Clear(); @@ -207,8 +207,8 @@ namespace AgroBase.Forms.Atuador Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha = Convert.ToDouble(nudPressaoSP.Value); DispAtu.Dados._PressaoVmin = Convert.ToDouble(txtPressaoVmin.Text); DispAtu.Dados._PressaoVmax = Convert.ToDouble(txtPressaoVmax.Text); - DispAtu.Dados._FluxoMlRef = Convert.ToDouble(txtMLRef.Text); - DispAtu.Dados._FluxoPulsoRef = Convert.ToDouble(txtPulsosRef.Text); + //DispAtu.Dados._FluxoMlRef = Convert.ToDouble(txtMLRef.Text); + //DispAtu.Dados._FluxoPulsoRef = Convert.ToDouble(txtPulsosRef.Text); DispAtu.Dados.VolumeVazaoAferido = Convert.ToDouble(txtVolume.Text); } diff --git a/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs b/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs index f3dac7ecd..02e2f3ee3 100644 --- a/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs +++ b/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs @@ -1,9 +1,6 @@ using AgroBase.Models; using AgroBase.Models.Components; -using AgroBase.Models.Operadores; -using AgroBase.Services; using AgroBase.Services.Operadores; -using Assimp; using System; using System.Collections.Generic; using System.Data; @@ -264,14 +261,14 @@ namespace AgroBase.Forms.IHM var cameras = CameraWorkerService.GetListaCameras(); ListaCameras = cameras.Select(x => new OAKCameraModel() { - Index = 0, + Index = cameras.IndexOf(x), Dispositivo = x.dispositivo, Version = x.versao, Name = x.modelo, Id = x.mx_id, }).ToList(); - CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.Clone(); + CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.Clone(); CamerasSolo = new List(Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo); for (int i = 0; i < Variaveis.OperacaoEmAndamento.Parametros.QtdCamerasSolo; i++) @@ -281,7 +278,7 @@ namespace AgroBase.Forms.IHM cmbCameraCaminho.Items.Clear(); cmbCameraCaminho.Items.AddRange(ListaCameras.Select(x => x.Description).ToArray()); - var cameraSelecionada = ListaCameras.FirstOrDefault(x => x.Id == CameraCaminho.Id); + var cameraSelecionada = ListaCameras.FirstOrDefault(x => x.Id == CameraCaminho?.Id); if (cameraSelecionada != null) { cmbCameraCaminho.SelectedIndex = cmbCameraCaminho.Items.IndexOf(cameraSelecionada.Description); @@ -344,7 +341,7 @@ namespace AgroBase.Forms.IHM CamerasSolo.Add(new OAKCameraModel()); } - var cameraSelecionada = ListaCameras.FirstOrDefault(x => x.Id == CamerasSolo[idx].Id); + var cameraSelecionada = ListaCameras.FirstOrDefault(x => x.Id == CamerasSolo[idx]?.Id); if (cameraSelecionada != null) { @@ -410,11 +407,11 @@ namespace AgroBase.Forms.IHM { if (idx == -1) { - emUso = CamerasSolo.Any(x => x.Id == cam.Id); + emUso = CamerasSolo.Any(x => cam.Id != null && x.Id == cam.Id); } else { - emUso = (CameraCaminho.Id == cam.Id) || (CamerasSolo.Any(x => x.Index != idx && x.Id == cam.Id)); + emUso = cam.Id != null && ((CameraCaminho?.Id == cam.Id) || (CamerasSolo.Any(x => x?.Index != idx && x?.Id == cam.Id))); } } diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.Designer.cs b/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.Designer.cs index 303c4627b..0358fac74 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.Designer.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.Designer.cs @@ -86,6 +86,7 @@ this.flwCamerasSolo = new System.Windows.Forms.FlowLayoutPanel(); this.chbSonarAtivado = new System.Windows.Forms.CheckBox(); this.lblPressao = new System.Windows.Forms.Label(); + this.lblStatusCarroMapa = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.chartGraficos)).BeginInit(); this.tabControl1.SuspendLayout(); this.tabMapa.SuspendLayout(); @@ -352,6 +353,7 @@ // // tabDeteccoes // + this.tabDeteccoes.Controls.Add(this.lblStatusCarroMapa); this.tabDeteccoes.Controls.Add(this.pnlDeteccoesRua); this.tabDeteccoes.Location = new System.Drawing.Point(4, 22); this.tabDeteccoes.Margin = new System.Windows.Forms.Padding(2); @@ -721,6 +723,16 @@ this.lblPressao.TabIndex = 38; this.lblPressao.Text = "80 psi"; // + // lblStatusCarroMapa + // + this.lblStatusCarroMapa.AutoSize = true; + this.lblStatusCarroMapa.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblStatusCarroMapa.Location = new System.Drawing.Point(10, 269); + this.lblStatusCarroMapa.Name = "lblStatusCarroMapa"; + this.lblStatusCarroMapa.Size = new System.Drawing.Size(168, 16); + this.lblStatusCarroMapa.TabIndex = 1; + this.lblStatusCarroMapa.Text = "Status: Parado | Conf: 1.000"; + // // frmOperacaoManual // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -763,6 +775,7 @@ this.tabInformacoes.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.gridDados)).EndInit(); this.tabDeteccoes.ResumeLayout(false); + this.tabDeteccoes.PerformLayout(); this.tabPulverizador.ResumeLayout(false); this.tabPulverizador.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.nudPotenciaSP)).EndInit(); @@ -835,5 +848,6 @@ private System.Windows.Forms.Label lblPotenciaSP; private System.Windows.Forms.NumericUpDown nudPotenciaSP; private System.Windows.Forms.Label lblPressao; + private System.Windows.Forms.Label lblStatusCarroMapa; } } \ No newline at end of file diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs b/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs index 1c95c2180..accfdb00c 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs @@ -219,6 +219,9 @@ namespace AgroBase.Forms.Operacoes //var frameSeg = await VisualWorkerService.GetCameraFrame(CameraFrameType.Segmentacao); //AtualizarImagemPainel(pnlDeteccoesRua, frameSeg?.image()); + + var dadosSeg = Variaveis.OperacaoEmAndamento.Sensoriamento?.OperadorVisual?.Analises?.segmentacao; + lblStatusCarroMapa.Text = $"Status: {dadosSeg?.status_corredor ?? StatusCarroMapa.Indefinido} | Conf: {dadosSeg?.status_corredor_debug?.status_modelo_conf ?? 0}"; } finally { diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs b/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs index 1600b5103..3453fb959 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs @@ -736,65 +736,44 @@ namespace AgroBase.Forms.Operacoes { new GraficoInterativoSerieModel() { - Titulo = "CPU %", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.CPU_load ?? 0)).ToList(), + Titulo = "GPU Geral %", + Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_load_percent ?? 0)).ToList(), Visivel = true, MostrarValor = true }, new GraficoInterativoSerieModel() { - Titulo = "GPU %", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_load ?? 0)).ToList(), + Titulo = "GPU Core %", + Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_core_load_percent ?? 0)).ToList(), Visivel = true, MostrarValor = true }, new GraficoInterativoSerieModel() { - Titulo = "HDD %", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.HDD_load ?? 0)).ToList(), + Titulo = "GPU Memory %", + Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_memory_load_percent ?? 0)).ToList(), Visivel = true, MostrarValor = true }, new GraficoInterativoSerieModel() { - Titulo = "RAM %", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.RAM_usage ?? 0)).ToList(), + Titulo = "GPU Frame Buffer %", + Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_frame_buffer_load_percent ?? 0)).ToList(), Visivel = true, MostrarValor = true }, new GraficoInterativoSerieModel() { - Titulo = "CPU Temp", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.CPU_temp ?? 0)).ToList(), - Visivel = true, + Titulo = "GPU Video Engine %", + Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_video_engine_load_percent ?? 0)).ToList(), + Visivel = false, MostrarValor = true }, new GraficoInterativoSerieModel() { - Titulo = "GPU Temp", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_temp ?? 0)).ToList(), - Visivel = true, - MostrarValor = true - }, - new GraficoInterativoSerieModel() - { - Titulo = "Threads Ativas", - Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.ActiveThreads ?? 0)).ToList(), - Visivel = true, - MostrarValor = true - }, - new GraficoInterativoSerieModel() - { - Titulo = "Threads de Trabalho %", - Valores = LogsOperacao.Select(x => (double)((x?.DadosPerformance.MaxWorkerThreads ?? 1) - (x?.DadosPerformance.WorkerThreads ?? 0)) / (x?.DadosPerformance.MaxWorkerThreads ?? 1) * 100.0).ToList(), - Visivel = true, - MostrarValor = true - }, - new GraficoInterativoSerieModel() - { - Titulo = "Threads de Portas %", - Valores = LogsOperacao.Select(x => (double)((x?.DadosPerformance.MaxCompletionPortThreads ?? 1) - (x?.DadosPerformance.CompletionPortThreads ?? 0)) / (x?.DadosPerformance.MaxCompletionPortThreads ?? 1) * 100.0).ToList(), - Visivel = true, + Titulo = "GPU Bus Interface %", + Valores = LogsOperacao.Select(x => (double)(x?.DadosPerformance.GPU_bus_interface_load_percent ?? 0)).ToList(), + Visivel = false, MostrarValor = true }, }); @@ -1148,11 +1127,6 @@ namespace AgroBase.Forms.Operacoes var Log = LogsOperacao[idxMomentoAtual]; var LogAtu = LogsAtuador[idxMomentoAtual]; - double herbicidaConsumido = Log.Atuador.HerbicidaConsumido != 0 ? Log.Atuador.HerbicidaConsumido : LogAtu.VolumeVazaoML; - double percentualHerbicidaConsumido = FuncoesMatematicas.Clamp(((herbicidaConsumido / 1000.0) / LogsAtuador.FirstOrDefault().VolumeReservatorio) * 100.0, 0, 100); - int totalAtuacoes = 0; // Log.Atuador.ErvasIdentificadas.Sum(dict => dict.Values.Sum()); - double herbicidaPorErva = Log.Atuador.HerbicidaPorErva != 0 ? Log.Atuador.HerbicidaPorErva : (herbicidaConsumido / (totalAtuacoes > 0 ? totalAtuacoes : 1)); - lblPercentualBateria.Text = $"Bateria {Log.Bateria.PercentualBateria.ToString("0.00")}%"; FuncoesGlobais.PreencheValorProgressBar(pgbPercentualBateria, Log.Bateria.PercentualBateria); @@ -1162,13 +1136,13 @@ namespace AgroBase.Forms.Operacoes lblPercentualReservatorio.Text = $"Reservatório {LogAtu.PercentualReservatorio.ToString("0.00")}%"; FuncoesGlobais.PreencheValorProgressBar(pgbPercentualReservatorio, LogAtu.PercentualReservatorio); - lblHerbicidaConsumido.Text = $"Consumido {herbicidaConsumido.ToString("0.00")} ml"; - FuncoesGlobais.PreencheValorProgressBar(pgbHerbicidaConsumido, percentualHerbicidaConsumido); + lblHerbicidaConsumido.Text = $"Consumido {LogAtu.PercentualHerbicidaConsumidoOperacao.ToString("0.00")} ml"; + FuncoesGlobais.PreencheValorProgressBar(pgbHerbicidaConsumido, LogAtu.PercentualHerbicidaConsumidoOperacao); txtTempoMovimento.Text = FuncoesGlobais.ConverterSegundosParaHHmmss((int)Log.Movimentacao.TempoMovimentoSegs); txtStatusOperacao.Text = Log.StatusOperacao.ToString(); txtStatusCarro.Text = $"{Log.Trajetoria.StatusCarro}"; - txtHerbicidaPorErva.Text = herbicidaPorErva.ToString("0.00") + " ml"; + 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") + "%)"; @@ -1177,7 +1151,7 @@ namespace AgroBase.Forms.Operacoes txtVelocidade.Text = (Log.Movimentacao.VelocidadeMedia * 3.6).ToString("0.00") + " km/h (" + Log.Movimentacao.VelocidadeMedia.ToString("0.00") + " m/s)"; txtDirecaoControle.Text = (Log.Controle?.TiposControle?.FirstOrDefault(x => x.Tipo == Enums.T_Code.Dir)?.DirecaoAtual ?? Enums.Direcao.Parado).ToString() + " (" + (Log.Controle?.Latencia ?? 0).ToString("0.00") + " s)"; txtVelocidadeControle.Text = ((Log.Controle?.PercentualVelocidadeSP ?? 0) / VariaveisEquipamento.RPM_Max_Roda * 100).ToString() + " rpm"; - txtErvasIdentificadas.Text = $"{totalAtuacoes}"; + txtErvasIdentificadas.Text = $"{Log.Controle?.Bicos?.Sum(x => x.Atuacoes) ?? 0}"; txtDistanciasLaterais.Text = (Log.Trajetoria.DistanciaEsquerda * 100.0).ToString("0.00") + " cm / " + (Log.Trajetoria.DistanciaDireita * 100.0).ToString("0.00") + " cm"; txtErroLateral.Text = (Log.Controle.ErroLateral * 100.0).ToString("0.00") + " cm"; diff --git a/AgroBase/AgroBase/Forms/frmWT901C.Designer.cs b/AgroBase/AgroBase/Forms/frmWT901C.Designer.cs index df7ec4002..820bc30fe 100644 --- a/AgroBase/AgroBase/Forms/frmWT901C.Designer.cs +++ b/AgroBase/AgroBase/Forms/frmWT901C.Designer.cs @@ -44,6 +44,8 @@ this.cmbAlgoritmo = new System.Windows.Forms.ComboBox(); this.lblAlgoritmo = new System.Windows.Forms.Label(); this.btnSalvarAlgoritmo = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.cmbDispositivo = new System.Windows.Forms.ComboBox(); ((System.ComponentModel.ISupportInitialize)(this.gridDados)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picBussola)).BeginInit(); this.SuspendLayout(); @@ -218,12 +220,34 @@ this.btnSalvarAlgoritmo.UseVisualStyleBackColor = true; this.btnSalvarAlgoritmo.Click += new System.EventHandler(this.btnSalvarAlgoritmo_Click); // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(575, 629); + this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(58, 13); + this.label4.TabIndex = 62; + this.label4.Text = "Dispositivo"; + // + // cmbDispositivo + // + this.cmbDispositivo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbDispositivo.FormattingEnabled = true; + this.cmbDispositivo.Location = new System.Drawing.Point(638, 625); + this.cmbDispositivo.Name = "cmbDispositivo"; + this.cmbDispositivo.Size = new System.Drawing.Size(121, 21); + this.cmbDispositivo.TabIndex = 61; + // // frmWT901C // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.White; this.ClientSize = new System.Drawing.Size(1088, 660); + this.Controls.Add(this.label4); + this.Controls.Add(this.cmbDispositivo); this.Controls.Add(this.btnSalvarAlgoritmo); this.Controls.Add(this.lblAlgoritmo); this.Controls.Add(this.cmbAlgoritmo); @@ -270,5 +294,7 @@ private System.Windows.Forms.ComboBox cmbAlgoritmo; private System.Windows.Forms.Label lblAlgoritmo; private System.Windows.Forms.Button btnSalvarAlgoritmo; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox cmbDispositivo; } } \ No newline at end of file diff --git a/AgroBase/AgroBase/Forms/frmWT901C.cs b/AgroBase/AgroBase/Forms/frmWT901C.cs index dccddbd39..f2eb14e31 100644 --- a/AgroBase/AgroBase/Forms/frmWT901C.cs +++ b/AgroBase/AgroBase/Forms/frmWT901C.cs @@ -6,6 +6,9 @@ using System.Windows.Forms; using System.Drawing.Drawing2D; using System.Threading.Tasks; using System.Linq; +using AgroBase.Services.Operadores; +using System.Collections.Generic; +using AgroBase.Models.Operadores; namespace AgroBase.Forms { @@ -17,6 +20,7 @@ namespace AgroBase.Forms double AnguloZ = 0; private Visualizador3DService visualizador3D; AsyncTaskTimerModel tmrLeitura; + List ListaCameras = new List(); //WT901C485 WT901C = Variaveis.WT901C; public frmWT901C() @@ -50,6 +54,8 @@ namespace AgroBase.Forms cmbAlgoritmo.Items.Clear(); cmbAlgoritmo.Items.AddRange(new string[] { "6 Eixos", "9 Eixos" }); + + AtualizarListaCameras(); } private void frmWT901C_FormClosing(object sender, FormClosingEventArgs e) @@ -59,7 +65,24 @@ namespace AgroBase.Forms private async Task tmrLeitura_Tick() { - var Imu = Variaveis.OperacaoEmAndamento.Sensoriamento.IMU; + if (cmbDispositivo.SelectedIndex < 0 || cmbDispositivo.SelectedIndex >= ListaCameras.Count) return; + + var Camera = ListaCameras[cmbDispositivo.SelectedIndex]; + if (Camera.imu == null) return; + + if (Camera.mx_id == Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id) + Camera.imu = Variaveis.OperacaoEmAndamento.DispSen.Dados?.CameraCaminho?.cam?.imu; + else if (Variaveis.OperacaoEmAndamento.DispSen.Dados?.CamerasSolo?.Any(x => x?.Id == Camera.mx_id) ?? false) + Camera.imu = Variaveis.OperacaoEmAndamento.DispSen.Dados?.CamerasSolo?.FirstOrDefault(x => x?.Id == Camera.mx_id)?.cam?.imu; + + //OperacaoSensoriamentoLogImuModel Imu = Variaveis.OperacaoEmAndamento.Sensoriamento.IMU; + OperacaoSensoriamentoLogImuModel Imu = new OperacaoSensoriamentoLogImuModel() + { + InclinacaoFrontal = Camera.imu.pitch, + InclinacaoLateral = Camera.imu.roll, + Rotacao = Camera.imu.yaw, + RotacaoCorrigida = Camera.imu.yaw, + }; AnguloX = Imu.InclinacaoFrontal; AnguloY = Imu.InclinacaoLateral; AnguloZ = Imu.Rotacao; @@ -68,6 +91,13 @@ namespace AgroBase.Forms AtualizarDadosTela(); } + private void AtualizarListaCameras() + { + ListaCameras = CameraWorkerService.GetListaCameras(); + cmbDispositivo.Items.Clear(); + cmbDispositivo.Items.AddRange(ListaCameras.Select(x => x.modelo).ToArray()); + } + private void PopularDadosGrid() { gridDados.Rows.Clear(); @@ -371,6 +401,5 @@ namespace AgroBase.Forms { await WT901CService.AlterarAlgoritmo(cmbAlgoritmo.SelectedIndex == 1); } - } } diff --git a/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs b/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs index a62ec3fc8..2d3088e50 100644 --- a/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs +++ b/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs @@ -86,134 +86,144 @@ namespace AgroBase.Models.Modules public HerbicideCounterService ContadorCarga = new HerbicideCounterService(VariaveisEquipamento.CapacidadeReservatorio, VariaveisEquipamento.DensidadeHerbicida); #region VAZAO + private SensorModel SensorFluxoLinha => Sensores.FirstOrDefault(x => x.Componente == S_Code.sFLX && x.ID == "FLXLN"); + public double VolumeVazaoAferido { get; set; } = 0; - public double _FluxoMlRef { get; set; } = 600; - public double _FluxoPulsoRef { get; set; } = 538; + + public double VazaoMaximaSensor_LMin { get; set; } = 20.0; + public double VazaoFluxoMLpSInstantanea => SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Fluxo)?.atual?.valor ?? 0; + public double VazaoFluxoMLpSInstantaneaCalc { get { int pulsos = (int)(SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.QtdPulsos)?.atual?.valor ?? 0.0); - int periodo = (int)(SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Periodo)?.atual?.valor ?? 0.0); - if (pulsos <= 0 || periodo <= 0) return 0.0; - double frequencia = (double)pulsos / (double)periodo * 1000.0; // Hz - - // Equação do fabricante: F = 8.1*q - 3 (q em L/min) + int periodo = (int)(SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Periodo)?.atual?.valor ?? 0.0); + + if (pulsos <= 0 || periodo <= 0) + return 0.0; + + double frequencia = (double)pulsos / periodo * 1000.0; + + // Fabricante: F = 8.1*q - 3, q em L/min double q_L_min = (frequencia + 3.0) / 8.1; - // Limites físicos do sensor (do próprio anúncio): 0.3–10 L/min - if (q_L_min < 0) q_L_min = 0; // evita negativo perto do zero - if (q_L_min > 10.0) q_L_min = 10.0; // opcional: clamp no máximo - // Converte para mL/s - double q_mL_s = q_L_min * (1000.0 / 60.0); - return q_mL_s; + + if (q_L_min < 0) + q_L_min = 0; + + if (q_L_min > VazaoMaximaSensor_LMin) + q_L_min = VazaoMaximaSensor_LMin; + + return q_L_min * 1000.0 / 60.0; } } - private DateTime _ultimoRegistroVazao; // Armazena o horário da última atualização + + public double VazaoFluxoMLpSReferencia + { + get + { + double direta = VazaoFluxoMLpSInstantanea; + + if (direta > 0) + return direta; + + return VazaoFluxoMLpSInstantaneaCalc; + } + } + + private DateTime _ultimoRegistroVazao = DateTime.MinValue; + public double VazaoMediaMLs { get; set; } public double VolumeVazadoML { get; set; } public double TempoAtuado { get; set; } - private List bicosAtivosAnterior; + + private List bicosAtivosAnterior = new List(); + public void AtualizaTempoAtuadoGeral() { DateTime agora = DateTime.Now; - // Quem está ativo AGORA - var bicosAtivos = BicosPulverizadores.Where(x => x.Inicializado && x._EstadoLeitura == Estado.Ligado).ToList(); + var bicosAtivos = BicosPulverizadores.Where(x => x.Inicializado && x.ComandoAtuar).ToList(); bool algumBicoAtivo = bicosAtivos.Count > 0; - bool algumBicoAtivoAnterior = bicosAtivosAnterior?.Any() ?? false; - // Início de uma "sessão" (transição OFF -> ON) if (algumBicoAtivo && !algumBicoAtivoAnterior) - { - _ultimoRegistroVazao = agora; // inicia integração a partir daqui + _ultimoRegistroVazao = agora; - // (opcional) se você quer métricas por sessão, zere volumes/médias aqui - // foreach (var b in BicosPulverizadores) { b.VolumeVazadoML = 0; b.VazaoMediaMLs = 0; } - // _tempoAtuadoBicoSeg.Clear(); - } - - // Fim de sessão (transição ON -> OFF) if (!algumBicoAtivo && algumBicoAtivoAnterior) { AtualizaVazaoEVolume(bicosAtivosAnterior, agora); _ultimoRegistroVazao = DateTime.MinValue; - VazaoMediaMLs = TempoAtuado > 0 ? (VolumeVazadoML / TempoAtuado) : 0; - // zera instantânea quando não tem bico ativo foreach (var b in BicosPulverizadores) b.VazaoInstantaneaMLs = 0; } - // Integra vazão/volume enquanto tiver bico ativo - AtualizaVazaoEVolume(bicosAtivos, agora); + if (algumBicoAtivo) + AtualizaVazaoEVolume(bicosAtivos, agora); + + VazaoMediaMLs = TempoAtuado > 0 ? VolumeVazadoML / TempoAtuado : 0; - // Atualiza indicadores AtualizaPercentualErvas(); bicosAtivosAnterior = new List(bicosAtivos); } + private void AtualizaVazaoEVolume(List bicosAtivos, DateTime agora) { - // Só integra se houver bicos ativos if (bicosAtivos == null || bicosAtivos.Count == 0) return; - // Captura vazão total instantânea (ml/s) - double vazaoTotalInst = VazaoFluxoMLpSInstantanea; - - if (_ultimoRegistroVazao == default || _ultimoRegistroVazao == DateTime.MinValue) + if (_ultimoRegistroVazao == DateTime.MinValue) + { + _ultimoRegistroVazao = agora; return; + } - // dt em segundos double dt = (agora - _ultimoRegistroVazao).TotalSeconds; _ultimoRegistroVazao = agora; - if (dt <= 0) return; + if (dt <= 0 || dt > 2.0) + return; + + double vazaoTotalInst = VazaoFluxoMLpSReferencia; + + if (vazaoTotalInst <= 0) + { + foreach (var bico in bicosAtivos) + bico.VazaoInstantaneaMLs = 0; + + return; + } - // ======== GERAL ======== VolumeVazadoML += vazaoTotalInst * dt; TempoAtuado += dt; - VazaoMediaMLs = TempoAtuado > 0 ? (VolumeVazadoML / TempoAtuado) : 0; - // ======== POR BICO (divide por ativos) ======== double vazaoPorBicoInst = vazaoTotalInst / bicosAtivos.Count; foreach (var bico in bicosAtivos) { - // Instantânea por bico bico.VazaoInstantaneaMLs = vazaoPorBicoInst; - - // Volume por bico (ml) = (ml/s) * s bico.VolumeVazadoML += vazaoPorBicoInst * dt; - bico.TempoAtuado += dt; - bico.VazaoMediaMLs = bico.TempoAtuado > 0 ? (bico.VolumeVazadoML / bico.TempoAtuado) : 0; + bico.VazaoMediaMLs = bico.TempoAtuado > 0 + ? bico.VolumeVazadoML / bico.TempoAtuado + : 0; } } + private void AtualizaPercentualErvas() { - //double tempoTotalOperacao = Variaveis.OperacaoEmAndamento.Sensoriamento.TempoDecorridoSeg * 1000.0; - double tempoTotalOperacao = (Variaveis.OperacaoEmAndamento.Sensoriamento?.Movimentacao?.TempoMovimentoSegs ?? 0) * 1000.0; - - if (tempoTotalOperacao > 0) - { - var percentual = Math.Round((TempoAtuado / tempoTotalOperacao) * 100.0, 2); - PercentualErvasTerreno = percentual; - } - else - { - PercentualErvasTerreno = 0; - } + double tempoTotalOperacao = Variaveis.OperacaoEmAndamento.Sensoriamento?.Movimentacao?.TempoMovimentoSegs ?? 0; + PercentualErvasTerreno = tempoTotalOperacao > 0 ? Math.Round((TempoAtuado / tempoTotalOperacao) * 100.0, 2) : 0; } - #endregion #region PRESSAO diff --git a/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs b/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs index d22eac338..7bc57186b 100644 --- a/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs +++ b/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs @@ -9,6 +9,7 @@ using System.Data; using static AgroBase.Services.LoRaEspService; using AgroBase.Services.Operadores; using System.Diagnostics; +using AgroBase.Models.Operadores; namespace AgroBase.Models.Modules { @@ -86,7 +87,7 @@ namespace AgroBase.Models.Modules _TaxaAmostragem = 1000, RequisitarDados = true, CameraCaminho = new OAKCameraModel(), - CamerasSolo = new List(), + CamerasSolo = new List(Variaveis.OperacaoEmAndamento.Parametros.QtdCamerasSolo ?? VariaveisEquipamento.QuantidadeCamerasSolo), // 21 ~ 23 Sinaleiros = new List() { @@ -2034,11 +2035,6 @@ namespace AgroBase.Models.Modules }, }; - for (int i = 0; i < Variaveis.OperacaoEmAndamento.Parametros.QtdCamerasSolo; i++) - { - obj.CamerasSolo.Add(new OAKCameraModel()); - } - return obj; } @@ -2408,6 +2404,24 @@ namespace AgroBase.Models.Modules Sinaleiros.First(x => x.ID == "LEDOP").Comportamento = VariaveisEquipamento.ComportamentoLedPorStatus(StatusLED.Piscando, status_operacao: _statusOperacao); } + public void AtualizarDadosCameras() + { + if (CameraCaminho?.Id != null) + { + var att = CameraWorkerService.AtualizarDadosCamera(CameraCaminho.Id); + if (att != null) CameraCaminho = att; + } + for (int i = 0; i < CamerasSolo?.Count; i++) + { + if (CamerasSolo[i]?.Id != null) + { + var att = CameraWorkerService.AtualizarDadosCamera(CamerasSolo[i].Id); + if (att != null) CamerasSolo[i] = att; + } + } + + } + } public class SensorModel diff --git a/AgroBase/AgroBase/Models/OAKCameraModel.cs b/AgroBase/AgroBase/Models/OAKCameraModel.cs index 7d64a7201..a3c7fd56e 100644 --- a/AgroBase/AgroBase/Models/OAKCameraModel.cs +++ b/AgroBase/AgroBase/Models/OAKCameraModel.cs @@ -32,6 +32,8 @@ namespace AgroBase.Models public double fx { get; set; } public double baseline { get; set; } + public CameraWorkerItemModel cam { get; set; } + public double DistanciaMaximaVisao { get; set; } = 5000; public double DistanciaMinimaVisao { get; set; } = 500; public double PercentualAlturaSolo { get; set; } = 30; @@ -100,6 +102,7 @@ namespace AgroBase.Models Version = Version, fx = fx, baseline = baseline, + cam = cam, }; } } @@ -167,6 +170,12 @@ namespace AgroBase.Models } public class OAKImuModel + { + public OAKImuDataModel dados { get; set; } + public ManagerWorkerMessageResponseModulosPendentesModel saude { get; set; } + } + + public class OAKImuDataModel { public double timestamp { get; set; } public double roll { get; set; } diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 2c1aacc0b..e6cfe1575 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -1197,7 +1197,12 @@ namespace AgroBase.Models Controle.Reiniciar(); ControleAnterior.Reiniciar(); - DispSen?.Dados?.ContadorCarga?.IniciarOperacao(); + + double distanciaPercorrida = DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0; + double tempoAtuado = DispAtu?.Dados?.TempoAtuado ?? 0; + DispSen?.Dados?.ContadorCarga?.AtualizarConsumo(distanciaPercorrida); + DispSen?.Dados?.ContadorCarga?.IniciarOperacao(distanciaPercorrida); + DispAtu?.Dados?.ContadorCarga?.IniciarOperacao(distanciaPercorrida, tempoAtuado); GPSTrajetoria = new List(); @@ -1234,6 +1239,7 @@ namespace AgroBase.Models Variaveis.OperacaoEmAndamento.Finalizando = false; Variaveis.OperacaoEmAndamento.Trajetoria?.AutonomiaCorredor?.Parar(); Variaveis.OperacaoEmAndamento.DispSen?.Dados?.ContadorCarga?.FinalizarOperacao(); + Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.ContadorCarga?.FinalizarOperacao(); if (Variaveis.OperacaoEmAndamento.Parametros.Modo == ModoOperacao.RetornoBase) { @@ -2026,6 +2032,9 @@ namespace AgroBase.Models MassaReservatorio = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ContadorCarga.MassaAtual_Kg, PercentualReservatorio = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ContadorCarga.PercentualAtual, VolumeReservatorio = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ContadorCarga.VolumeAtual_L, + PercentualHerbicidaConsumido = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ContadorCarga.PercentualConsumidoTotal, + PercentualHerbicidaConsumidoOperacao = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ContadorCarga.PercentualConsumidoOperacao, + HerbicidaPorAtuacao = Variaveis.OperacaoEmAndamento.DispAtu.Dados.ContadorCarga.HerbicidaPorAtuacao_L, PressaoLinha_SP = Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha, PressaoLinha = Variaveis.OperacaoEmAndamento.DispAtu.Dados.PressaoLinha, PotenciaBomba = Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombasPressurizadoras.FirstOrDefault()?._Potencia ?? 0, @@ -2689,16 +2698,17 @@ namespace AgroBase.Models double tmp_max = Math.Max(Variaveis.OperacaoEmAndamento.DadosPerformance?.GPU_temp ?? 0, Variaveis.OperacaoEmAndamento.DadosPerformance?.CPU_temp ?? 0); _DispMvd?.Dados?.AtualizarDadosFreio(); _DispSen?.Dados?.AtualizarStatusSinaleiros(Variaveis.OperacaoEmAndamento.Parametros?.Modo ?? ModoOperacao.NaoDefinido, Variaveis.OperacaoEmAndamento.StatusAtual); + _DispSen?.Dados?.AtualizarDadosCameras(); _DispAtu?.Dados?.AtualizaTempoAtuadoGeral(); _Cooler?.Atualizar(tmp_max, DateTime.Now); _Bateria?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0); - _Reservatorio?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0, Variaveis.OperacaoEmAndamento.TempoDecorridoSegs); + _Reservatorio?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0, Variaveis.OperacaoEmAndamento.TempoDecorridoSegs, _DispMvd?.Dados?.VelocidadeMedia ?? 0); _Reservatorio?.AtualizarAgitadorCalda(pControle?.AtuAgitadorModo ?? ModoAgitadorCalda.SemAgitacao); _Trajetoria?.AtualizarDadosAutonomiaCorredor(); //var sIMU = LivoxManagerProcess.DadosLeitura.imu; string imu = RedisService.Get(RedisService.ModKey(T_Code.Imu)) ?? ""; - var sIMU = JsonConvert.DeserializeObject(imu); + var sIMU = JsonConvert.DeserializeObject(imu); bool imu_iniciado = sIMU?.timestamp > 0 && HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == T_Code.Imu)?.status != StatusModulo.Desconectado; var dadosIMU = sIMU != null ? new OperacaoSensoriamentoLogImuModel() { @@ -2719,9 +2729,12 @@ namespace AgroBase.Models { QtdCamerasSolo = _DispSen.Dados.CamerasSolo.Count, CapacidadeReservatorio = VariaveisEquipamento.CapacidadeReservatorio, - HerbicidaConsumido = _DispAtu.Dados.VolumeVazadoML, + HerbicidaConsumido = _Reservatorio.VolumeConsumidoTotal_L, + HerbicidaConsumidoOperacao = _Reservatorio.VolumeConsumidoOperacao_L, + PercentualHerbicidaConsumido = _Reservatorio.PercentualConsumidoTotal, + PercentualHerbicidaConsumidoOperacao = _Reservatorio.PercentualConsumidoOperacao, PercentualErvasTerreno = _DispAtu.Dados.PercentualErvasTerreno, - HerbicidaPorErva = _Reservatorio.HerbicidaPorAtuacao, + HerbicidaPorErva = _Reservatorio.HerbicidaPorAtuacao_L, ErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.ervas_no_radar ?? false, PercentualErvasNoRadar = WeedWorkerService.DadosLeitura?.Analise?.estatisticas?.erva?.ema_global ?? 0, PercentualReservatorio = _Reservatorio.PercentualAtual, @@ -3029,6 +3042,9 @@ namespace AgroBase.Models public int QtdCamerasSolo { get; set; } public double CapacidadeReservatorio { get; set; } public double HerbicidaConsumido { get; set; } + public double HerbicidaConsumidoOperacao { get; set; } + public double PercentualHerbicidaConsumido { get; set; } + public double PercentualHerbicidaConsumidoOperacao { get; set; } public double PercentualErvasTerreno { get; set; } public double HerbicidaPorErva { get; set; } public bool ErvasNoRadar { get; set; } @@ -3066,6 +3082,9 @@ namespace AgroBase.Models ErvasNoRadar = ErvasNoRadar, PercentualErvasNoRadar = PercentualErvasNoRadar, HerbicidaConsumido = HerbicidaConsumido, + HerbicidaConsumidoOperacao = HerbicidaConsumidoOperacao, + PercentualHerbicidaConsumido = PercentualHerbicidaConsumido, + PercentualHerbicidaConsumidoOperacao = PercentualHerbicidaConsumidoOperacao, HerbicidaPorErva = HerbicidaPorErva, PercentualErvasTerreno = PercentualErvasTerreno, PercentualReservatorio = PercentualReservatorio, @@ -3352,6 +3371,9 @@ namespace AgroBase.Models public double PressaoLinha { get; set; } public double VolumeReservatorio { get; set; } public double PercentualReservatorio { get; set; } + public double PercentualHerbicidaConsumido { get; set; } + public double PercentualHerbicidaConsumidoOperacao { get; set; } + public double HerbicidaPorAtuacao { get; set; } public double PotenciaBomba { get; set; } public bool BombaLigada { get; set; } public double VazaoFluxoMLpSInstantanea { get; set; } diff --git a/AgroBase/AgroBase/Models/Operadores/CameraWorkerModel.cs b/AgroBase/AgroBase/Models/Operadores/CameraWorkerModel.cs index 9df43421d..dc9fcda6c 100644 --- a/AgroBase/AgroBase/Models/Operadores/CameraWorkerModel.cs +++ b/AgroBase/AgroBase/Models/Operadores/CameraWorkerModel.cs @@ -52,6 +52,19 @@ namespace AgroBase.Models.Operadores public SaudeWorkerModel saude { get; set; } public CameraWorkerItemPerformanceModel performance { get; set; } + public OAKImuDataModel imu { get; set; } + + public OAKCameraModel ToOAK() + { + return new OAKCameraModel() + { + Id = mx_id, + Dispositivo = dispositivo, + Version = versao, + Name = modelo, + cam = this + }; + } } public class CameraWorkerItemPerformanceModel diff --git a/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs b/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs index 1f32169e9..f5babfda0 100644 --- a/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs +++ b/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs @@ -592,10 +592,15 @@ namespace AgroBase.Models.Operadores public class VisualWorkerMessageSegmentacaoSemanticaDebugCorredorModel { - public double nav_global { get; set; } - public double nav_near { get; set; } - public double nav_mid { get; set; } - public double nav_far { get; set; } + public string origem_status { get; set; } + public StatusCarroMapa status_modelo { get; set; } + public string status_modelo_nome { get; set; } + public string status_modelo_label_name { get; set; } + public double status_modelo_conf { get; set; } + public dynamic status_modelo_probs { get; set; } + public StatusCarroMapa status_heuristico { get; set; } + public string status_heuristico_nome { get; set; } + public dynamic heuristica_debug { get; set; } } public class VisualWorkerMessageMatrizConfiancaModel diff --git a/AgroBase/AgroBase/Models/PerformanceModel.cs b/AgroBase/AgroBase/Models/PerformanceModel.cs index f32acb189..cdc454041 100644 --- a/AgroBase/AgroBase/Models/PerformanceModel.cs +++ b/AgroBase/AgroBase/Models/PerformanceModel.cs @@ -1,176 +1,778 @@ using OpenHardwareMonitor.Hardware; -using System.Diagnostics; -using System.Threading; using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; namespace AgroBase.Models { + /// + /// Snapshot rico de performance do PC. + /// + /// Objetivo: + /// - telemetria em tempo real; + /// - replay de operação; + /// - diagnóstico do health_worker; + /// - correlação com performance dos workers Python. + /// + /// Observação: + /// - Disk_used_space_percent representa espaço usado, não atividade do disco. + /// - GPU_load_percent é um agregado dos principais sensores de load disponíveis. + /// public class PerformanceModel { + private static readonly object ComputerLock = new object(); private static Computer computer; + private DateTime _ultimaLeituraPerformance = DateTime.MinValue; + public PerformanceModel() { - if (computer == null) + InicializarComputer(); + } + + // ============================================================ + // Identificação / tempo + // ============================================================ + + public DateTime Timestamp { get; set; } = DateTime.Now; + public long TimestampUnixMs { get; set; } + + // ============================================================ + // CPU + // ============================================================ + + public float? CPU_load_percent { get; set; } + public float? CPU_temp_package_c { get; set; } + public float? CPU_power_package_w { get; set; } + public float? CPU_power_cores_w { get; set; } + public float? CPU_power_graphics_w { get; set; } + public float? CPU_power_dram_w { get; set; } + + public float? CPU_clock_avg_mhz { get; set; } + public float? CPU_clock_max_mhz { get; set; } + public float? CPU_bus_speed_mhz { get; set; } + + // Compatibilidade conceitual, se você quiser usar nomes antigos depois. + public float? CPU_load => CPU_load_percent; + public float? CPU_temp => CPU_temp_package_c; + + // ============================================================ + // RAM + // ============================================================ + + public float? RAM_usage_percent { get; set; } + public float? RAM_used_gb { get; set; } + public float? RAM_available_gb { get; set; } + public float? RAM_total_gb + { + get { + if (RAM_used_gb.HasValue && RAM_available_gb.HasValue) + return RAM_used_gb.Value + RAM_available_gb.Value; + + return null; + } + } + + // Compatibilidade conceitual + public float? RAM_usage => RAM_usage_percent; + + // ============================================================ + // GPU geral + // ============================================================ + + /// + /// Agregado dos sensores de carga da GPU. + /// Calculado como o máximo entre core, frame buffer, memory, + /// video engine e bus interface. + /// + public float? GPU_load_percent { get; set; } + + public float? GPU_core_load_percent { get; set; } + public float? GPU_frame_buffer_load_percent { get; set; } + public float? GPU_memory_load_percent { get; set; } + public float? GPU_video_engine_load_percent { get; set; } + public float? GPU_bus_interface_load_percent { get; set; } + + public float? GPU_temp_core_c { get; set; } + + public float? GPU_power_w { get; set; } + + public float? GPU_core_clock_mhz { get; set; } + public float? GPU_memory_clock_mhz { get; set; } + public float? GPU_shader_clock_mhz { get; set; } + + public float? GPU_fan_percent { get; set; } + public float? GPU_fan_rpm { get; set; } + + public float? GPU_pcie_rx { get; set; } + public float? GPU_pcie_tx { get; set; } + + // Compatibilidade conceitual + public float? GPU_load => GPU_load_percent; + public float? GPU_temp => GPU_temp_core_c; + + // ============================================================ + // GPU memória / VRAM + // ============================================================ + + public float? GPU_vram_total_mb { get; set; } + public float? GPU_vram_used_mb { get; set; } + public float? GPU_vram_free_mb { get; set; } + + public float? GPU_vram_used_percent + { + get + { + if (GPU_vram_total_mb.HasValue && + GPU_vram_total_mb.Value > 0 && + GPU_vram_used_mb.HasValue) + { + return 100.0f * GPU_vram_used_mb.Value / GPU_vram_total_mb.Value; + } + + return GPU_memory_load_percent; + } + } + + // ============================================================ + // Disco + // ============================================================ + + /// + /// Espaço usado no disco. + /// Isso NÃO é atividade do disco. + /// + public float? Disk_used_space_percent { get; set; } + + // Nome antigo, mas agora intencionalmente apontando para espaço usado. + public float? HDD_load => Disk_used_space_percent; + + // ============================================================ + // Processo atual + // ============================================================ + + public int Process_thread_count { get; set; } + public long Process_working_set_mb { get; set; } + public long Process_private_memory_mb { get; set; } + public long Process_virtual_memory_mb { get; set; } + public double Process_cpu_time_total_s { get; set; } + + // ============================================================ + // ThreadPool + // ============================================================ + + public int ThreadPool_available_worker_threads { get; set; } + public int ThreadPool_available_completion_port_threads { get; set; } + + public int ThreadPool_max_worker_threads { get; set; } + public int ThreadPool_max_completion_port_threads { get; set; } + + public int ThreadPool_used_worker_threads + { + get + { + return Math.Max(0, ThreadPool_max_worker_threads - ThreadPool_available_worker_threads); + } + } + + public int ThreadPool_used_completion_port_threads + { + get + { + return Math.Max(0, ThreadPool_max_completion_port_threads - ThreadPool_available_completion_port_threads); + } + } + + public float ThreadPool_used_worker_percent + { + get + { + if (ThreadPool_max_worker_threads <= 0) + return 0; + + return 100.0f * ThreadPool_used_worker_threads / ThreadPool_max_worker_threads; + } + } + + public float ThreadPool_used_completion_port_percent + { + get + { + if (ThreadPool_max_completion_port_threads <= 0) + return 0; + + return 100.0f * ThreadPool_used_completion_port_threads / ThreadPool_max_completion_port_threads; + } + } + + + // ============================================================ + // String resumida para debug rápido + // ============================================================ + + public string PerformanceStr + { + get + { + return + $"CPU={Fmt(CPU_load_percent)}% " + + $"CPU_T={Fmt(CPU_temp_package_c)}C " + + $"RAM={Fmt(RAM_usage_percent)}% " + + $"GPU={Fmt(GPU_load_percent)}% " + + $"GPU_Core={Fmt(GPU_core_load_percent)}% " + + $"GPU_Mem={Fmt(GPU_memory_load_percent)}% " + + $"VRAM={Fmt(GPU_vram_used_mb)}/{Fmt(GPU_vram_total_mb)}MB " + + $"GPU_T={Fmt(GPU_temp_core_c)}C " + + $"GPU_P={Fmt(GPU_power_w)}W"; + } + } + + // ============================================================ + // Inicialização + // ============================================================ + + private static void InicializarComputer() + { + if (computer != null) + return; + + lock (ComputerLock) + { + if (computer != null) + return; + computer = new Computer() { CPUEnabled = true, GPUEnabled = true, RAMEnabled = true, HDDEnabled = true, + MainboardEnabled = false, + FanControllerEnabled = false, }; try { computer.Open(); } - catch (ArgumentOutOfRangeException ex) - { - } - catch (Exception ex) + catch { + computer = null; } } } - public float? CPU_load { get; set; } - public float? GPU_load { get; set; } - public float? HDD_load { get; set; } - public float? RAM_usage { get; set; } - public float? CPU_temp { get; set; } - public float? GPU_temp { get; set; } - public int ActiveThreads { get; set; } - public int WorkerThreads { get; set; } - public int CompletionPortThreads { get; set; } - public int MaxWorkerThreads { get; set; } - public int MaxCompletionPortThreads { get; set; } - public string PerformanceStr - { - get - { - return FuncoesGlobais.MontarStringPerformance( - GPU_load, - CPU_load, - RAM_usage, - GPU_temp, - CPU_temp, - HDD_load - ); - } - } - - DateTime UltimaLeituraPerformance = DateTime.Now; + // ============================================================ + // Atualização principal + // ============================================================ public void AtualizarDadosPerformance() { + AtualizarDadosPerformance(TimeSpan.FromSeconds(1)); + } + + public void AtualizarDadosPerformance(TimeSpan intervaloMinimo) + { + if (DateTime.Now < _ultimaLeituraPerformance.Add(intervaloMinimo)) + return; + + _ultimaLeituraPerformance = DateTime.Now; + Timestamp = DateTime.Now; + TimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + if (computer == null || computer.Hardware == null) { - return; + InicializarComputer(); + if (computer == null || computer.Hardware == null) + return; } - if (DateTime.Now > UltimaLeituraPerformance.AddSeconds(1)) + // Variáveis locais, para só atualizar propriedades no final. + float? cpuLoad = null; + float? cpuTempPackage = null; + float? cpuPowerPackage = null; + float? cpuPowerCores = null; + float? cpuPowerGraphics = null; + float? cpuPowerDram = null; + float? cpuClockAvg = null; + float? cpuClockMax = null; + float? cpuBusSpeed = null; + + float? ramUsage = null; + float? ramUsedGb = null; + float? ramAvailableGb = null; + + float? gpuCoreLoad = null; + float? gpuFrameBufferLoad = null; + float? gpuMemoryLoad = null; + float? gpuVideoEngineLoad = null; + float? gpuBusInterfaceLoad = null; + + float? gpuTempCore = null; + float? gpuPower = null; + + float? gpuCoreClock = null; + float? gpuMemoryClock = null; + float? gpuShaderClock = null; + + float? gpuFanPercent = null; + float? gpuFanRpm = null; + + float? gpuVramTotalMb = null; + float? gpuVramUsedMb = null; + float? gpuVramFreeMb = null; + + float? gpuPcieRx = null; + float? gpuPcieTx = null; + + float? diskUsedSpace = null; + + try { - UltimaLeituraPerformance = DateTime.Now; - - float? cpuLoad = null; - float? gpuLoad = null; - float? hddLoad = null; - float? ramUsage = null; - float? cpuTemp = null; - float? gpuTemp = null; - - foreach (var hardwareItem in computer.Hardware) + lock (ComputerLock) { - hardwareItem.Update(); - - if (hardwareItem.HardwareType == HardwareType.CPU) + foreach (var hardwareItem in computer.Hardware) { - foreach (var sensor in hardwareItem.Sensors) - { - if (sensor.SensorType == SensorType.Load && sensor.Name == "CPU Total") - { - cpuLoad = sensor.Value; - } - if (sensor.SensorType == SensorType.Temperature && sensor.Name == "CPU Package") - { - cpuTemp = sensor.Value; - } - } - } + hardwareItem.Update(); - if (hardwareItem.HardwareType == HardwareType.GpuNvidia || hardwareItem.HardwareType == HardwareType.GpuAti) - { - foreach (var sensor in hardwareItem.Sensors) + switch (hardwareItem.HardwareType) { - if (sensor.SensorType == SensorType.Load && sensor.Name == "GPU Core") - { - gpuLoad = sensor.Value; - } - if (sensor.SensorType == SensorType.Temperature && sensor.Name == "GPU Core") - { - gpuTemp = sensor.Value; - } - } - } + case HardwareType.CPU: + LerCpu(hardwareItem, + ref cpuLoad, + ref cpuTempPackage, + ref cpuPowerPackage, + ref cpuPowerCores, + ref cpuPowerGraphics, + ref cpuPowerDram, + ref cpuClockAvg, + ref cpuClockMax, + ref cpuBusSpeed); + break; - if (hardwareItem.HardwareType == HardwareType.RAM) - { - foreach (var sensor in hardwareItem.Sensors) - { - if (sensor.SensorType == SensorType.Load && sensor.Name == "Memory") - { - ramUsage = sensor.Value; - } - } - } + case HardwareType.RAM: + LerRam(hardwareItem, + ref ramUsage, + ref ramUsedGb, + ref ramAvailableGb); + break; - if (hardwareItem.HardwareType == HardwareType.HDD) - { - foreach (var sensor in hardwareItem.Sensors) - { - if (sensor.SensorType == SensorType.Load && sensor.Name == "Used Space") - { - hddLoad = sensor.Value; - } + case HardwareType.GpuNvidia: + case HardwareType.GpuAti: + LerGpu(hardwareItem, + ref gpuCoreLoad, + ref gpuFrameBufferLoad, + ref gpuMemoryLoad, + ref gpuVideoEngineLoad, + ref gpuBusInterfaceLoad, + ref gpuTempCore, + ref gpuPower, + ref gpuCoreClock, + ref gpuMemoryClock, + ref gpuShaderClock, + ref gpuFanPercent, + ref gpuFanRpm, + ref gpuVramTotalMb, + ref gpuVramUsedMb, + ref gpuVramFreeMb, + ref gpuPcieRx, + ref gpuPcieTx); + break; + + case HardwareType.HDD: + LerDisco(hardwareItem, ref diskUsedSpace); + break; } } } - int activeThreads = Process.GetCurrentProcess().Threads.Count; - ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads); - ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads); + CPU_load_percent = cpuLoad; + CPU_temp_package_c = cpuTempPackage; + CPU_power_package_w = cpuPowerPackage; + CPU_power_cores_w = cpuPowerCores; + CPU_power_graphics_w = cpuPowerGraphics; + CPU_power_dram_w = cpuPowerDram; + CPU_clock_avg_mhz = cpuClockAvg; + CPU_clock_max_mhz = cpuClockMax; + CPU_bus_speed_mhz = cpuBusSpeed; - CPU_load = cpuLoad; - GPU_load = gpuLoad; - HDD_load = hddLoad; - RAM_usage = ramUsage; - CPU_temp = cpuTemp; - GPU_temp = gpuTemp; - ActiveThreads = activeThreads; - WorkerThreads = workerThreads; - MaxWorkerThreads = maxWorkerThreads; - CompletionPortThreads = completionPortThreads; - MaxCompletionPortThreads = maxCompletionPortThreads; + RAM_usage_percent = ramUsage; + RAM_used_gb = ramUsedGb; + RAM_available_gb = ramAvailableGb; + + GPU_core_load_percent = gpuCoreLoad; + GPU_frame_buffer_load_percent = gpuFrameBufferLoad; + GPU_memory_load_percent = gpuMemoryLoad; + GPU_video_engine_load_percent = gpuVideoEngineLoad; + GPU_bus_interface_load_percent = gpuBusInterfaceLoad; + + GPU_load_percent = MaxNullable( + gpuCoreLoad, + gpuFrameBufferLoad, + gpuMemoryLoad, + gpuVideoEngineLoad, + gpuBusInterfaceLoad + ); + + GPU_temp_core_c = gpuTempCore; + GPU_power_w = gpuPower; + + GPU_core_clock_mhz = gpuCoreClock; + GPU_memory_clock_mhz = gpuMemoryClock; + GPU_shader_clock_mhz = gpuShaderClock; + + GPU_fan_percent = gpuFanPercent; + GPU_fan_rpm = gpuFanRpm; + + GPU_vram_total_mb = gpuVramTotalMb; + GPU_vram_used_mb = gpuVramUsedMb; + GPU_vram_free_mb = gpuVramFreeMb; + + GPU_pcie_rx = gpuPcieRx; + GPU_pcie_tx = gpuPcieTx; + + Disk_used_space_percent = diskUsedSpace; + + AtualizarDadosProcesso(); + AtualizarDadosThreadPool(); + } + catch + { + // Mantém último snapshot válido. + // Se quiser, depois podemos adicionar um campo: + // LastError, ErrorCount, LastErrorTimestamp. } } + // ============================================================ + // Leitores por hardware + // ============================================================ + + private static void LerCpu( + IHardware hardwareItem, + ref float? cpuLoad, + ref float? cpuTempPackage, + ref float? cpuPowerPackage, + ref float? cpuPowerCores, + ref float? cpuPowerGraphics, + ref float? cpuPowerDram, + ref float? cpuClockAvg, + ref float? cpuClockMax, + ref float? cpuBusSpeed) + { + float clockSum = 0; + int clockCount = 0; + float? clockMax = null; + + foreach (var sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? ""; + + if (sensor.SensorType == SensorType.Load && name == "CPU Total") + cpuLoad = sensor.Value; + + else if (sensor.SensorType == SensorType.Temperature && name == "CPU Package") + cpuTempPackage = sensor.Value; + + else if (sensor.SensorType == SensorType.Power && name == "CPU Package") + cpuPowerPackage = sensor.Value; + + else if (sensor.SensorType == SensorType.Power && name == "CPU Cores") + cpuPowerCores = sensor.Value; + + else if (sensor.SensorType == SensorType.Power && name == "CPU Graphics") + cpuPowerGraphics = sensor.Value; + + else if (sensor.SensorType == SensorType.Power && name == "CPU DRAM") + cpuPowerDram = sensor.Value; + + else if (sensor.SensorType == SensorType.Clock && name.StartsWith("CPU Core")) + { + float v = sensor.Value.Value; + clockSum += v; + clockCount++; + + if (!clockMax.HasValue || v > clockMax.Value) + clockMax = v; + } + + else if (sensor.SensorType == SensorType.Clock && name == "Bus Speed") + cpuBusSpeed = sensor.Value; + } + + if (clockCount > 0) + cpuClockAvg = clockSum / clockCount; + + cpuClockMax = clockMax; + } + + private static void LerRam( + IHardware hardwareItem, + ref float? ramUsage, + ref float? ramUsedGb, + ref float? ramAvailableGb) + { + foreach (var sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? ""; + + if (sensor.SensorType == SensorType.Load && name == "Memory") + ramUsage = sensor.Value; + + else if (sensor.SensorType == SensorType.Data && name == "Used Memory") + ramUsedGb = sensor.Value; + + else if (sensor.SensorType == SensorType.Data && name == "Available Memory") + ramAvailableGb = sensor.Value; + } + } + + private static void LerGpu( + IHardware hardwareItem, + ref float? gpuCoreLoad, + ref float? gpuFrameBufferLoad, + ref float? gpuMemoryLoad, + ref float? gpuVideoEngineLoad, + ref float? gpuBusInterfaceLoad, + ref float? gpuTempCore, + ref float? gpuPower, + ref float? gpuCoreClock, + ref float? gpuMemoryClock, + ref float? gpuShaderClock, + ref float? gpuFanPercent, + ref float? gpuFanRpm, + ref float? gpuVramTotalMb, + ref float? gpuVramUsedMb, + ref float? gpuVramFreeMb, + ref float? gpuPcieRx, + ref float? gpuPcieTx) + { + foreach (var sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? ""; + + if (sensor.SensorType == SensorType.Temperature && name == "GPU Core") + gpuTempCore = sensor.Value; + + else if (sensor.SensorType == SensorType.Load && name == "GPU Core") + gpuCoreLoad = sensor.Value; + + else if (sensor.SensorType == SensorType.Load && name == "GPU Frame Buffer") + gpuFrameBufferLoad = sensor.Value; + + else if (sensor.SensorType == SensorType.Load && name == "GPU Memory") + gpuMemoryLoad = sensor.Value; + + else if (sensor.SensorType == SensorType.Load && name == "GPU Video Engine") + gpuVideoEngineLoad = sensor.Value; + + else if (sensor.SensorType == SensorType.Load && name == "GPU Bus Interface") + gpuBusInterfaceLoad = sensor.Value; + + else if (sensor.SensorType == SensorType.Clock && name == "GPU Core") + gpuCoreClock = sensor.Value; + + else if (sensor.SensorType == SensorType.Clock && name == "GPU Memory") + gpuMemoryClock = sensor.Value; + + else if (sensor.SensorType == SensorType.Clock && name == "GPU Shader") + gpuShaderClock = sensor.Value; + + else if (sensor.SensorType == SensorType.Control && name == "GPU Fan") + gpuFanPercent = sensor.Value; + + else if (sensor.SensorType == SensorType.Fan && name == "GPU") + gpuFanRpm = sensor.Value; + + else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Total") + gpuVramTotalMb = sensor.Value; + + else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Used") + gpuVramUsedMb = sensor.Value; + + else if (sensor.SensorType == SensorType.SmallData && name == "GPU Memory Free") + gpuVramFreeMb = sensor.Value; + + else if (sensor.SensorType == SensorType.Power && name == "GPU Power") + gpuPower = sensor.Value; + + else if (sensor.SensorType == SensorType.Throughput && name == "GPU PCIE Rx") + gpuPcieRx = sensor.Value; + + else if (sensor.SensorType == SensorType.Throughput && name == "GPU PCIE Tx") + gpuPcieTx = sensor.Value; + } + } + + private static void LerDisco(IHardware hardwareItem, ref float? diskUsedSpace) + { + foreach (var sensor in hardwareItem.Sensors) + { + if (!sensor.Value.HasValue) + continue; + + string name = sensor.Name ?? ""; + + if (sensor.SensorType == SensorType.Load && name == "Used Space") + diskUsedSpace = sensor.Value; + } + } + + // ============================================================ + // Processo / ThreadPool + // ============================================================ + + private void AtualizarDadosProcesso() + { + try + { + using (var proc = Process.GetCurrentProcess()) + { + Process_thread_count = proc.Threads.Count; + Process_working_set_mb = BytesToMb(proc.WorkingSet64); + Process_private_memory_mb = BytesToMb(proc.PrivateMemorySize64); + Process_virtual_memory_mb = BytesToMb(proc.VirtualMemorySize64); + Process_cpu_time_total_s = proc.TotalProcessorTime.TotalSeconds; + } + } + catch + { + // Mantém último valor. + } + } + + private void AtualizarDadosThreadPool() + { + try + { + ThreadPool.GetAvailableThreads( + out int availableWorkerThreads, + out int availableCompletionPortThreads + ); + + ThreadPool.GetMaxThreads( + out int maxWorkerThreads, + out int maxCompletionPortThreads + ); + + ThreadPool_available_worker_threads = availableWorkerThreads; + ThreadPool_available_completion_port_threads = availableCompletionPortThreads; + + ThreadPool_max_worker_threads = maxWorkerThreads; + ThreadPool_max_completion_port_threads = maxCompletionPortThreads; + } + catch + { + // Mantém último valor. + } + } + + // ============================================================ + // Clone / Snapshot + // ============================================================ + public PerformanceModel Clone() { return new PerformanceModel() { - ActiveThreads = ActiveThreads, - CompletionPortThreads = CompletionPortThreads, - MaxWorkerThreads = MaxWorkerThreads, - MaxCompletionPortThreads = MaxCompletionPortThreads, - CPU_load = CPU_load, - GPU_load = GPU_load, - RAM_usage = RAM_usage, - CPU_temp = CPU_temp, - GPU_temp = GPU_temp, - HDD_load = HDD_load, - WorkerThreads = WorkerThreads + Timestamp = this.Timestamp, + TimestampUnixMs = this.TimestampUnixMs, + + CPU_load_percent = this.CPU_load_percent, + CPU_temp_package_c = this.CPU_temp_package_c, + CPU_power_package_w = this.CPU_power_package_w, + CPU_power_cores_w = this.CPU_power_cores_w, + CPU_power_graphics_w = this.CPU_power_graphics_w, + CPU_power_dram_w = this.CPU_power_dram_w, + CPU_clock_avg_mhz = this.CPU_clock_avg_mhz, + CPU_clock_max_mhz = this.CPU_clock_max_mhz, + CPU_bus_speed_mhz = this.CPU_bus_speed_mhz, + + RAM_usage_percent = this.RAM_usage_percent, + RAM_used_gb = this.RAM_used_gb, + RAM_available_gb = this.RAM_available_gb, + + GPU_load_percent = this.GPU_load_percent, + GPU_core_load_percent = this.GPU_core_load_percent, + GPU_frame_buffer_load_percent = this.GPU_frame_buffer_load_percent, + GPU_memory_load_percent = this.GPU_memory_load_percent, + GPU_video_engine_load_percent = this.GPU_video_engine_load_percent, + GPU_bus_interface_load_percent = this.GPU_bus_interface_load_percent, + + GPU_temp_core_c = this.GPU_temp_core_c, + GPU_power_w = this.GPU_power_w, + + GPU_core_clock_mhz = this.GPU_core_clock_mhz, + GPU_memory_clock_mhz = this.GPU_memory_clock_mhz, + GPU_shader_clock_mhz = this.GPU_shader_clock_mhz, + + GPU_fan_percent = this.GPU_fan_percent, + GPU_fan_rpm = this.GPU_fan_rpm, + + GPU_vram_total_mb = this.GPU_vram_total_mb, + GPU_vram_used_mb = this.GPU_vram_used_mb, + GPU_vram_free_mb = this.GPU_vram_free_mb, + + GPU_pcie_rx = this.GPU_pcie_rx, + GPU_pcie_tx = this.GPU_pcie_tx, + + Disk_used_space_percent = this.Disk_used_space_percent, + + Process_thread_count = this.Process_thread_count, + Process_working_set_mb = this.Process_working_set_mb, + Process_private_memory_mb = this.Process_private_memory_mb, + Process_virtual_memory_mb = this.Process_virtual_memory_mb, + Process_cpu_time_total_s = this.Process_cpu_time_total_s, + + ThreadPool_available_worker_threads = this.ThreadPool_available_worker_threads, + ThreadPool_available_completion_port_threads = this.ThreadPool_available_completion_port_threads, + ThreadPool_max_worker_threads = this.ThreadPool_max_worker_threads, + ThreadPool_max_completion_port_threads = this.ThreadPool_max_completion_port_threads, }; } + + // ============================================================ + // Helpers + // ============================================================ + + private static float? MaxNullable(params float?[] values) + { + float? max = null; + + foreach (float? value in values) + { + if (!value.HasValue) + continue; + + if (!max.HasValue || value.Value > max.Value) + max = value.Value; + } + + return max; + } + + private static long BytesToMb(long bytes) + { + return bytes / (1024L * 1024L); + } + + private static string Fmt(float? value, int casas = 1) + { + if (!value.HasValue) + return "-"; + + return value.Value.ToString($"F{casas}"); + } } -} +} \ No newline at end of file diff --git a/AgroBase/AgroBase/Services/CoulombCounterService.cs b/AgroBase/AgroBase/Services/CoulombCounterService.cs index f9d02dab5..ab82c1de8 100644 --- a/AgroBase/AgroBase/Services/CoulombCounterService.cs +++ b/AgroBase/AgroBase/Services/CoulombCounterService.cs @@ -8,88 +8,93 @@ namespace AgroBase.Services { public class CoulombCounterService { - public CoulombCounterService(double max_Ah, double w_m_base = 0.1167) + public CoulombCounterService(double max_Ah, double wh_m_base = 0.1167) { - CargaMaxima_C = max_Ah * 3600.0; // Ah -> Coulombs - WhPorMetroBase = w_m_base; - - Iniciado = false; - BmsLigado = false; - - _estadoInicializado = false; - _ultimaLeituraIntegrada = null; - - CargaAtual_C = 0; - ConsumidoTotal_C = 0; - EnergiaConsumidaTotal_Wh = 0; - - _socInicialInstancia = 0; - _cargaInicialInstancia_C = 0; - - _socInicialOperacao = 0; - _cargaInicioOperacao_C = 0; - _consumidoTotalNoInicioOperacao_C = 0; - _energiaTotalNoInicioOperacao_Wh = 0; + CapacidadeMaxima_Ah = max_Ah; + CargaMaxima_C = max_Ah * 3600.0; + WhPorMetroBase = wh_m_base; MargemSegurancaAutonomia = 0.30; - ResetarHistoricosAutonomia(); + ResetarTudo(); } public bool Iniciado { get; private set; } public bool BmsLigado { get; private set; } + public bool OperacaoAtiva { get; private set; } + public bool OperacaoFinalizada { get; private set; } private bool _estadoInicializado; private DateTime? _ultimaLeituraIntegrada; - // Capacidade e estado absoluto do pack - private double CargaMaxima_C { get; set; } // Coulombs - private double CargaAtual_C { get; set; } // Coulombs + public double CapacidadeMaxima_Ah { get; private set; } + private double CargaMaxima_C { get; set; } - // Snapshot inicial da instância - private double _socInicialInstancia { get; set; } - private double _cargaInicialInstancia_C { get; set; } + public double CargaAtual_C { get; private set; } + public double CargaAtualAmp => CargaAtual_C / 3600.0; + + private double _socInicialInstancia; + private double _socInicialOperacao; + + private double _distanciaInicioOperacao_m; + + private double _consumidoIntegradoTotal_C; + private double _consumidoIntegradoOperacao_C; - // Acumuladores totais desde a criação da instância - private double ConsumidoTotal_C { get; set; } public double ConsumidoTotal_Ah => ConsumidoTotal_C / 3600.0; + public double ConsumidoOperacao_Ah => ConsumidoOperacao_C / 3600.0; + + public double ConsumidoTotal_C + { + get + { + if (BmsLigado) + return Math.Max(0, (_socInicialInstancia - PercentualBateria) / 100.0 * CargaMaxima_C); + + return _consumidoIntegradoTotal_C; + } + } + + public double ConsumidoOperacao_C + { + get + { + if (!OperacaoAtiva && !OperacaoFinalizada) + return 0; + + if (BmsLigado) + return Math.Max(0, (_socInicialOperacao - PercentualBateria) / 100.0 * CargaMaxima_C); + + return _consumidoIntegradoOperacao_C; + } + } public double PercentualConsumidoTotal { get { if (CargaMaxima_C <= 0) return 0; - double calc = (ConsumidoTotal_C / CargaMaxima_C) * 100.0; - return FuncoesMatematicas.Clamp(calc, 0, 100); + + if (BmsLigado) + return Clamp(_socInicialInstancia - PercentualBateria, 0, 100); + + return Clamp((_consumidoIntegradoTotal_C / CargaMaxima_C) * 100.0, 0, 100); } } - public double EnergiaConsumidaTotal_Wh { get; private set; } - - // Snapshot do início da operação - private double _socInicialOperacao { get; set; } - private double _cargaInicioOperacao_C { get; set; } - private double _consumidoTotalNoInicioOperacao_C { get; set; } - private double _energiaTotalNoInicioOperacao_Wh { get; set; } - public bool OperacaoAtiva { get; private set; } - public bool OperacaoFinalizada { get; private set; } - - private double _consumidoTotalNoFimOperacao_C; - private double _energiaTotalNoFimOperacao_Wh; - - // Derivados da operação - public double ConsumidoOperacao_C { get; set; } - public double ConsumidoOperacao_Ah => ConsumidoOperacao_C / 3600.0; - public double PercentualConsumidoOperacao { get { - if (CargaMaxima_C <= 0) + if (!OperacaoAtiva && !OperacaoFinalizada) return 0; - double calc = (ConsumidoOperacao_C / CargaMaxima_C) * 100.0; - return FuncoesMatematicas.Clamp(calc, 0, 100); + if (CargaMaxima_C <= 0) return 0; + + if (BmsLigado) + return Clamp(_socInicialOperacao - PercentualBateria, 0, 100); + + return Clamp((_consumidoIntegradoOperacao_C / CargaMaxima_C) * 100.0, 0, 100); } } @@ -97,12 +102,65 @@ namespace AgroBase.Services { get { - if (_cargaInicioOperacao_C <= 0) return 0; - double calc = (ConsumidoOperacao_C / _cargaInicioOperacao_C) * 100.0; - return FuncoesMatematicas.Clamp(calc, 0, 100); + if (_socInicialOperacao <= 0) return 0; + + double consumido = PercentualConsumidoOperacao; + return Clamp((consumido / _socInicialOperacao) * 100.0, 0, 100); } } + public double PercentualConsumidoTotalIntegrado + { + get + { + if (CargaMaxima_C <= 0) return 0; + return Clamp((_consumidoIntegradoTotal_C / CargaMaxima_C) * 100.0, 0, 100); + } + } + + public double PercentualConsumidoOperacaoIntegrado + { + get + { + if (CargaMaxima_C <= 0) return 0; + return Clamp((_consumidoIntegradoOperacao_C / CargaMaxima_C) * 100.0, 0, 100); + } + } + + public double DiferencaSocBmsVsIntegrado_Total + { + get + { + return PercentualConsumidoTotalIntegrado - PercentualConsumidoTotal; + } + } + + public double PercentualAtual + { + get + { + if (BmsLigado) + return PercentualBateria; + + if (CargaMaxima_C <= 0) return 0; + return Clamp((CargaAtual_C / CargaMaxima_C) * 100.0, 0, 100); + } + } + + public double PercentualBateria { get; private set; } + public double Temperatura { get; private set; } + public double CorrenteInstantanea { get; private set; } + public double CorrenteDescarga_A { get; private set; } + public double TensaoInstantanea { get; private set; } + public double PotenciaInstantanea { get; private set; } + public double PotenciaDescarga_W { get; private set; } + public double TempoEstimadoMinutos { get; private set; } + public double _dt { get; private set; } + + public double EnergiaConsumidaTotal_Wh { get; private set; } + private double _energiaConsumidaInicioOperacao_Wh; + private double _energiaConsumidaFimOperacao_Wh; + public double EnergiaConsumidaOperacao_Wh { get @@ -110,32 +168,16 @@ namespace AgroBase.Services if (!OperacaoAtiva && !OperacaoFinalizada) return 0; - double referenciaFim = OperacaoFinalizada - ? _energiaTotalNoFimOperacao_Wh + double fim = OperacaoFinalizada + ? _energiaConsumidaFimOperacao_Wh : EnergiaConsumidaTotal_Wh; - return Math.Max(0, referenciaFim - _energiaTotalNoInicioOperacao_Wh); + return Math.Max(0, fim - _energiaConsumidaInicioOperacao_Wh); } } - // Estado atual - public double PercentualAtual - { - get - { - if (CargaMaxima_C <= 0) return 0; - return FuncoesMatematicas.Clamp((CargaAtual_C / CargaMaxima_C) * 100.0, 0, 100); - } - } - - public double CargaAtualAmp => CargaAtual_C / 3600.0; - public double PercentualBateria { get; private set; } - public double Temperatura { get; private set; } - public double CorrenteInstantanea { get; private set; } - public double TensaoInstantanea { get; private set; } - public double PotenciaInstantanea { get; private set; } - public double TempoEstimadoMinutos { get; private set; } - public double _dt { get; private set; } + public double EnergiaConsumidaTotal_kWh => EnergiaConsumidaTotal_Wh / 1000.0; + public double EnergiaConsumidaOperacao_kWh => EnergiaConsumidaOperacao_Wh / 1000.0; public double WhRestante { @@ -149,12 +191,14 @@ namespace AgroBase.Services } } - // Histórico para suavização de corrente - private const int BufferSize = 30; - private readonly Queue _historicoCorrente = new Queue(); + public double kWhRestante => WhRestante / 1000.0; + + private const int BufferSize = 30; + private readonly Queue _historicoCorrenteDescarga = new Queue(); + private readonly Queue _historicoPotenciaDescarga = new Queue(); - // Autonomia public double WhPorMetro { get; private set; } + public double kWhPorMetro => WhPorMetro / 1000.0; public double WhPorMetroBase { get; set; } public double WhPorMetroHistorico { get; private set; } @@ -181,18 +225,17 @@ namespace AgroBase.Services private double _distanciaTotalMetrosAnterior; private double _energiaConsumidaAnterior_Wh_Total; - public void IniciarOperacao() + public void IniciarOperacao(double distanciaTotalMetrosAtual = 0) { if (!_estadoInicializado) return; _socInicialOperacao = PercentualBateria; - _cargaInicioOperacao_C = CargaAtual_C; - _consumidoTotalNoInicioOperacao_C = ConsumidoTotal_C; - _energiaTotalNoInicioOperacao_Wh = EnergiaConsumidaTotal_Wh; + _distanciaInicioOperacao_m = distanciaTotalMetrosAtual; - _consumidoTotalNoFimOperacao_C = 0; - _energiaTotalNoFimOperacao_Wh = 0; + _consumidoIntegradoOperacao_C = 0; + _energiaConsumidaInicioOperacao_Wh = EnergiaConsumidaTotal_Wh; + _energiaConsumidaFimOperacao_Wh = 0; OperacaoAtiva = true; OperacaoFinalizada = false; @@ -203,8 +246,7 @@ namespace AgroBase.Services if (!OperacaoAtiva) return; - _consumidoTotalNoFimOperacao_C = ConsumidoTotal_C; - _energiaTotalNoFimOperacao_Wh = EnergiaConsumidaTotal_Wh; + _energiaConsumidaFimOperacao_Wh = EnergiaConsumidaTotal_Wh; OperacaoAtiva = false; OperacaoFinalizada = true; @@ -212,63 +254,22 @@ namespace AgroBase.Services public void AtualizarConsumo(double distanciaTotalMetrosAtual) { - var dalyBms = DalyBMSService.DadosLeitura; - bool bms_iniciado = dalyBms?.Iniciado ?? false; - DateTime? bms_ultima_leitura = dalyBms?.UltimoComandoRecebido; - double bms_tensao = dalyBms?.Pack?.TensaoTotal_V ?? 0; - double bms_corrente = dalyBms?.Pack?.Corrente_A ?? 0; - double bms_soc = dalyBms?.Pack?.SOC_percent ?? 0; - double bms_tmp = dalyBms?.TemperaturaMax ?? 0; - - var sensor = Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Sensores?.FirstOrDefault(x => x.ID == "AB36V"); - bool sen_iniciado = sensor?.Inicializado ?? false; - DateTime? sen_ultima_leitura = sensor?.UltimaLeitura; - double sen_tensao = sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Tensao)?.atual?.valor ?? 0; - double sen_corrente = (sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Corrente)?.atual?.valor ?? 0) / 1000.0; - double sen_soc = Math.Round( - FuncoesMatematicas.Map( - sen_tensao, - VariaveisEquipamento.TensaoMinimaBateria, - VariaveisEquipamento.TensaoMaximaBateria, - 0.0, - 100.0), 2); - double sen_tmp = sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Temperatura)?.atual?.valor ?? 0; - - Iniciado = bms_iniciado || sen_iniciado; - BmsLigado = bms_iniciado; + LerFontes(); if (!Iniciado) return; - DateTime? leituraAtual = BmsLigado ? bms_ultima_leitura : sen_ultima_leitura; - TensaoInstantanea = BmsLigado ? bms_tensao : sen_tensao; - CorrenteInstantanea = BmsLigado ? bms_corrente : sen_corrente; - PotenciaInstantanea = TensaoInstantanea * CorrenteInstantanea; - PercentualBateria = FuncoesMatematicas.Clamp(BmsLigado ? bms_soc : sen_soc, 0, 100); - Temperatura = BmsLigado ? bms_tmp : sen_tmp; + DateTime? leituraAtual = ObterDataLeituraAtual(); if (!leituraAtual.HasValue || leituraAtual.Value == DateTime.MinValue) return; - // Primeira leitura válida da instância if (!_estadoInicializado) { - _socInicialInstancia = PercentualBateria; - _cargaInicialInstancia_C = CargaMaxima_C * (PercentualBateria / 100.0); - CargaAtual_C = _cargaInicialInstancia_C; - - ConsumidoTotal_C = 0; - EnergiaConsumidaTotal_Wh = 0; - - _ultimaLeituraIntegrada = leituraAtual.Value; - _estadoInicializado = true; - - _dt = 0; - AtualizarAutonomia(distanciaTotalMetrosAtual); + InicializarEstado(leituraAtual.Value, distanciaTotalMetrosAtual); return; } - // Só integra se chegou leitura nova if (_ultimaLeituraIntegrada.HasValue && leituraAtual.Value <= _ultimaLeituraIntegrada.Value) { _dt = 0; @@ -282,45 +283,160 @@ namespace AgroBase.Services if (_dt <= 0 || _dt > 10) { Console.WriteLine($"[COULOMB COUNTER] dt inválido: {_dt:F3}s"); + AtualizarCargaAtualPeloSocOuIntegrador(); + AtualizarAutonomia(distanciaTotalMetrosAtual); return; } - // Assumindo descarga positiva. Se no Daly a descarga vier negativa, inverter aqui. - double correnteDescarga_A = Math.Max(0, CorrenteInstantanea); + CorrenteDescarga_A = NormalizarCorrenteDescarga(CorrenteInstantanea); + PotenciaDescarga_W = Math.Max(0, TensaoInstantanea * CorrenteDescarga_A); + + double deltaCoulombs = CorrenteDescarga_A * _dt; + double deltaEnergia_Wh = PotenciaDescarga_W * _dt / 3600.0; + + _consumidoIntegradoTotal_C = Clamp(_consumidoIntegradoTotal_C + deltaCoulombs, 0, CargaMaxima_C); - double deltaCoulombs = correnteDescarga_A * _dt; - ConsumidoTotal_C += deltaCoulombs; - ConsumidoTotal_C = FuncoesMatematicas.Clamp(ConsumidoTotal_C, 0, CargaMaxima_C); if (OperacaoAtiva && !OperacaoFinalizada) - { - ConsumidoOperacao_C += deltaCoulombs; - ConsumidoOperacao_C = FuncoesMatematicas.Clamp(ConsumidoOperacao_C, 0, CargaMaxima_C); - } + _consumidoIntegradoOperacao_C = Clamp(_consumidoIntegradoOperacao_C + deltaCoulombs, 0, CargaMaxima_C); - // Estado absoluto do pack sempre baseado no total da instância - double cargaAtualCalculada = _cargaInicialInstancia_C - ConsumidoTotal_C; - CargaAtual_C = FuncoesMatematicas.Clamp(cargaAtualCalculada, 0, CargaMaxima_C); - - double deltaEnergia_Wh = PotenciaInstantanea * _dt / 3600.0; if (deltaEnergia_Wh > 0) EnergiaConsumidaTotal_Wh += deltaEnergia_Wh; - _historicoCorrente.Enqueue(correnteDescarga_A); - if (_historicoCorrente.Count > BufferSize) - _historicoCorrente.Dequeue(); + AtualizarCargaAtualPeloSocOuIntegrador(); + AtualizarHistoricos(); + AtualizarTempoRestante(); + AtualizarAutonomia(distanciaTotalMetrosAtual); + } - double correnteMedia = _historicoCorrente.Count > 0 - ? _historicoCorrente.Average() - : 0; + private void LerFontes() + { + var dalyBms = DalyBMSService.DadosLeitura; - if (correnteMedia > 0 && CargaAtualAmp > 0) - TempoEstimadoMinutos = (CargaAtualAmp / correnteMedia) * 60.0; - else - TempoEstimadoMinutos = 0; + bool bms_iniciado = dalyBms?.Iniciado ?? false; + DateTime? bms_ultima_leitura = dalyBms?.UltimoComandoRecebido; + double bms_tensao = dalyBms?.Pack?.TensaoTotal_V ?? 0; + double bms_corrente = dalyBms?.Pack?.Corrente_A ?? 0; + double bms_soc = dalyBms?.Pack?.SOC_percent ?? 0; + double bms_tmp = dalyBms?.TemperaturaMax ?? 0; + + var sensor = Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Sensores? + .FirstOrDefault(x => x.ID == "AB36V"); + + bool sen_iniciado = sensor?.Inicializado ?? false; + DateTime? sen_ultima_leitura = sensor?.UltimaLeitura; + double sen_tensao = sensor?.ValoresLeituras? + .FirstOrDefault(x => x.funcao == FuncoesPinout.Tensao)?.atual?.valor ?? 0; + + double sen_corrente = (sensor?.ValoresLeituras? + .FirstOrDefault(x => x.funcao == FuncoesPinout.Corrente)?.atual?.valor ?? 0) / 1000.0; + + double sen_soc = Math.Round( + FuncoesMatematicas.Map( + sen_tensao, + VariaveisEquipamento.TensaoMinimaBateria, + VariaveisEquipamento.TensaoMaximaBateria, + 0.0, + 100.0), 2); + + double sen_tmp = sensor?.ValoresLeituras? + .FirstOrDefault(x => x.funcao == FuncoesPinout.Temperatura)?.atual?.valor ?? 0; + + Iniciado = bms_iniciado || sen_iniciado; + BmsLigado = bms_iniciado; + + TensaoInstantanea = BmsLigado ? bms_tensao : sen_tensao; + CorrenteInstantanea = BmsLigado ? bms_corrente : sen_corrente; + PotenciaInstantanea = TensaoInstantanea * CorrenteInstantanea; + PercentualBateria = Clamp(BmsLigado ? bms_soc : sen_soc, 0, 100); + Temperatura = BmsLigado ? bms_tmp : sen_tmp; + } + + private DateTime? ObterDataLeituraAtual() + { + var dalyBms = DalyBMSService.DadosLeitura; + + if (BmsLigado) + return dalyBms?.UltimoComandoRecebido; + + var sensor = Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Sensores? + .FirstOrDefault(x => x.ID == "AB36V"); + + return sensor?.UltimaLeitura; + } + + private void InicializarEstado(DateTime leituraAtual, double distanciaTotalMetrosAtual) + { + _socInicialInstancia = PercentualBateria; + + AtualizarCargaAtualPeloSocOuIntegrador(); + + _consumidoIntegradoTotal_C = 0; + _consumidoIntegradoOperacao_C = 0; + EnergiaConsumidaTotal_Wh = 0; + + _ultimaLeituraIntegrada = leituraAtual; + _estadoInicializado = true; + + _distanciaTotalMetrosAnterior = distanciaTotalMetrosAtual; + _energiaConsumidaAnterior_Wh_Total = EnergiaConsumidaTotal_Wh; + + _dt = 0; AtualizarAutonomia(distanciaTotalMetrosAtual); } + private void AtualizarCargaAtualPeloSocOuIntegrador() + { + if (CargaMaxima_C <= 0) + { + CargaAtual_C = 0; + return; + } + + if (BmsLigado) + { + CargaAtual_C = CargaMaxima_C * (PercentualBateria / 100.0); + } + else + { + double cargaInicial = CargaMaxima_C * (_socInicialInstancia / 100.0); + CargaAtual_C = Clamp(cargaInicial - _consumidoIntegradoTotal_C, 0, CargaMaxima_C); + } + } + + private double NormalizarCorrenteDescarga(double corrente) + { + if (Math.Abs(corrente) < 0.05) + return 0; + + // Daly normalmente pode reportar descarga negativa. + // Para autonomia, queremos magnitude da descarga. + return Math.Abs(corrente); + } + + private void AtualizarHistoricos() + { + _historicoCorrenteDescarga.Enqueue(CorrenteDescarga_A); + if (_historicoCorrenteDescarga.Count > BufferSize) + _historicoCorrenteDescarga.Dequeue(); + + _historicoPotenciaDescarga.Enqueue(PotenciaDescarga_W); + if (_historicoPotenciaDescarga.Count > BufferSize) + _historicoPotenciaDescarga.Dequeue(); + } + + private void AtualizarTempoRestante() + { + double potenciaMedia_W = _historicoPotenciaDescarga.Count > 0 + ? _historicoPotenciaDescarga.Average() + : 0; + + if (potenciaMedia_W > 1 && WhRestante > 0) + TempoEstimadoMinutos = (WhRestante / potenciaMedia_W) * 60.0; + else + TempoEstimadoMinutos = 0; + } + private void AtualizarAutonomia(double distanciaTotalMetrosAtual) { if (!_estadoInicializado) @@ -337,6 +453,7 @@ namespace AgroBase.Services { _historicoDeltaDist_m.Enqueue(deltaDist_m); _historicoDeltaWh.Enqueue(deltaWh); + _somaDistanciaJanela_m += deltaDist_m; _somaEnergiaJanela_Wh += deltaWh; @@ -366,38 +483,12 @@ namespace AgroBase.Services } } - private void ResetarHistoricosAutonomia() - { - _historicoCorrente.Clear(); - - _historicoDeltaDist_m.Clear(); - _historicoDeltaWh.Clear(); - _somaDistanciaJanela_m = 0; - _somaEnergiaJanela_Wh = 0; - - _distanciaTotalMetrosAnterior = 0; - _energiaConsumidaAnterior_Wh_Total = EnergiaConsumidaTotal_Wh; - - WhPorMetro = 0; - WhPorMetroHistorico = 0; - DistanciaMaximaTeorica_m = 0; - DistanciaMaximaSegura_m = 0; - DistanciaEstimadaRestante_m = 0; - - TempoEstimadoMinutos = 0; - _dt = 0; - } - public void AtualizarValorConsumidoTotal(double consumoTotal_Ah) { double coulombsConsumidos = consumoTotal_Ah * 3600.0; - ConsumidoTotal_C = FuncoesMatematicas.Clamp(coulombsConsumidos, 0, CargaMaxima_C); + _consumidoIntegradoTotal_C = Clamp(coulombsConsumidos, 0, CargaMaxima_C); - if (_estadoInicializado) - { - double cargaAtualCalculada = _cargaInicialInstancia_C - ConsumidoTotal_C; - CargaAtual_C = FuncoesMatematicas.Clamp(cargaAtualCalculada, 0, CargaMaxima_C); - } + AtualizarCargaAtualPeloSocOuIntegrador(); } public bool ConseguePercorrer(double distanciaRequerida_m) @@ -408,5 +499,65 @@ namespace AgroBase.Services return DistanciaMaximaSegura_m >= distanciaRequerida_m; } + private void ResetarTudo() + { + Iniciado = false; + BmsLigado = false; + OperacaoAtiva = false; + OperacaoFinalizada = false; + + _estadoInicializado = false; + _ultimaLeituraIntegrada = null; + + CargaAtual_C = 0; + _socInicialInstancia = 0; + _socInicialOperacao = 0; + + _consumidoIntegradoTotal_C = 0; + _consumidoIntegradoOperacao_C = 0; + + EnergiaConsumidaTotal_Wh = 0; + _energiaConsumidaInicioOperacao_Wh = 0; + _energiaConsumidaFimOperacao_Wh = 0; + + PercentualBateria = 0; + Temperatura = 0; + CorrenteInstantanea = 0; + CorrenteDescarga_A = 0; + TensaoInstantanea = 0; + PotenciaInstantanea = 0; + PotenciaDescarga_W = 0; + TempoEstimadoMinutos = 0; + _dt = 0; + + ResetarHistoricosAutonomia(); + } + + private void ResetarHistoricosAutonomia() + { + _historicoCorrenteDescarga.Clear(); + _historicoPotenciaDescarga.Clear(); + + _historicoDeltaDist_m.Clear(); + _historicoDeltaWh.Clear(); + + _somaDistanciaJanela_m = 0; + _somaEnergiaJanela_Wh = 0; + + _distanciaTotalMetrosAnterior = 0; + _energiaConsumidaAnterior_Wh_Total = 0; + + WhPorMetro = 0; + WhPorMetroHistorico = 0; + + DistanciaMaximaTeorica_m = 0; + DistanciaMaximaSegura_m = 0; + DistanciaEstimadaRestante_m = 0; + } + + private static double Clamp(double value, double min, double max) + { + return FuncoesMatematicas.Clamp(value, min, max); + } } -} +} \ No newline at end of file diff --git a/AgroBase/AgroBase/Services/GPSService.cs b/AgroBase/AgroBase/Services/GPSService.cs index 109f6475f..83dad0454 100644 --- a/AgroBase/AgroBase/Services/GPSService.cs +++ b/AgroBase/AgroBase/Services/GPSService.cs @@ -107,8 +107,8 @@ namespace AgroBase.Services string[] comandos = { // Bauds $"config com1 115200\r\n", - $"config {porta_usb} 115200\r\n", - $"config {porta_entrada} 115200\r\n", + $"config com2 115200\r\n", + $"config com3 115200\r\n", // limpa logs das portas $"unlog com1\r\n", @@ -158,8 +158,9 @@ namespace AgroBase.Services string distancia_min = "0"; string[] comandos = { // Bauds - $"config {porta_usb} 115200\r\n", - $"config {porta_saida} 115200\r\n", + $"config com1 115200\r\n", + $"config com2 115200\r\n", + $"config com3 115200\r\n", // limpa logs das portas $"unlog com1\r\n", diff --git a/AgroBase/AgroBase/Services/HerbicideCounterService.cs b/AgroBase/AgroBase/Services/HerbicideCounterService.cs index 483ccb24b..08a72b17c 100644 --- a/AgroBase/AgroBase/Services/HerbicideCounterService.cs +++ b/AgroBase/AgroBase/Services/HerbicideCounterService.cs @@ -9,138 +9,129 @@ namespace AgroBase.Services { public class HerbicideCounterService { - public HerbicideCounterService(double capacidadeLitros, double densidadeProduto, double l_m_base = 0.1188) // 3km/h ~ 3x 33 ml/s + public HerbicideCounterService(double capacidadeLitros, double densidadeProduto, double vazaoPorBicoBase_mLs = 33.0) { CapacidadeLitros = capacidadeLitros; - VolumeAtual_L = 0; - VolumeConsumido_L = 0; DensidadeProduto = densidadeProduto; - LPorMetroBase = l_m_base; - MargemSegurancaAutonomia = 0.30; // 30% por padrão + QuantidadeBicos = VariaveisEquipamento.QuantidadeBicosPulverizadores; + + VazaoPorBicoBase_mLs = vazaoPorBicoBase_mLs; + VazaoPorBicoBase_LMin = vazaoPorBicoBase_mLs * 60.0 / 1000.0; + VazaoTotalTodosBicos_LMin = VazaoPorBicoBase_LMin * QuantidadeBicos; + + MargemSegurancaAutonomia = 0.30; + + ResetarTudo(); } public bool Iniciado { get; private set; } + public bool OperacaoAtiva { get; private set; } + public bool OperacaoFinalizada { get; private set; } - // ---------- CONFIGURAÇÕES / ESTADO BÁSICO ---------- - - /// - /// Capacidade máxima do reservatório em litros. - /// public double CapacidadeLitros { get; } - - /// - /// Densidade do produto. - /// public double DensidadeProduto { get; } - - /// - /// Massa atual lida no reservatório (Kg). - /// + + public int QuantidadeBicos { get; } + + public double VazaoPorBicoBase_mLs { get; } + public double VazaoPorBicoBase_LMin { get; } + public double VazaoTotalTodosBicos_LMin { get; } + public double MassaAtual_Kg { get; private set; } public double MassaAtual_Kg_Calc { get; private set; } - - /// - /// Volume atual estimado no reservatório (L). - /// public double VolumeAtual_L { get; private set; } - /// - /// Volume total consumido desde o início (L). - /// - public double VolumeConsumido_L { get; private set; } + private double _volumeInicialInstancia_L; + private double _volumeInicialOperacao_L; + + public double VolumeConsumidoTotal_L => Math.Max(0, _volumeInicialInstancia_L - VolumeAtual_L); + + public double VolumeConsumidoOperacao_L + { + get + { + if (!OperacaoAtiva && !OperacaoFinalizada) + return 0; + + return Math.Max(0, _volumeInicialOperacao_L - VolumeAtual_L); + } + } public double PercentualAtual { get { if (CapacidadeLitros <= 0) return 0; - var p = (VolumeAtual_L / CapacidadeLitros) * 100.0; - return FuncoesMatematicas.Clamp(p, 0, 100); + return Clamp((VolumeAtual_L / CapacidadeLitros) * 100.0, 0, 100); } } - public double PercentualConsumido + public double PercentualConsumidoTotal { get { - return 100.0 - PercentualAtual; + if (CapacidadeLitros <= 0) return 0; + return Clamp((VolumeConsumidoTotal_L / CapacidadeLitros) * 100.0, 0, 100); } } - public double HerbicidaPorAtuacao { get; private set; } + public double PercentualConsumidoOperacao + { + get + { + if (CapacidadeLitros <= 0) return 0; + return Clamp((VolumeConsumidoOperacao_L / CapacidadeLitros) * 100.0, 0, 100); + } + } + + public double PercentualConsumidoOperacaoDaCargaInicial + { + get + { + if (_volumeInicialOperacao_L <= 0) return 0; + return Clamp((VolumeConsumidoOperacao_L / _volumeInicialOperacao_L) * 100.0, 0, 100); + } + } + + public int BicosAtivos { get; private set; } + public bool Pulverizando { get; private set; } + public bool PulverizadorLiberado { get; private set; } + + public double VazaoFluxoAtual_mLs { get; private set; } + public double VazaoFluxoAtual_LMin => VazaoFluxoAtual_mLs * 60.0 / 1000.0; + + public double VazaoEstimativaBicos_mLs { get; private set; } + public double VazaoReferencia_mLs { get; private set; } + + public double LPorMinuto { get; private set; } + public double LPorMinutoEstimadoBicos { get; private set; } + + public double LPorMetro { get; private set; } + public double LPorMetroHistorico { get; private set; } + public double LPorMetroBase { get; private set; } - /// - /// Margem de segurança para estimativa (0.30 = 30%). - /// public double MargemSegurancaAutonomia { get; set; } - // ---------- AUTONOMIA EM METROS (L/m) ---------- - - /// - /// Consumo médio de herbicida por metro pulverizado (L/m), - /// calculado numa janela móvel de distância. - /// - public double LPorMetro { get; private set; } - - public double LPorMetroBase { get; set; } - public double LPorMetroHistorico { get; private set; } - - public double LPorMetroReferencia - { - get - { - if (LPorMetro > 0) return LPorMetro; - if (LPorMetroHistorico > 0) return LPorMetroHistorico; - return LPorMetroBase; - } - } - - /// - /// Distância máxima teórica que ainda pode ser pulverizada (sem margem), em metros. - /// public double DistanciaMaximaTeorica_m { get; private set; } - - /// - /// Distância máxima segura que ainda pode ser pulverizada (com margem), em metros. - /// public double DistanciaMaximaSegura_m { get; private set; } - - /// - /// Alias mais descritivo para uso direto pelo Manager. - /// public double DistanciaEstimadaRestantePulverizando_m => DistanciaMaximaSegura_m; - // ---------- AUTONOMIA EM TEMPO (L/min) ---------- - - /// - /// Consumo médio de herbicida por minuto (L/min) durante pulverização. - /// - public double LPorMinuto { get; private set; } - - /// - /// Tempo estimado restante pulverizando, em minutos. - /// public double TempoEstimadoMinutosPulverizando { get; private set; } - // ---------- JANELAS DE MÉDIA MÓVEL ---------- + public double HerbicidaPorAtuacao_L { get; private set; } - // Distância: vamos usar uma janela de ~50 m (mesma ideia da bateria) private const double JanelaAutonomia_m = 50.0; - - // Tempo: janela de ~60 s pulverizando private const double JanelaTempo_s = 60.0; - private readonly Queue<(double deltaDist_m, double deltaVol_L)> _janelaDist = new Queue<(double deltaDist_m, double deltaVol_L)>(); - private readonly Queue<(double deltaTempo_s, double deltaVol_L)> _janelaTempo = new Queue<(double deltaTempo_s, double deltaVol_L)>(); + private readonly Queue<(double dist_m, double vol_L)> _janelaDist = new Queue<(double dist_m, double vol_L)>(); + private readonly Queue<(double tempo_s, double vol_L)> _janelaTempo = new Queue<(double tempo_s, double vol_L)>(); private double _somaDistanciaJanela_m; private double _somaVolumeDistJanela_L; - private double _somaTempoJanela_s; private double _somaVolumeTempoJanela_L; - // Valores anteriores para cálculo de deltas private double _volumeAnterior_L; private double _distanciaTotalAnterior_m; private double _tempoTotalAnterior_s; @@ -148,192 +139,258 @@ namespace AgroBase.Services private bool _inicializado; private SensorModel SensorMassaReservatorio => Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.Sensores?.FirstOrDefault(x => x.Componente == S_Code.sMAS && x.ID == "MASRS"); - public AtuadorBombaModel AgitadorCalda => Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == "AGTCLD"); - public bool PulverizadorLiberado { get; set; } - /// - /// Atualiza o estado do sistema de herbicida. - /// - /// Chamar periodicamente com: - /// - volumeMedido_L: leitura de nível atual do reservatório (L) - /// - pulverizando: true se algum bico está ativo - /// - distanciaTotalMetrosAtual: distância total acumulada (m) - /// - tempoTotalSegundosAtual: tempo total de operação (s) - /// - public void AtualizarConsumo(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual) + public AtuadorBombaModel AgitadorCalda => Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == "AGTCLD"); + + public void IniciarOperacao(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual) + { + if (!_inicializado) + return; + + _volumeInicialOperacao_L = VolumeAtual_L; + + _distanciaTotalAnterior_m = distanciaTotalMetrosAtual; + _tempoTotalAnterior_s = tempoTotalSegundosAtual; + + OperacaoAtiva = true; + OperacaoFinalizada = false; + + ResetarJanelas(); + } + + public void FinalizarOperacao() + { + if (!OperacaoAtiva) + return; + + OperacaoAtiva = false; + OperacaoFinalizada = true; + } + + public void AtualizarConsumo(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual, double velocidadeAtual_m_s) { Iniciado = SensorMassaReservatorio?.Inicializado ?? false; if (!Iniciado) return; - bool pulverizando = Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.BicosPulverizadores?.Any(x => x.Inicializado && x._EstadoLeitura == Estado.Ligado) ?? false; - double massaLida_Kg = SensorMassaReservatorio?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Massa)?.atual?.valor ?? 0; - double massaLida_Kg_bruto = SensorMassaReservatorio?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.LeituraBruta)?.atual?.valor ?? 0; + LerReservatorio(out double massaLida_Kg, out double massaCalc_Kg, out double volumeMedido_L); - double.TryParse(SensorMassaReservatorio.Parametros.FirstOrDefault(x => x.Descricao == "Offset")?.Valor ?? "0", out double offset); - double massa_calc = (massaLida_Kg_bruto * 1) + offset; + MassaAtual_Kg = massaLida_Kg; + MassaAtual_Kg_Calc = massaCalc_Kg; + VolumeAtual_L = volumeMedido_L; - double volumeMedido_L = massa_calc / DensidadeProduto; - int qtd_atuacoes = Variaveis.OperacaoEmAndamento.Controle?.Bicos?.Sum(x => x.Atuacoes) ?? 0; + BicosAtivos = ObterQuantidadeBicosAtivos(); + Pulverizando = BicosAtivos > 0; - // Sanitiza volume - if (volumeMedido_L < 0) volumeMedido_L = 0; - if (volumeMedido_L > CapacidadeLitros) volumeMedido_L = CapacidadeLitros; + VazaoFluxoAtual_mLs = ObterVazaoFluxo_mLs(); + VazaoEstimativaBicos_mLs = BicosAtivos * VazaoPorBicoBase_mLs; + VazaoReferencia_mLs = VazaoFluxoAtual_mLs > 0 + ? VazaoFluxoAtual_mLs + : VazaoEstimativaBicos_mLs; + + LPorMinutoEstimadoBicos = VazaoEstimativaBicos_mLs * 60.0 / 1000.0; + + PulverizadorLiberado = + PercentualAtual >= VariaveisEquipamento.PercentualReservatorioMinCritio; - // Inicialização if (!_inicializado) { - _inicializado = true; - _volumeAnterior_L = volumeMedido_L; - _distanciaTotalAnterior_m = distanciaTotalMetrosAtual; - _tempoTotalAnterior_s = tempoTotalSegundosAtual; - - MassaAtual_Kg = massaLida_Kg; - MassaAtual_Kg_Calc = massa_calc; - VolumeAtual_L = volumeMedido_L; + Inicializar(distanciaTotalMetrosAtual, tempoTotalSegundosAtual); + AtualizarAutonomia(velocidadeAtual_m_s); return; } - MassaAtual_Kg = massaLida_Kg; - MassaAtual_Kg_Calc = massa_calc; - VolumeAtual_L = volumeMedido_L; + double deltaVolumeMassa_L = Math.Max(0, _volumeAnterior_L - VolumeAtual_L); + _volumeAnterior_L = VolumeAtual_L; - PulverizadorLiberado = PercentualAtual >= VariaveisEquipamento.PercentualReservatorioMinCritio; - - // --- Cálculo de deltas básicos --- - - double deltaVolume_L = 0.0; - double diffVol = _volumeAnterior_L - volumeMedido_L; - - // Só considera volume consumido (queda de nível) e apenas quando pulverizando - if (pulverizando && diffVol > 0) - { - deltaVolume_L = diffVol; - VolumeConsumido_L += deltaVolume_L; - } - - if (qtd_atuacoes > 0) - { - HerbicidaPorAtuacao = VolumeConsumido_L / qtd_atuacoes; - } - - _volumeAnterior_L = volumeMedido_L; - - double deltaDist_m = distanciaTotalMetrosAtual - _distanciaTotalAnterior_m; - if (deltaDist_m < 0) deltaDist_m = 0; + double deltaDist_m = Math.Max(0, distanciaTotalMetrosAtual - _distanciaTotalAnterior_m); _distanciaTotalAnterior_m = distanciaTotalMetrosAtual; - double deltaTempo_s = tempoTotalSegundosAtual - _tempoTotalAnterior_s; - if (deltaTempo_s < 0) deltaTempo_s = 0; + double deltaTempo_s = Math.Max(0, tempoTotalSegundosAtual - _tempoTotalAnterior_s); _tempoTotalAnterior_s = tempoTotalSegundosAtual; - // --- Atualiza janela de distância (L/m) --- - AtualizarJanelaDistancia(deltaDist_m, deltaVolume_L, pulverizando); + double deltaVolumeFluxo_L = 0; - // --- Atualiza janela de tempo (L/min) --- - AtualizarJanelaTempo(deltaTempo_s, deltaVolume_L, pulverizando); + if (Pulverizando && deltaTempo_s > 0 && VazaoReferencia_mLs > 0) + deltaVolumeFluxo_L = VazaoReferencia_mLs * deltaTempo_s / 1000.0; - // --- Atualiza métricas de autonomia --- - AtualizarAutonomia(); + double deltaVolumeParaTaxa_L = EscolherDeltaVolumeParaTaxa(deltaVolumeMassa_L, deltaVolumeFluxo_L); + + if (Pulverizando && deltaVolumeParaTaxa_L > 0) + { + AtualizarJanelaDistancia(deltaDist_m, deltaVolumeParaTaxa_L); + AtualizarJanelaTempo(deltaTempo_s, deltaVolumeParaTaxa_L); + } + + AtualizarHerbicidaPorAtuacao(); + AtualizarAutonomia(velocidadeAtual_m_s); } - private void AtualizarJanelaDistancia(double deltaDist_m, double deltaVolume_L, bool pulverizando) + private double EscolherDeltaVolumeParaTaxa(double deltaMassa_L, double deltaFluxo_L) { - // Só faz sentido atualizar se o robô andou e estava pulverizando - if (!pulverizando || deltaDist_m <= 0 || deltaVolume_L <= 0) + if (deltaFluxo_L > 0) + return deltaFluxo_L; + + return deltaMassa_L; + } + + private void LerReservatorio(out double massaLida_Kg, out double massaCalc_Kg, out double volumeMedido_L) + { + massaLida_Kg = SensorMassaReservatorio?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Massa)?.atual?.valor ?? 0; + + double leituraBruta = SensorMassaReservatorio?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.LeituraBruta)?.atual?.valor ?? 0; + + double.TryParse(SensorMassaReservatorio?.Parametros?.FirstOrDefault(x => x.Descricao == "Offset")?.Valor ?? "0", out double offset); + + massaCalc_Kg = leituraBruta + offset; + + volumeMedido_L = DensidadeProduto > 0 ? massaCalc_Kg / DensidadeProduto : 0; + + volumeMedido_L = Clamp(volumeMedido_L, 0, CapacidadeLitros); + } + + private double ObterVazaoFluxo_mLs() + { + var dados = Variaveis.OperacaoEmAndamento.DispAtu?.Dados; + + if (dados == null) + return 0; + + double vazao = dados.VazaoFluxoMLpSReferencia; + + if (vazao < 0) + return 0; + + return vazao; + } + + private int ObterQuantidadeBicosAtivos() + { + return Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.BicosPulverizadores?.Count(x => x.Inicializado && x.ComandoAtuar) ?? 0; + } + + private void Inicializar(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual) + { + _inicializado = true; + + _volumeInicialInstancia_L = VolumeAtual_L; + _volumeAnterior_L = VolumeAtual_L; + + _distanciaTotalAnterior_m = distanciaTotalMetrosAtual; + _tempoTotalAnterior_s = tempoTotalSegundosAtual; + } + + private void AtualizarJanelaDistancia(double deltaDist_m, double deltaVolume_L) + { + if (deltaDist_m <= 0 || deltaVolume_L <= 0) return; _janelaDist.Enqueue((deltaDist_m, deltaVolume_L)); + _somaDistanciaJanela_m += deltaDist_m; _somaVolumeDistJanela_L += deltaVolume_L; - // Mantém a janela limitada a JanelaAutonomia_m while (_somaDistanciaJanela_m > JanelaAutonomia_m && _janelaDist.Count > 0) { - var (dOld, vOld) = _janelaDist.Dequeue(); - _somaDistanciaJanela_m -= dOld; - _somaVolumeDistJanela_L -= vOld; + var old = _janelaDist.Dequeue(); + + _somaDistanciaJanela_m -= old.dist_m; + _somaVolumeDistJanela_L -= old.vol_L; } - if (_somaDistanciaJanela_m > 0 && _somaVolumeDistJanela_L > 0) + if (_somaDistanciaJanela_m > 0.5) { LPorMetro = _somaVolumeDistJanela_L / _somaDistanciaJanela_m; - double alpha = 0.2; - if (LPorMetroHistorico <= 0) - LPorMetroHistorico = LPorMetro; - else - LPorMetroHistorico = LPorMetroHistorico * (1 - alpha) + LPorMetro * alpha; + const double alpha = 0.2; + + LPorMetroHistorico = LPorMetroHistorico <= 0 + ? LPorMetro + : LPorMetroHistorico * (1.0 - alpha) + LPorMetro * alpha; } } - private void AtualizarJanelaTempo(double deltaTempo_s, double deltaVolume_L, bool pulverizando) + private void AtualizarJanelaTempo(double deltaTempo_s, double deltaVolume_L) { - // Só faz sentido atualizar se o tempo avançou, estava pulverizando e houve consumo - if (!pulverizando || deltaTempo_s <= 0 || deltaVolume_L <= 0) + if (deltaTempo_s <= 0 || deltaVolume_L <= 0) return; _janelaTempo.Enqueue((deltaTempo_s, deltaVolume_L)); + _somaTempoJanela_s += deltaTempo_s; _somaVolumeTempoJanela_L += deltaVolume_L; - // Mantém a janela de tempo limitada a JanelaTempo_s while (_somaTempoJanela_s > JanelaTempo_s && _janelaTempo.Count > 0) { - var (tOld, vOld) = _janelaTempo.Dequeue(); - _somaTempoJanela_s -= tOld; - _somaVolumeTempoJanela_L -= vOld; + var old = _janelaTempo.Dequeue(); + + _somaTempoJanela_s -= old.tempo_s; + _somaVolumeTempoJanela_L -= old.vol_L; } - if (_somaTempoJanela_s > 0 && _somaVolumeTempoJanela_L > 0) - { - // L/s * 60 = L/min + if (_somaTempoJanela_s > 1.0) LPorMinuto = (_somaVolumeTempoJanela_L / _somaTempoJanela_s) * 60.0; - } } - private void AtualizarAutonomia() + private void AtualizarHerbicidaPorAtuacao() { - // ----- Distância máxima pulverizando (m) ----- - if (VolumeAtual_L > 0 && LPorMetro > 0) - { - DistanciaMaximaTeorica_m = VolumeAtual_L / LPorMetro; + int qtdAtuacoes = Variaveis.OperacaoEmAndamento.Controle?.Bicos?.Sum(x => x.Atuacoes) ?? 0; - double margem = FuncoesMatematicas.Clamp(MargemSegurancaAutonomia, 0.0, 0.9); - DistanciaMaximaSegura_m = DistanciaMaximaTeorica_m * (1.0 - margem); - if (DistanciaMaximaSegura_m < 0) - DistanciaMaximaSegura_m = 0; - } - else - { - DistanciaMaximaTeorica_m = 0; - DistanciaMaximaSegura_m = 0; - } + if (qtdAtuacoes > 0) + HerbicidaPorAtuacao_L = VolumeConsumidoOperacao_L / qtdAtuacoes; + } - // ----- Tempo máximo pulverizando (min) ----- - double consumoRef_LPorMetro = LPorMetroReferencia; + private void AtualizarAutonomia(double velocidadeAtual_m_s) + { + double consumoRef_LPorMetro = CalcularLPorMetroReferencia(velocidadeAtual_m_s); if (VolumeAtual_L > 0 && consumoRef_LPorMetro > 0) { DistanciaMaximaTeorica_m = VolumeAtual_L / consumoRef_LPorMetro; - double margem = FuncoesMatematicas.Clamp(MargemSegurancaAutonomia, 0.0, 0.9); + double margem = Clamp(MargemSegurancaAutonomia, 0.0, 0.9); DistanciaMaximaSegura_m = DistanciaMaximaTeorica_m * (1.0 - margem); - if (DistanciaMaximaSegura_m < 0) - DistanciaMaximaSegura_m = 0; } else { DistanciaMaximaTeorica_m = 0; DistanciaMaximaSegura_m = 0; } + + double consumoTempoRef_LMin = LPorMinuto > 0 + ? LPorMinuto + : LPorMinutoEstimadoBicos; + + if (VolumeAtual_L > 0 && consumoTempoRef_LMin > 0) + { + double margem = Clamp(MargemSegurancaAutonomia, 0.0, 0.9); + + TempoEstimadoMinutosPulverizando = + (VolumeAtual_L / consumoTempoRef_LMin) * (1.0 - margem); + } + else + { + TempoEstimadoMinutosPulverizando = 0; + } + } + + private double CalcularLPorMetroReferencia(double velocidadeAtual_m_s) + { + if (LPorMetroHistorico > 0) + return LPorMetroHistorico; + + if (LPorMetro > 0) + return LPorMetro; + + double velocidade_m_min = velocidadeAtual_m_s * 60.0; + + if (velocidade_m_min > 0.1 && LPorMinutoEstimadoBicos > 0) + return LPorMinutoEstimadoBicos / velocidade_m_min; + + return LPorMetroBase; } - /// - /// Helper direto para o Manager/Health: - /// indica se o volume atual é suficiente para pulverizar uma rua de certo comprimento (m). - /// public bool ConseguePulverizarRua(double comprimentoRua_m) { if (comprimentoRua_m <= 0) @@ -342,28 +399,80 @@ namespace AgroBase.Services return DistanciaMaximaSegura_m >= comprimentoRua_m; } - /// - /// Permite "forçar" um volume inicial (por exemplo, depois de reabastecer). - /// public void DefinirVolumeAtual(double volume_L) { - volume_L = FuncoesMatematicas.Clamp(volume_L, 0, CapacidadeLitros); + volume_L = Clamp(volume_L, 0, CapacidadeLitros); VolumeAtual_L = volume_L; _volumeAnterior_L = volume_L; - VolumeConsumido_L = CapacidadeLitros - VolumeAtual_L; + _volumeInicialInstancia_L = volume_L; - // Zera as janelas (melhor não carregar histórico de antes de reabastecer) + ResetarJanelas(); + } + + private void ResetarTudo() + { + Iniciado = false; + OperacaoAtiva = false; + OperacaoFinalizada = false; + + MassaAtual_Kg = 0; + MassaAtual_Kg_Calc = 0; + VolumeAtual_L = 0; + + _volumeInicialInstancia_L = 0; + _volumeInicialOperacao_L = 0; + + BicosAtivos = 0; + Pulverizando = false; + PulverizadorLiberado = false; + + VazaoFluxoAtual_mLs = 0; + VazaoEstimativaBicos_mLs = 0; + VazaoReferencia_mLs = 0; + + LPorMetro = 0; + LPorMetroHistorico = 0; + LPorMetroBase = 0; + LPorMinuto = 0; + LPorMinutoEstimadoBicos = 0; + + DistanciaMaximaTeorica_m = 0; + DistanciaMaximaSegura_m = 0; + TempoEstimadoMinutosPulverizando = 0; + + HerbicidaPorAtuacao_L = 0; + + _inicializado = false; + _volumeAnterior_L = 0; + _distanciaTotalAnterior_m = 0; + _tempoTotalAnterior_s = 0; + + ResetarJanelas(); + } + + private void ResetarJanelas() + { _janelaDist.Clear(); _janelaTempo.Clear(); + _somaDistanciaJanela_m = 0; _somaVolumeDistJanela_L = 0; + _somaTempoJanela_s = 0; _somaVolumeTempoJanela_L = 0; + + LPorMetro = 0; + LPorMinuto = 0; + } + + private static double Clamp(double value, double min, double max) + { + return FuncoesMatematicas.Clamp(value, min, max); } - + // ---------------- AGITADOR ---------------- private bool _agitadorIntermitenteLigado = false; private DateTime _agitadorUltimaTroca = DateTime.MinValue; @@ -378,7 +487,6 @@ namespace AgroBase.Services private int AtuAgitadorTempoDesligadoMs = 30000; private int AtuAgitadorBoostRetomadaMs = 15000; - private double PotenciaLimitePorNivel(double nivelPct) { if (nivelPct <= 5) return 0; @@ -402,7 +510,6 @@ namespace AgroBase.Services bool operacaoIniciada = Variaveis.OperacaoEmAndamento?.Iniciado ?? false; bool pausado = Variaveis.OperacaoEmAndamento?.Pausa ?? false; - bool operacaoAtiva = operacaoIniciada && !pausado; bool bicosAtuando = @@ -412,7 +519,6 @@ namespace AgroBase.Services double potenciaFinal = 0; DateTime agora = DateTime.Now; - // Boost ao iniciar/retomar operação if (operacaoAtiva && !_agitadorOperacaoAtivaAnterior) { _agitadorBoostAte = agora.AddMilliseconds(AtuAgitadorBoostRetomadaMs); @@ -438,10 +544,6 @@ namespace AgroBase.Services else potenciaFinal = AtuAgitadorPotenciaManutencao; } - else - { - potenciaFinal = 0; - } break; case ModoAgitadorCalda.Intermitente: @@ -472,18 +574,13 @@ namespace AgroBase.Services : 0; } break; - - default: - potenciaFinal = 0; - break; } potenciaFinal = Math.Min(potenciaFinal, PotenciaLimitePorNivel(PercentualAtual)); - potenciaFinal = FuncoesMatematicas.Clamp(potenciaFinal, 0, 100); + potenciaFinal = Clamp(potenciaFinal, 0, 100); AgitadorCalda.PotenciaSP = potenciaFinal; _agitadorOperacaoAtivaAnterior = operacaoAtiva; } - } -} +} \ No newline at end of file diff --git a/AgroBase/AgroBase/Services/Operadores/CameraWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/CameraWorkerService.cs index 576077ccc..285ee2509 100644 --- a/AgroBase/AgroBase/Services/Operadores/CameraWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/CameraWorkerService.cs @@ -104,9 +104,9 @@ namespace AgroBase.Services.Operadores { foreach (var cam in cameras) { - var camera_str = RedisService.Get(RedisService.CamKey(cam.Key)); + var camera_str = RedisService.Get(RedisService.CamKey(cam.Key)) ?? ""; var camera = JsonConvert.DeserializeObject(camera_str); - if (camera.mx_id == null) continue; + if (camera?.mx_id == null) continue; CamerasConectadas.Add(camera); } } @@ -114,5 +114,19 @@ namespace AgroBase.Services.Operadores return CamerasConectadas; } + public static OAKCameraModel AtualizarDadosCamera(string mx_id) + { + var cam = CamerasConectadas.FirstOrDefault(x => x.mx_id == mx_id); + if (cam == null) return null; + var camera_str = RedisService.Get(RedisService.CamKey(cam.mx_id)) ?? ""; + var camera = JsonConvert.DeserializeObject(camera_str); + if (camera?.mx_id == null) return null; + var imu_str = RedisService.Get(RedisService.CamImuKey(cam.mx_id)) ?? ""; + var imu = JsonConvert.DeserializeObject(imu_str); + camera.imu = imu?.dados; + cam = camera; + return camera.ToOAK(); + } + } } diff --git a/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs index 8140fa0fc..9ce260382 100644 --- a/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs @@ -221,12 +221,14 @@ namespace AgroBase.Services.Operadores ("path_ia_model_ruas_seg", VersionamentoService.ArquivoModeloSegStreetDetector.CaminhoCompleto), ("path_ia_labelmap_ruas_seg", VersionamentoService.ArquivoLabelmapSegStreetDetector.CaminhoCompleto), ("path_ia_norm_stats_ruas_seg", VersionamentoService.ArquivoModeloNormstatsStreetDetector.CaminhoCompleto), - ("ia_backbone_ruas_seg", "nvidia/segformer-b0-finetuned-ade-512-512"), + ("ia_backbone_ruas_seg", "C:\\AgroBaseModels\\Backbones\\nvidia_mit_b0"), + ("ia_mode_ruas", "dual_label"), ("path_ia_model_ruas_det", VersionamentoService.ArquivoModeloDetStreetDetector.CaminhoCompleto), ("path_ia_model_ervas", VersionamentoService.ArquivoModeloWeedDetector.CaminhoCompleto), ("path_ia_labelmap_ervas", VersionamentoService.ArquivoModeloLabelmapWeedDetector.CaminhoCompleto), ("path_ia_norm_stats_ervas", VersionamentoService.ArquivoModeloNormstatsWeedDetector.CaminhoCompleto), - ("ia_backbone_ervas", "nvidia/segformer-b1-finetuned-ade-512-512"), + ("path_ia_module_params_ervas", VersionamentoService.ArquivoModeloModuleParamsWeedDetector.CaminhoCompleto), + ("ia_backbone_ervas", "C:\\AgroBaseModels\\Backbones\\nvidia_mit_b1"), ("angulo_roll_max", VariaveisEquipamento.AnguloInclinacaoRollMax), ("angulo_pitch_max", VariaveisEquipamento.AnguloInclinacaoPitchMax) ); @@ -313,9 +315,9 @@ namespace AgroBase.Services.Operadores ), ("Snr", new { - percentual_altura_solo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.PercentualAlturaSolo, - distancia_maxima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.DistanciaMaximaVisao, - distancia_minima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.DistanciaMinimaVisao + percentual_altura_solo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.PercentualAlturaSolo ?? 0.1, + distancia_maxima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.DistanciaMaximaVisao ?? 5000, + distancia_minima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho?.DistanciaMinimaVisao ?? 300 } ) ); @@ -757,15 +759,76 @@ namespace AgroBase.Services.Operadores ); var DadosMod = _Sensoriamento.DadosPerformance; - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Npc), + RedisService.AtualizarCampos( + RedisService.ModKey(Enums.T_Code.Npc), + // Base / sincronismo ("timestamp", agora), + ("timestamp_unix_ms", DadosMod.TimestampUnixMs), ("freq_base", 1.0 / Variaveis.OperacaoEmAndamento.tmrLeiturasInterval), - ("cpu_load", DadosMod.CPU_load ?? 0), - ("gpu_load", DadosMod.GPU_load ?? 0), - ("hdd_load", DadosMod.HDD_load ?? 0), - ("ram_usage", DadosMod.RAM_usage ?? 0), - ("cpu_temp", DadosMod.CPU_temp ?? 0), - ("gpu_temp", DadosMod.GPU_temp ?? 0) + + // CPU + ("cpu_load_percent", DadosMod.CPU_load_percent ?? 0), + ("cpu_temp_package_c", DadosMod.CPU_temp_package_c ?? 0), + ("cpu_power_package_w", DadosMod.CPU_power_package_w ?? 0), + ("cpu_power_cores_w", DadosMod.CPU_power_cores_w ?? 0), + ("cpu_power_graphics_w", DadosMod.CPU_power_graphics_w ?? 0), + ("cpu_power_dram_w", DadosMod.CPU_power_dram_w ?? 0), + ("cpu_clock_avg_mhz", DadosMod.CPU_clock_avg_mhz ?? 0), + ("cpu_clock_max_mhz", DadosMod.CPU_clock_max_mhz ?? 0), + ("cpu_bus_speed_mhz", DadosMod.CPU_bus_speed_mhz ?? 0), + + // RAM + ("ram_usage_percent", DadosMod.RAM_usage_percent ?? 0), + ("ram_used_gb", DadosMod.RAM_used_gb ?? 0), + ("ram_available_gb", DadosMod.RAM_available_gb ?? 0), + ("ram_total_gb", DadosMod.RAM_total_gb ?? 0), + + // GPU loads + ("gpu_load_percent", DadosMod.GPU_load_percent ?? 0), + ("gpu_core_load_percent", DadosMod.GPU_core_load_percent ?? 0), + ("gpu_frame_buffer_load_percent", DadosMod.GPU_frame_buffer_load_percent ?? 0), + ("gpu_memory_load_percent", DadosMod.GPU_memory_load_percent ?? 0), + ("gpu_video_engine_load_percent", DadosMod.GPU_video_engine_load_percent ?? 0), + ("gpu_bus_interface_load_percent", DadosMod.GPU_bus_interface_load_percent ?? 0), + + // GPU térmico / energia / clock + ("gpu_temp_core_c", DadosMod.GPU_temp_core_c ?? 0), + ("gpu_power_w", DadosMod.GPU_power_w ?? 0), + ("gpu_core_clock_mhz", DadosMod.GPU_core_clock_mhz ?? 0), + ("gpu_memory_clock_mhz", DadosMod.GPU_memory_clock_mhz ?? 0), + ("gpu_shader_clock_mhz", DadosMod.GPU_shader_clock_mhz ?? 0), + + // GPU fan / PCIe + ("gpu_fan_percent", DadosMod.GPU_fan_percent ?? 0), + ("gpu_fan_rpm", DadosMod.GPU_fan_rpm ?? 0), + ("gpu_pcie_rx", DadosMod.GPU_pcie_rx ?? 0), + ("gpu_pcie_tx", DadosMod.GPU_pcie_tx ?? 0), + + // VRAM + ("gpu_vram_total_mb", DadosMod.GPU_vram_total_mb ?? 0), + ("gpu_vram_used_mb", DadosMod.GPU_vram_used_mb ?? 0), + ("gpu_vram_free_mb", DadosMod.GPU_vram_free_mb ?? 0), + ("gpu_vram_used_percent", DadosMod.GPU_vram_used_percent ?? 0), + + // Disco + ("disk_used_space_percent", DadosMod.Disk_used_space_percent ?? 0), + + // Processo C# + ("process_thread_count", DadosMod.Process_thread_count), + ("process_working_set_mb", DadosMod.Process_working_set_mb), + ("process_private_memory_mb", DadosMod.Process_private_memory_mb), + ("process_virtual_memory_mb", DadosMod.Process_virtual_memory_mb), + ("process_cpu_time_total_s", DadosMod.Process_cpu_time_total_s), + + // ThreadPool + ("threadpool_available_worker_threads", DadosMod.ThreadPool_available_worker_threads), + ("threadpool_available_completion_port_threads", DadosMod.ThreadPool_available_completion_port_threads), + ("threadpool_max_worker_threads", DadosMod.ThreadPool_max_worker_threads), + ("threadpool_max_completion_port_threads", DadosMod.ThreadPool_max_completion_port_threads), + ("threadpool_used_worker_threads", DadosMod.ThreadPool_used_worker_threads), + ("threadpool_used_completion_port_threads", DadosMod.ThreadPool_used_completion_port_threads), + ("threadpool_used_worker_percent", DadosMod.ThreadPool_used_worker_percent), + ("threadpool_used_completion_port_percent", DadosMod.ThreadPool_used_completion_port_percent) ); var DadosCan = CanManager.CanService; diff --git a/AgroBase/AgroBase/Services/RedisService.cs b/AgroBase/AgroBase/Services/RedisService.cs index fcc09fd31..b016da625 100644 --- a/AgroBase/AgroBase/Services/RedisService.cs +++ b/AgroBase/AgroBase/Services/RedisService.cs @@ -31,6 +31,11 @@ namespace AgroBase.Services return CtxKey.DadosCameras + mx_id; } + public static string CamImuKey(string mx_id) + { + return CtxKey.DadosCameras + mx_id + "_Imu"; + } + public static string Get(string chave) { return _db.StringGet(chave); diff --git a/AgroBase/AgroBase/Services/VersionamentoService.cs b/AgroBase/AgroBase/Services/VersionamentoService.cs index be7c54cca..fb0782f96 100644 --- a/AgroBase/AgroBase/Services/VersionamentoService.cs +++ b/AgroBase/AgroBase/Services/VersionamentoService.cs @@ -92,6 +92,16 @@ namespace AgroBase.Services } } } + public static VersaoArquivoModel ArquivoModeloModuleParamsWeedDetector + { + get + { + lock (_ArquivoLock) + { + return _ArquivosVersionados.Where(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_WeedDetector).Skip(3).FirstOrDefault(); + } + } + } public static VersaoArquivoModel ArquivoParametros(T_Code Dispositivo) { lock (_ArquivoLock)