ajustes de conexao e log de dados bateria

This commit is contained in:
Diego Freitas 2026-03-06 15:38:40 -03:00
parent 18b795237f
commit 7ff339eaf6
8 changed files with 141 additions and 96 deletions

View File

@ -2482,6 +2482,7 @@ namespace AgroBase.Models
EnergiaConsumida_Wh = _Bateria.EnergiaConsumida_Wh, EnergiaConsumida_Wh = _Bateria.EnergiaConsumida_Wh,
WhPorMetro = _Bateria.WhPorMetro, WhPorMetro = _Bateria.WhPorMetro,
DistanciaEstimadaRestanteMetros = _Bateria.DistanciaEstimadaRestante_m, DistanciaEstimadaRestanteMetros = _Bateria.DistanciaEstimadaRestante_m,
Temperatura = _Bateria.Temperatura,
Barramentos = _DispSen?.Dados?.Sensores?.Where(x => x.Componente == S_Code.sCOR)?.Select(x => new OperacaoSensoriamentoLogBateriaBarramentoModel() Barramentos = _DispSen?.Dados?.Sensores?.Where(x => x.Componente == S_Code.sCOR)?.Select(x => new OperacaoSensoriamentoLogBateriaBarramentoModel()
{ {
ID = x.ID, ID = x.ID,
@ -2823,6 +2824,7 @@ namespace AgroBase.Models
public double WhPorMetro { get; set; } public double WhPorMetro { get; set; }
public double TempoEstimadoRestanteMinutos { get; set; } public double TempoEstimadoRestanteMinutos { get; set; }
public double DistanciaEstimadaRestanteMetros { get; set; } public double DistanciaEstimadaRestanteMetros { get; set; }
public double Temperatura { get; set; }
public List<OperacaoSensoriamentoLogBateriaBarramentoModel> Barramentos { get; set; } public List<OperacaoSensoriamentoLogBateriaBarramentoModel> Barramentos { get; set; }
public OperacaoSensoriamentoLogBateriaModel Clone() public OperacaoSensoriamentoLogBateriaModel Clone()
@ -2841,6 +2843,7 @@ namespace AgroBase.Models
WhPorMetro = WhPorMetro, WhPorMetro = WhPorMetro,
TempoEstimadoRestanteMinutos = TempoEstimadoRestanteMinutos, TempoEstimadoRestanteMinutos = TempoEstimadoRestanteMinutos,
DistanciaEstimadaRestanteMetros = DistanciaEstimadaRestanteMetros, DistanciaEstimadaRestanteMetros = DistanciaEstimadaRestanteMetros,
Temperatura = Temperatura,
Barramentos = new List<OperacaoSensoriamentoLogBateriaBarramentoModel>(Barramentos ?? new List<OperacaoSensoriamentoLogBateriaBarramentoModel>()) Barramentos = new List<OperacaoSensoriamentoLogBateriaBarramentoModel>(Barramentos ?? new List<OperacaoSensoriamentoLogBateriaBarramentoModel>())
}; };
} }

View File

@ -88,6 +88,7 @@ namespace AgroBase.Services
return CargaAtualAmp * tensaoNominal; // Ah * V = Wh return CargaAtualAmp * tensaoNominal; // Ah * V = Wh
} }
} }
public double Temperatura { get; private set; }
public double WhPorMetro { get; private set; } public double WhPorMetro { get; private set; }
public double WhPorMetroBase { get; set; } // configurado externamente public double WhPorMetroBase { get; set; } // configurado externamente
@ -123,6 +124,7 @@ namespace AgroBase.Services
double bms_corrente = DalyBms?.Pack?.Corrente_A ?? 0; double bms_corrente = DalyBms?.Pack?.Corrente_A ?? 0;
double bms_potencia = bms_tensao * bms_corrente; double bms_potencia = bms_tensao * bms_corrente;
double bms_soc = DalyBms?.Pack?.SOC_percent ?? 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"); var Sensor = Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Sensores?.FirstOrDefault(x => x.ID == "AB36V");
bool sen_iniciado = Sensor?.Inicializado ?? false; bool sen_iniciado = Sensor?.Inicializado ?? false;
@ -132,6 +134,7 @@ namespace AgroBase.Services
double sen_corrente = (Sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Corrente)?.atual?.valor ?? 0) / 1000.0; double sen_corrente = (Sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Corrente)?.atual?.valor ?? 0) / 1000.0;
double sen_potencia = Sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Potencia)?.atual?.valor ?? 0; double sen_potencia = Sensor?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Potencia)?.atual?.valor ?? 0;
double sen_soc = Math.Round(FuncoesMatematicas.Map(sen_tensao, VariaveisEquipamento.TensaoMinimaBateria, VariaveisEquipamento.TensaoMaximaBateria, 0.0, 100.0), 2); 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; Iniciado = bms_iniciado || sen_iniciado;
BmsLigado = bms_iniciado; BmsLigado = bms_iniciado;
@ -145,6 +148,7 @@ namespace AgroBase.Services
CorrenteInstantanea = BmsLigado ? bms_corrente : sen_corrente; CorrenteInstantanea = BmsLigado ? bms_corrente : sen_corrente;
PotenciaInstantanea = BmsLigado ? bms_potencia : sen_potencia; PotenciaInstantanea = BmsLigado ? bms_potencia : sen_potencia;
PercentualBateria = BmsLigado ? bms_soc : sen_soc; PercentualBateria = BmsLigado ? bms_soc : sen_soc;
Temperatura = BmsLigado ? bms_tmp : sen_tmp;
PercentualBateria = FuncoesMatematicas.Clamp(PercentualBateria, 0, 100); PercentualBateria = FuncoesMatematicas.Clamp(PercentualBateria, 0, 100);
// Verifique se as datas são válidas (não MinValue, MaxValue ou valores anômalos) // Verifique se as datas são válidas (não MinValue, MaxValue ou valores anômalos)

View File

@ -821,6 +821,16 @@ namespace AgroBase.Services.Operadores
("motivo", DadosTrj?.AutonomiaCooredor?.Motivo) ("motivo", DadosTrj?.AutonomiaCooredor?.Motivo)
); );
double distanciaBase = -1;
if (VariaveisOperacao.PosicaoBase?.Inicializado == true && _Sensoriamento?.Gps?.Inicializado == true)
{
distanciaBase = GPSUtils.DistanciaEntrePontos(VariaveisOperacao.PosicaoBase, _Sensoriamento.Gps);
}
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Ipb),
("timestamp", agora),
("iniciado", Variaveis.MqttServiceBase?.StatusConexao() ?? false),
("distancia_base", distanciaBase)
);
} }

View File

@ -470,7 +470,7 @@ class CameraGal:
ipb = ContextoGlobalRedis.get_modulo(T_Code.Ipb) or {} # ajuste se sua key for diferente ipb = ContextoGlobalRedis.get_modulo(T_Code.Ipb) or {} # ajuste se sua key for diferente
saude = (ipb.get("saude") or {}) saude = (ipb.get("saude") or {})
detalhes = (saude.get("detalhes") or {}) detalhes = (saude.get("detalhes") or {})
return float(detalhes.get("bw_util_pct") or 0.0) return float(detalhes.get("bw_pressure_pct") or detalhes.get("bw_util_pct") or 0.0)
def _mode_params(self, mode: int): def _mode_params(self, mode: int):
# mode 0..3 # mode 0..3

View File

@ -797,7 +797,7 @@ class CameraOak:
ipb = ContextoGlobalRedis.get_modulo(T_Code.Ipb) or {} # ajuste se sua key for diferente ipb = ContextoGlobalRedis.get_modulo(T_Code.Ipb) or {} # ajuste se sua key for diferente
saude = (ipb.get("saude") or {}) saude = (ipb.get("saude") or {})
detalhes = (saude.get("detalhes") or {}) detalhes = (saude.get("detalhes") or {})
return float(detalhes.get("bw_util_pct") or 0.0) return float(detalhes.get("bw_pressure_pct") or detalhes.get("bw_util_pct") or 0.0)
def _mode_params(self, mode: int): def _mode_params(self, mode: int):
# mode 0..3 # mode 0..3

View File

@ -1,3 +1,4 @@
import math
import re import re
import time import time
import threading import threading
@ -567,10 +568,92 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
state = "ALTO" if x >= 0.5 else "MEDIO" state = "ALTO" if x >= 0.5 else "MEDIO"
return max(0.0, min(100.0, score)), util_pct, state return max(0.0, min(100.0, score)), util_pct, state
def _bandwidth_pressure_pct(self, bw_util_pct: float, ls: float, tmout: float, los: float, js: float, ns: float, hbs: float):
"""
Pressão real do link para guiar vídeo adaptativo.
0 = folgado
100 = estrangulado
"""
bw_util_pct = max(0.0, min(100.0, float(bw_util_pct or 0.0)))
p_lat = 100.0 - max(0.0, min(100.0, float(ls or 0.0)))
p_tmout = 100.0 - max(0.0, min(100.0, float(tmout or 0.0)))
p_loss = 100.0 - max(0.0, min(100.0, float(los or 0.0)))
p_jit = 100.0 - max(0.0, min(100.0, float(js or 0.0)))
p_nic = 100.0 - max(0.0, min(100.0, float(ns or 0.0)))
p_hb = 100.0 - max(0.0, min(100.0, float(hbs or 0.0)))
pressure_rel = (
0.30 * p_tmout +
0.25 * p_loss +
0.15 * p_hb +
0.15 * p_jit +
0.10 * p_lat +
0.05 * p_nic
)
# gatilhos de proteção
if tmout < 50:
pressure_rel = max(pressure_rel, 85.0)
if los < 50:
pressure_rel = max(pressure_rel, 85.0)
if hbs < 50:
pressure_rel = max(pressure_rel, 80.0)
if js < 30:
pressure_rel = max(pressure_rel, 75.0)
pressure = max(bw_util_pct, pressure_rel)
return round(max(0.0, min(100.0, pressure)), 1)
def _interp_log(self, x, x0, x1, y0, y1):
x = max(x0, min(x, x1))
t = (math.log10(x) - math.log10(x0)) / (math.log10(x1) - math.log10(x0))
return y0 + t * (y1 - y0)
def _ajustar_limites_banda_por_distancia(self, distancia_m: float):
"""
Ajusta bw_max/safe/hard com base na distância da base.
Curva conservadora para Wi-Fi HaLow 915 MHz em campo.
"""
# fallback quando não há posição válida
if distancia_m is None or distancia_m < 0:
self.bw_max_mbps = 3.0
self.bw_safe_mbps = 2.0
self.bw_hard_mbps = 2.5
return
# limita a curva
d = max(10.0, min(float(distancia_m), 1200.0))
# 10 m -> 10 Mbps
# 1200 m -> 0.4 Mbps
bw_max = self._interp_log(d, 10.0, 1200.0, 10.0, 0.4)
# opcional: um pequeno piso e teto
bw_max = max(0.35, min(bw_max, 10.0))
bw_safe = bw_max * 0.68
bw_hard = bw_max * 0.82
# garantias mínimas
bw_safe = max(0.20, bw_safe)
bw_hard = max(bw_safe + 0.05, bw_hard)
self.bw_max_mbps = round(bw_max, 3)
self.bw_safe_mbps = round(bw_safe, 3)
self.bw_hard_mbps = round(bw_hard, 3)
def atualizar_saude_interno(self): def atualizar_saude_interno(self):
#print("atualizando saude IPB...") #print("atualizando saude IPB...")
try: try:
m = ContextoGlobalRedis.get_modulo(self.t_code) or {}
distancia_base = m.get("distancia_base", -1)
self._ajustar_limites_banda_por_distancia(distancia_m=distancia_base)
self._start_mqtt_heartbeat_async() self._start_mqtt_heartbeat_async()
SAUDE_MIN_ALERTA = 80 SAUDE_MIN_ALERTA = 80
@ -688,6 +771,16 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
bw_util_pct = bw_util_pct_s bw_util_pct = bw_util_pct_s
bw_state = bw_state_s bw_state = bw_state_s
bw_pressure_pct = self._bandwidth_pressure_pct(
bw_util_pct=bw_util_pct,
ls=ls,
tmout=tmout,
los=los,
js=js,
ns=ns,
hbs=hbs,
)
# 1) Latência # 1) Latência
cond_lat = [] cond_lat = []
if ls_f < SAUDE_MIN_ALERTA: if ls_f < SAUDE_MIN_ALERTA:
@ -915,6 +1008,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
"saude_individual": saude_individual, "saude_individual": saude_individual,
"condicoes_operacionais": condicoes, "condicoes_operacionais": condicoes,
"detalhes": { "detalhes": {
"d_base": distancia_base,
"ls": ls, "ls": ls,
"tmout": tmout, "tmout": tmout,
"los": los, "los": los,
@ -936,6 +1030,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
"bw_max_mbps": self.bw_max_mbps, "bw_max_mbps": self.bw_max_mbps,
"bw_safe_mbps": self.bw_safe_mbps, "bw_safe_mbps": self.bw_safe_mbps,
"bw_hard_mbps": self.bw_hard_mbps, "bw_hard_mbps": self.bw_hard_mbps,
"bw_pressure_pct": bw_pressure_pct,
} }
} }

View File

@ -41,6 +41,9 @@ namespace OperationControl.Models
try try
{ {
var p = message.Mensagem.Split(','); var p = message.Mensagem.Split(',');
if (string.IsNullOrEmpty(message.Mensagem) || p.Length < 2)
return;
string device_id = p[0]; string device_id = p[0];
string device_ip = p[1]; string device_ip = p[1];
VariaveisControleOperacao.AdicionarNovoRoverNaRede(device_id, device_ip); VariaveisControleOperacao.AdicionarNovoRoverNaRede(device_id, device_ip);

View File

@ -4,10 +4,7 @@ using OperationControl.Controls;
using OperationControl.Models; using OperationControl.Models;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using LibVLCSharp.Shared;
using System.Windows.Input; using System.Windows.Input;
using System.IO;
using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;
using ComboBox = System.Windows.Controls.ComboBox; using ComboBox = System.Windows.Controls.ComboBox;
using Application = System.Windows.Application; using Application = System.Windows.Application;
using MessageBox = System.Windows.MessageBox; using MessageBox = System.Windows.MessageBox;
@ -27,11 +24,6 @@ namespace OperationControl.Windows
/// </summary> /// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged public partial class MainWindow : Window, INotifyPropertyChanged
{ {
//private LibVLC _libVLC;
//private MediaPlayer _playerFront;
//private MediaPlayer _playerWeed;
//private Media _mediaFront;
//private Media _mediaWeed;
private TcpVideoReceiver videoFront; private TcpVideoReceiver videoFront;
private TcpVideoReceiver videoWeed; private TcpVideoReceiver videoWeed;
@ -72,30 +64,6 @@ namespace OperationControl.Windows
DataContext = this; DataContext = this;
//DataContext = this;
// atualizar as caixas
//BBoxes3D.SetBboxes(new[]
//{
// new AgroBase.Models.LivoxBboxModel{ cx=1.2, cy=0.5, cz=0.4, d=0.6, w=0.4, h=0.8 },
// new AgroBase.Models.LivoxBboxModel{ cx=3.0, cy=-0.7, cz=0.6, d=0.8, w=0.5, h=1.2 },
//});
// Opções focadas em baixa latência
//_libVLC = new LibVLC(
// "--network-caching=1000", // ms (ajusta depois)
// "--clock-jitter=0",
// "--clock-synchro=0"
//);
// Player da câmera frontal
//_playerFront = new MediaPlayer(_libVLC);
//VideoFront.MediaPlayer = _playerFront;
//_playerWeed = new MediaPlayer(_libVLC);
//VideoWeed.MediaPlayer = _playerWeed;
videoFront = new TcpVideoReceiver(5000, imgVideoFront); videoFront = new TcpVideoReceiver(5000, imgVideoFront);
videoWeed = new TcpVideoReceiver(5002, imgVideoWeed); videoWeed = new TcpVideoReceiver(5002, imgVideoWeed);
@ -124,7 +92,6 @@ namespace OperationControl.Windows
{ {
PararStreamCameraFrontal(true); PararStreamCameraFrontal(true);
PararStreamCameraErvas(true); PararStreamCameraErvas(true);
//_libVLC?.Dispose();
} }
#region DISPOSITIVOS #region DISPOSITIVOS
@ -1052,12 +1019,6 @@ namespace OperationControl.Windows
Valor = perda Valor = perda
}, },
new GraficoModel.GraficoParametrosModel() new GraficoModel.GraficoParametrosModel()
{
Label = "Erro",
Unidade = "%",
Valor = errRate
},
new GraficoModel.GraficoParametrosModel()
{ {
Label = "Banda", Label = "Banda",
Unidade = "Mbps", Unidade = "Mbps",
@ -1101,6 +1062,30 @@ namespace OperationControl.Windows
}, },
}); });
break; break;
case AgroBase.Models.Enums.T_Code.Bat:
var bat = VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Bateria;
graficoAcompanhamento.Atualizar(inicial, momento, new List<GraficoModel.GraficoParametrosModel>()
{
new GraficoModel.GraficoParametrosModel()
{
Label = "Tensão",
Unidade = "V",
Valor = bat?.TensaoInstantanea
},
new GraficoModel.GraficoParametrosModel()
{
Label = "Corrente",
Unidade = "A",
Valor = bat?.CorrenteInstantanea
},
new GraficoModel.GraficoParametrosModel()
{
Label = "Temperatura",
Unidade = "°C",
Valor = bat?.Temperatura
},
});
break;
} }
graficoAcompanhamento.Limpar(momento); graficoAcompanhamento.Limpar(momento);
@ -1958,39 +1943,12 @@ namespace OperationControl.Windows
private void IniciarStreamCameraFrontal() private void IniciarStreamCameraFrontal()
{ {
//var path = @"streams\camera_frontal.sdp";
//if (!File.Exists(path))
//{
// string stream =
// "v=0\r\n" +
// "o=- 0 0 IN IP4 0.0.0.0\r\n" +
// "s=OAK H264\r\n" +
// "c=IN IP4 0.0.0.0\r\n" +
// "t=0 0\r\n" +
// "m=video 5000 RTP/AVP 96\r\n" +
// "a=rtpmap:96 H264/90000\r\n" +
// "a=fmtp:96 packetization-mode=1\r\n";
// File.WriteAllText(path, stream);
//}
//_mediaFront?.Dispose(); // limpa antigo, se existir
//_mediaFront = new Media(_libVLC, path, FromType.FromPath);
//_playerFront.Play(_mediaFront);
videoFront.Iniciar(); videoFront.Iniciar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex)); VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex));
} }
private void PararStreamCameraFrontal(bool finalizar = false) private void PararStreamCameraFrontal(bool finalizar = false)
{ {
//_playerFront?.Stop();
//if (finalizar)
//{
// _mediaFront?.Dispose();
// _mediaFront = null;
// _playerFront?.Dispose();
// _playerFront = null;
//}
videoFront.Parar(); videoFront.Parar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex)); VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex));
} }
@ -2008,40 +1966,12 @@ namespace OperationControl.Windows
private void IniciarStreamCameraErvas() private void IniciarStreamCameraErvas()
{ {
//var path = @"streams\camera_ervas.sdp";
//if (!File.Exists(path))
//{
// string stream =
// "v=0\r\n" +
// "o=- 0 0 IN IP4 0.0.0.0\r\n" +
// "s=OAK H264\r\n" +
// "c=IN IP4 0.0.0.0\r\n" +
// "t=0 0\r\n" +
// "m=video 5002 RTP/AVP 96\r\n" + // <- porta 5001
// "a=rtpmap:96 H264/90000\r\n" +
// "a=fmtp:96 packetization-mode=1\r\n";
// File.WriteAllText(path, stream);
//}
//_mediaWeed?.Dispose();
//_mediaWeed = new Media(_libVLC, path, FromType.FromPath);
//_mediaWeed.AddOption(":network-caching=150");
//_playerWeed.Play(_mediaWeed);
videoWeed.Iniciar(); videoWeed.Iniciar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex)); VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex));
} }
private void PararStreamCameraErvas(bool finalizar = false) private void PararStreamCameraErvas(bool finalizar = false)
{ {
//_playerWeed?.Stop();
//if (finalizar)
//{
// _mediaWeed?.Dispose();
// _mediaWeed = null;
// _playerWeed?.Dispose();
// _playerWeed = null;
//}
videoWeed.Parar(); videoWeed.Parar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex)); VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex));
} }