Ajustes de conectividade mqtt
This commit is contained in:
parent
ca86e1b1e2
commit
8ee9368c8b
|
|
@ -172,7 +172,7 @@ namespace AgroBase
|
||||||
|
|
||||||
tkbVelocidade.Minimum = 0;
|
tkbVelocidade.Minimum = 0;
|
||||||
tkbVelocidade.Maximum = 100;
|
tkbVelocidade.Maximum = 100;
|
||||||
FuncoesGlobais.PreencheValorTrackBar(tkbVelocidade, Variaveis.OperacaoEmAndamento.Parametros.Controle.DirVelocidadeMovimento);
|
FuncoesGlobais.PreencheValorTrackBar(tkbVelocidade, Variaveis.OperacaoEmAndamento.Parametros.Controle?.DirVelocidadeMovimento ?? 0);
|
||||||
|
|
||||||
tkbAngulo.Minimum = 0;
|
tkbAngulo.Minimum = 0;
|
||||||
tkbAngulo.Maximum = 90;
|
tkbAngulo.Maximum = 90;
|
||||||
|
|
|
||||||
|
|
@ -612,7 +612,7 @@ namespace AgroBase
|
||||||
_Controle.Angulo = solto ? 0 : pControle.DirAnguloMaximo * percent;
|
_Controle.Angulo = solto ? 0 : pControle.DirAnguloMaximo * percent;
|
||||||
break;
|
break;
|
||||||
case T_Code.Mov:
|
case T_Code.Mov:
|
||||||
_Controle.PercentualVelocidadeSP = solto ? 0 : pControle.MovVelocidadeSErvasPercent * percent;
|
_Controle.PercentualVelocidadeSP = solto ? 0 : Math.Max(pControle.MovVelocidadeCErvasPercent, pControle.MovVelocidadeSErvasPercent * percent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -338,10 +338,12 @@ namespace AgroBase.Models
|
||||||
OperacaoParametrosModel p = new OperacaoParametrosModel()
|
OperacaoParametrosModel p = new OperacaoParametrosModel()
|
||||||
{
|
{
|
||||||
Modo = Modo,
|
Modo = Modo,
|
||||||
|
Descricao = $"{Modo} - Padrão",
|
||||||
QtdCamerasSolo = VariaveisEquipamento.QuantidadeCamerasSolo,
|
QtdCamerasSolo = VariaveisEquipamento.QuantidadeCamerasSolo,
|
||||||
QtdBicos = VariaveisEquipamento.QuantidadeBicosPulverizadores,
|
QtdBicos = VariaveisEquipamento.QuantidadeBicosPulverizadores,
|
||||||
CapacidadeReservatorio = VariaveisEquipamento.CapacidadeReservatorio,
|
CapacidadeReservatorio = VariaveisEquipamento.CapacidadeReservatorio,
|
||||||
RoverId = VariaveisEquipamento.Parametros.serial_number,
|
RoverId = VariaveisEquipamento.Parametros.serial_number,
|
||||||
|
IP = VariaveisEquipamento.Parametros.rover_ip,
|
||||||
RuasPercorrer = null,
|
RuasPercorrer = null,
|
||||||
Mapa = null,
|
Mapa = null,
|
||||||
DadosLeitura = new OperacaoParametrosDadosModel(),
|
DadosLeitura = new OperacaoParametrosDadosModel(),
|
||||||
|
|
@ -382,7 +384,7 @@ namespace AgroBase.Models
|
||||||
MpcMatrizCusto = false,
|
MpcMatrizCusto = false,
|
||||||
MpcHorizonte = 4.0,
|
MpcHorizonte = 4.0,
|
||||||
|
|
||||||
MovVelocidadeSErvasPercent = 100,
|
MovVelocidadeSErvasPercent = 50,
|
||||||
MovVelocidadeCErvasPercent = 15,
|
MovVelocidadeCErvasPercent = 15,
|
||||||
DirAnguloMaximo = 25,
|
DirAnguloMaximo = 25,
|
||||||
DirVelocidadeMovimento = 40,
|
DirVelocidadeMovimento = 40,
|
||||||
|
|
@ -832,6 +834,7 @@ namespace AgroBase.Models
|
||||||
if (isagrobase)
|
if (isagrobase)
|
||||||
{
|
{
|
||||||
Variaveis.OperacaoEmAndamento.Parametros = p;
|
Variaveis.OperacaoEmAndamento.Parametros = p;
|
||||||
|
Variaveis.OperacaoEmAndamento.DefinirComponentesEmUso();
|
||||||
Variaveis.OperacaoEmAndamento.Trajetoria = new TrajetoriaMapaOperacaoModel(new List<List<GPSModel>>());
|
Variaveis.OperacaoEmAndamento.Trajetoria = new TrajetoriaMapaOperacaoModel(new List<List<GPSModel>>());
|
||||||
Variaveis.OperacaoEmAndamento.Sensoriamento = new OperacaoSensoriamentoLogModel();
|
Variaveis.OperacaoEmAndamento.Sensoriamento = new OperacaoSensoriamentoLogModel();
|
||||||
RedisService.AtualizarCampos(CtxKey.DadosOperacao, ("configurado", false));
|
RedisService.AtualizarCampos(CtxKey.DadosOperacao, ("configurado", false));
|
||||||
|
|
@ -891,36 +894,38 @@ namespace AgroBase.Models
|
||||||
case T_Code.Mov:
|
case T_Code.Mov:
|
||||||
foreach (var c in m.ComponentesEmUso)
|
foreach (var c in m.ComponentesEmUso)
|
||||||
{
|
{
|
||||||
var d = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => x.MovMotor.Mod_ID == c.Key).MovMotor;
|
var d = Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.FirstOrDefault(x => x?.MovMotor?.Mod_ID == c.Key)?.MovMotor;
|
||||||
|
if (d != null)
|
||||||
d.Comandar = c.Value.Item2;
|
d.Comandar = c.Value.Item2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_Code.Dir:
|
case T_Code.Dir:
|
||||||
foreach (var c in m.ComponentesEmUso)
|
foreach (var c in m.ComponentesEmUso)
|
||||||
{
|
{
|
||||||
var d = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => x.DirMotor.Mod_ID == c.Key).DirMotor;
|
var d = Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.FirstOrDefault(x => x?.DirMotor?.Mod_ID == c.Key)?.DirMotor;
|
||||||
|
if (d != null)
|
||||||
d.Comandar = c.Value.Item2;
|
d.Comandar = c.Value.Item2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_Code.Atu:
|
case T_Code.Atu:
|
||||||
var da = Variaveis.OperacaoEmAndamento.DispAtu.Dados;
|
var da = Variaveis.OperacaoEmAndamento.DispAtu?.Dados;
|
||||||
foreach (var c in m.ComponentesEmUso)
|
foreach (var c in m.ComponentesEmUso)
|
||||||
{
|
{
|
||||||
var s = da.Sensores.FirstOrDefault(x => x.ID == c.Key);
|
var s = da?.Sensores?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (s != null)
|
if (s != null)
|
||||||
{
|
{
|
||||||
s.Mandatorio = c.Value.Item1;
|
s.Mandatorio = c.Value.Item1;
|
||||||
s.Aferir = c.Value.Item2;
|
s.Aferir = c.Value.Item2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var bc = da.BicosPulverizadores.FirstOrDefault(x => x.ID == c.Key);
|
var bc = da?.BicosPulverizadores?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (bc != null)
|
if (bc != null)
|
||||||
{
|
{
|
||||||
bc.Mandatorio = c.Value.Item1;
|
bc.Mandatorio = c.Value.Item1;
|
||||||
bc.Comandar = c.Value.Item2;
|
bc.Comandar = c.Value.Item2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var bo = da.BombasPressurizadoras.FirstOrDefault(x => x.ID == c.Key);
|
var bo = da?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (bo != null)
|
if (bo != null)
|
||||||
{
|
{
|
||||||
bo.Mandatorio = c.Value.Item1;
|
bo.Mandatorio = c.Value.Item1;
|
||||||
|
|
@ -930,31 +935,31 @@ namespace AgroBase.Models
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_Code.Sen:
|
case T_Code.Sen:
|
||||||
var ds = Variaveis.OperacaoEmAndamento.DispSen.Dados;
|
var ds = Variaveis.OperacaoEmAndamento.DispSen?.Dados;
|
||||||
foreach (var c in m.ComponentesEmUso)
|
foreach (var c in m.ComponentesEmUso)
|
||||||
{
|
{
|
||||||
var s = ds.Sensores.FirstOrDefault(x => x.ID == c.Key);
|
var s = ds?.Sensores?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (s != null)
|
if (s != null)
|
||||||
{
|
{
|
||||||
s.Mandatorio = c.Value.Item1;
|
s.Mandatorio = c.Value.Item1;
|
||||||
s.Aferir = c.Value.Item2;
|
s.Aferir = c.Value.Item2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var sv = ds.Servos.FirstOrDefault(x => x.ID == c.Key);
|
var sv = ds?.Servos?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (sv != null)
|
if (sv != null)
|
||||||
{
|
{
|
||||||
sv.Mandatorio = c.Value.Item1;
|
sv.Mandatorio = c.Value.Item1;
|
||||||
sv.Controlar = c.Value.Item2;
|
sv.Controlar = c.Value.Item2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var rl = ds.Reles.FirstOrDefault(x => x.ID == c.Key);
|
var rl = ds?.Reles?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (rl != null)
|
if (rl != null)
|
||||||
{
|
{
|
||||||
rl.Mandatorio = c.Value.Item1;
|
rl.Mandatorio = c.Value.Item1;
|
||||||
rl.Controlar = c.Value.Item2;
|
rl.Controlar = c.Value.Item2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var ld = ds.Sinaleiros.FirstOrDefault(x => x.ID == c.Key);
|
var ld = ds?.Sinaleiros?.FirstOrDefault(x => x.ID == c.Key);
|
||||||
if (ld != null)
|
if (ld != null)
|
||||||
{
|
{
|
||||||
ld.Mandatorio = c.Value.Item1;
|
ld.Mandatorio = c.Value.Item1;
|
||||||
|
|
@ -973,15 +978,17 @@ namespace AgroBase.Models
|
||||||
|
|
||||||
CarregarParametrosOperacaoPadrao((ModoOperacao)Parametros.Modo, true);
|
CarregarParametrosOperacaoPadrao((ModoOperacao)Parametros.Modo, true);
|
||||||
|
|
||||||
Variaveis.OperacaoEmAndamento.Parametros = Parametros;
|
|
||||||
Variaveis.OperacaoEmAndamento.Parametros.RoverId = VariaveisEquipamento.Parametros.serial_number;
|
|
||||||
Variaveis.OperacaoEmAndamento.Parametros.DadosLeitura = new OperacaoParametrosDadosModel();
|
|
||||||
Variaveis.OperacaoEmAndamento.Sensoriamento.AtualizarDados();
|
|
||||||
Variaveis.OperacaoEmAndamento.Parametros.ModulosMandatorios.ForEach(m => m.ComponentesEmUso = Variaveis.OperacaoEmAndamento.PreencherComponentesEmUso(m.Dispositivo));
|
|
||||||
Variaveis.OperacaoEmAndamento.DefinirComponentesEmUso();
|
|
||||||
|
|
||||||
Variaveis.OperacaoEmAndamento.Descricao = Parametros.Descricao;
|
Variaveis.OperacaoEmAndamento.Descricao = Parametros.Descricao;
|
||||||
Variaveis.OperacaoEmAndamento.OprPath = $"Base ({VariaveisEquipamento.Parametros.base_ip})";
|
Variaveis.OperacaoEmAndamento.OprPath = $"Base ({VariaveisEquipamento.Parametros.base_ip})";
|
||||||
|
var p = Variaveis.OperacaoEmAndamento.Parametros;
|
||||||
|
p.Descricao = Parametros.Descricao;
|
||||||
|
p.CapacidadeReservatorio = Parametros.CapacidadeReservatorio;
|
||||||
|
p.Controle = Parametros.Controle;
|
||||||
|
p.QtdBicos = Parametros.QtdBicos;
|
||||||
|
p.QtdCamerasSolo = Parametros.QtdCamerasSolo;
|
||||||
|
p.RuasPercorrer = Parametros.RuasPercorrer;
|
||||||
|
p.ModulosMandatorios = Parametros.ModulosMandatorios;
|
||||||
|
p.ParametrosMandatorios = Parametros.ParametrosMandatorios;
|
||||||
|
|
||||||
List<string> ruasSelecionadas = Parametros.RuasPercorrer ?? new List<string>();
|
List<string> ruasSelecionadas = Parametros.RuasPercorrer ?? new List<string>();
|
||||||
if (Parametros.Mapa != null)
|
if (Parametros.Mapa != null)
|
||||||
|
|
@ -1491,7 +1498,8 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
case BotoesJoystick.Share: // Requisitar parametros da operacao
|
case BotoesJoystick.Share: // Requisitar parametros da operacao
|
||||||
var topico = Variaveis.MqttServiceBase.Topicos.FirstOrDefault(x => x.Topico == VariaveisEquipamento.TopicoMqttParametros.Replace("<id>", VariaveisEquipamento.Parametros.serial_number));
|
var topico = Variaveis.MqttServiceBase.Topicos.FirstOrDefault(x => x.Topico == VariaveisEquipamento.TopicoMqttParametros.Replace("<id>", VariaveisEquipamento.Parametros.serial_number));
|
||||||
string msg = JsonConvert.SerializeObject(Variaveis.OperacaoEmAndamento.Parametros);
|
var p = Variaveis.OperacaoEmAndamento.Parametros;
|
||||||
|
string msg = JsonConvert.SerializeObject(p);
|
||||||
Task.Run(async () => await Variaveis.MqttServiceBase.PublishAsync(topico, msg));
|
Task.Run(async () => await Variaveis.MqttServiceBase.PublishAsync(topico, msg));
|
||||||
break;
|
break;
|
||||||
case BotoesJoystick.Options: // Operacao nao iniciada, carrega todos os parametros compeltos
|
case BotoesJoystick.Options: // Operacao nao iniciada, carrega todos os parametros compeltos
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace AgroBase.Models.Operadores
|
||||||
public void AtualizarStatusBloqueio(bool consideraEmMovimento)
|
public void AtualizarStatusBloqueio(bool consideraEmMovimento)
|
||||||
{
|
{
|
||||||
var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
|
var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
|
||||||
ObstaculoDetectado = (Sonar.Analises?.matriz_confianca?.block?.decision?.parar ?? false);
|
ObstaculoDetectado = (Sonar?.Analises?.matriz_confianca?.block?.decision?.parar ?? false);
|
||||||
StatusCarro = Sonar?.Analises?.segmentacao?.status_corredor ?? StatusCarroMapa.Indefinido;
|
StatusCarro = Sonar?.Analises?.segmentacao?.status_corredor ?? StatusCarroMapa.Indefinido;
|
||||||
ErroLateral = Sonar?.Analises?.segmentacao?.erro_angular ?? 0;
|
ErroLateral = Sonar?.Analises?.segmentacao?.erro_angular ?? 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,17 +49,24 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
self.last_heartbeat_ts = time.time()
|
self.last_heartbeat_ts = time.time()
|
||||||
|
|
||||||
self.rover_id = None
|
self.rover_id = None
|
||||||
self.sub = False
|
|
||||||
|
|
||||||
self._mqtt = None
|
self._mqtt = None
|
||||||
self._mqtt_conectado = False
|
|
||||||
self._mqtt_executor = ThreadPoolExecutor(max_workers=1)
|
|
||||||
self._mqtt_future = None
|
|
||||||
self._mqtt_lock = threading.Lock()
|
self._mqtt_lock = threading.Lock()
|
||||||
self._mqtt_last_attempt_ts = 0.0
|
self._mqtt_conectado = False
|
||||||
self._mqtt_backoff_s = 1.0 # começa rápido
|
|
||||||
self._mqtt_backoff_max_s = 15.0 # limite
|
|
||||||
self._mqtt_connecting = False
|
self._mqtt_connecting = False
|
||||||
|
self._heartbeat_ok = False
|
||||||
|
self._mqtt_last_attempt_ts = 0.0
|
||||||
|
self._mqtt_backoff_s = 1.0
|
||||||
|
self._mqtt_backoff_max_s = 30.0
|
||||||
|
self._mqtt_connected_ts = 0.0
|
||||||
|
self.last_heartbeat_ts = 0.0
|
||||||
|
self._heartbeat_timeout_s = 10.0
|
||||||
|
self._heartbeat_grace_after_connect_s = 15.0
|
||||||
|
self.sub = False
|
||||||
|
self._mqtt_future = None
|
||||||
|
self._mqtt_executor = ThreadPoolExecutor(max_workers=1)
|
||||||
|
self._mqtt_client_seq = 0
|
||||||
|
self._mqtt_debug = False
|
||||||
|
|
||||||
self._ping_executor = ThreadPoolExecutor(max_workers=1)
|
self._ping_executor = ThreadPoolExecutor(max_workers=1)
|
||||||
self._ping_future = None
|
self._ping_future = None
|
||||||
|
|
@ -152,128 +159,208 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
finally:
|
finally:
|
||||||
self._running = False
|
self._running = False
|
||||||
|
|
||||||
def _start_mqtt_heartbeat(self):
|
|
||||||
try:
|
# MQTT HEARTBEAT
|
||||||
if self._mqtt_conectado:
|
def _mqtt_mostra_log(self, msg):
|
||||||
|
if self._mqtt_debug:
|
||||||
|
print(f"[HEARTBEAT] {msg}")
|
||||||
|
|
||||||
|
def _check_mqtt_heartbeat(self):
|
||||||
|
now = time.time()
|
||||||
|
client_to_close = None
|
||||||
|
|
||||||
|
with self._mqtt_lock:
|
||||||
|
if not self._mqtt_conectado:
|
||||||
return
|
return
|
||||||
base_ip = self.get_base_ip()
|
|
||||||
if base_ip is None:
|
connected_ts = self._mqtt_connected_ts
|
||||||
|
last_hb = self.last_heartbeat_ts
|
||||||
|
|
||||||
|
# janela de graça após reconnect
|
||||||
|
if (now - connected_ts) < self._heartbeat_grace_after_connect_s:
|
||||||
|
return
|
||||||
|
|
||||||
|
atraso_s = now - last_hb
|
||||||
|
|
||||||
|
if atraso_s >= self._heartbeat_timeout_s:
|
||||||
|
self._mqtt_mostra_log(f"atraso de {atraso_s:.1f}s detectado")
|
||||||
|
|
||||||
|
with self._mqtt_lock:
|
||||||
|
self._heartbeat_ok = False
|
||||||
|
client_to_close = self._mqtt_detach_client_locked("heartbeat atrasado")
|
||||||
|
|
||||||
|
self._mqtt_close_client(client_to_close)
|
||||||
|
|
||||||
|
def _is_active_client_locked(self, client):
|
||||||
|
return self._mqtt is client
|
||||||
|
|
||||||
|
def _mqtt_detach_client_locked(self, reason=""):
|
||||||
|
client = self._mqtt
|
||||||
|
self._mqtt = None
|
||||||
self._mqtt_conectado = False
|
self._mqtt_conectado = False
|
||||||
return
|
self._mqtt_connecting = False
|
||||||
self._mqtt = mqtt.Client()
|
self._heartbeat_ok = False
|
||||||
self._mqtt.on_connect = self._on_connect
|
self.sub = False
|
||||||
self._mqtt.on_message = self._on_message
|
self._mqtt_last_attempt_ts = time.time()
|
||||||
self._mqtt.on_disconnect = self._on_disconnect
|
self._mqtt_backoff_s = max(self._mqtt_backoff_s, 5.0)
|
||||||
self._mqtt.connect(base_ip, 1883, 60)
|
|
||||||
self._mqtt.loop_start()
|
if reason:
|
||||||
except:
|
self._mqtt_mostra_log(f"Resetando client MQTT: {reason}")
|
||||||
#pass
|
|
||||||
print("Erro ao se conectar no broker mqtt")
|
return client
|
||||||
|
|
||||||
|
def _mqtt_close_client(self, client):
|
||||||
|
if client is not None:
|
||||||
|
cid = getattr(client, "_agro_client_local_id", "?")
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] fechando client antigo")
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.loop_stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
client.disconnect()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _start_mqtt_heartbeat_async(self):
|
def _start_mqtt_heartbeat_async(self):
|
||||||
"""Dispara tentativa de conexão MQTT sem bloquear o loop."""
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
|
||||||
with self._mqtt_lock:
|
with self._mqtt_lock:
|
||||||
if self._mqtt_conectado:
|
if self._mqtt_conectado:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._mqtt_connecting:
|
if self._mqtt_connecting:
|
||||||
return
|
return
|
||||||
|
|
||||||
# respeita backoff
|
if self._mqtt_future is not None and not self._mqtt_future.done():
|
||||||
|
return
|
||||||
|
|
||||||
if (now - self._mqtt_last_attempt_ts) < self._mqtt_backoff_s:
|
if (now - self._mqtt_last_attempt_ts) < self._mqtt_backoff_s:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
base_ip = self.get_base_ip()
|
||||||
|
if not base_ip:
|
||||||
|
self._mqtt_mostra_log("mqtt heartbeat sem ip base definido")
|
||||||
|
return
|
||||||
|
|
||||||
self._mqtt_last_attempt_ts = now
|
self._mqtt_last_attempt_ts = now
|
||||||
self._mqtt_connecting = True
|
self._mqtt_connecting = True
|
||||||
|
|
||||||
base_ip = self.get_base_ip()
|
self._mqtt_mostra_log("tentando conectar mqtt heartbeat")
|
||||||
if base_ip is None:
|
|
||||||
with self._mqtt_lock:
|
|
||||||
self._mqtt_connecting = False
|
|
||||||
return
|
|
||||||
|
|
||||||
# se já tem uma future rodando, não dispara outra
|
|
||||||
if self._mqtt_future is not None and not self._mqtt_future.done():
|
|
||||||
return
|
|
||||||
|
|
||||||
self._mqtt_future = self._mqtt_executor.submit(self._mqtt_connect_worker, base_ip)
|
self._mqtt_future = self._mqtt_executor.submit(self._mqtt_connect_worker, base_ip)
|
||||||
|
|
||||||
def _mqtt_connect_worker(self, base_ip: str):
|
def _mqtt_connect_worker(self, base_ip: str):
|
||||||
"""Roda em background. Pode bloquear aqui sem travar o health."""
|
client = None
|
||||||
try:
|
try:
|
||||||
client = mqtt.Client()
|
client = mqtt.Client()
|
||||||
|
with self._mqtt_lock:
|
||||||
|
self._mqtt_client_seq += 1
|
||||||
|
cid = self._mqtt_client_seq
|
||||||
|
|
||||||
|
client._agro_client_local_id = cid
|
||||||
client.on_connect = self._on_connect
|
client.on_connect = self._on_connect
|
||||||
client.on_message = self._on_message
|
client.on_message = self._on_message
|
||||||
client.on_disconnect = self._on_disconnect
|
client.on_disconnect = self._on_disconnect
|
||||||
|
|
||||||
# dica V1: keepalive menor ajuda a detectar quedas mais rápido
|
client.connect(base_ip, 1883, 15)
|
||||||
keepalive = 15
|
|
||||||
|
|
||||||
# conecta (bloqueante)
|
|
||||||
client.connect(base_ip, 1883, keepalive)
|
|
||||||
|
|
||||||
# se chegou aqui, iniciou. loop_start é não-bloqueante
|
|
||||||
client.loop_start()
|
client.loop_start()
|
||||||
|
|
||||||
# troca o client com lock (evita race)
|
|
||||||
with self._mqtt_lock:
|
with self._mqtt_lock:
|
||||||
# se tinha um client antigo, tenta parar
|
# só assume como ativo depois de iniciar sem exception
|
||||||
if self._mqtt is not None:
|
old = self._mqtt
|
||||||
|
self._mqtt = client
|
||||||
|
|
||||||
|
if old is not None and old is not client:
|
||||||
try:
|
try:
|
||||||
self._mqtt.loop_stop()
|
old.loop_stop()
|
||||||
self._mqtt.disconnect()
|
except Exception:
|
||||||
except:
|
pass
|
||||||
|
try:
|
||||||
|
old.disconnect()
|
||||||
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self._mqtt = client
|
self._mqtt_mostra_log(f"mqtt heartbeat conectado [client={cid}]")
|
||||||
# on_connect vai setar _mqtt_conectado quando confirmar
|
|
||||||
|
|
||||||
except Exception:
|
except Exception as e:
|
||||||
# falhou: libera pra tentar de novo com backoff maior
|
|
||||||
with self._mqtt_lock:
|
with self._mqtt_lock:
|
||||||
self._mqtt_conectado = False
|
|
||||||
self._mqtt_connecting = False
|
self._mqtt_connecting = False
|
||||||
self._mqtt_backoff_s = min(self._mqtt_backoff_s * 2.0, self._mqtt_backoff_max_s)
|
self._mqtt_conectado = False
|
||||||
|
self._heartbeat_ok = False
|
||||||
|
self.sub = False
|
||||||
|
self._mqtt_last_attempt_ts = time.time()
|
||||||
|
self._mqtt_backoff_s = min(max(self._mqtt_backoff_s * 2.0, 5.0), self._mqtt_backoff_max_s)
|
||||||
|
|
||||||
|
self._mqtt_mostra_log(f"mqtt heartbeat erro ao conectar [client={getattr(client, '_agro_client_local_id', '?')}]: {e}")
|
||||||
|
|
||||||
def _on_connect(self, client, userdata, flags, rc):
|
def _on_connect(self, client, userdata, flags, rc):
|
||||||
|
cid = getattr(client, "_agro_client_local_id", "?")
|
||||||
|
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] on_connect rc={rc} | t={time.time():.3f}")
|
||||||
|
|
||||||
with self._mqtt_lock:
|
with self._mqtt_lock:
|
||||||
self._mqtt_conectado = (rc == 0)
|
if not self._is_active_client_locked(client):
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] on_connect IGNORADO (client antigo)")
|
||||||
|
return
|
||||||
|
|
||||||
self._mqtt_connecting = False
|
self._mqtt_connecting = False
|
||||||
# reset backoff quando conecta
|
|
||||||
if self._mqtt_conectado:
|
if rc == 0:
|
||||||
|
self._mqtt_conectado = True
|
||||||
|
self._heartbeat_ok = False
|
||||||
self._mqtt_backoff_s = 1.0
|
self._mqtt_backoff_s = 1.0
|
||||||
self._reset_janelas()
|
self._mqtt_connected_ts = time.time()
|
||||||
|
self.last_heartbeat_ts = self._mqtt_connected_ts
|
||||||
|
|
||||||
self.rover_id = ContextoGlobalRedis.get_equipamento().get("serial_number")
|
self.rover_id = ContextoGlobalRedis.get_equipamento().get("serial_number")
|
||||||
if self.sub == False:
|
|
||||||
topic = f"agrobot/v1/rover/{self.rover_id}/heartbeat"
|
topic = f"agrobot/v1/rover/{self.rover_id}/heartbeat"
|
||||||
client.subscribe(topic)
|
|
||||||
print("[HEARTBEAT] Subscribado em:", topic)
|
result, mid = client.subscribe(topic)
|
||||||
self.sub = True
|
self.sub = (result == 0)
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] Subscribado em: {topic} | result={result} mid={mid}")
|
||||||
else:
|
else:
|
||||||
print("Erro ao conectar MQTT:", rc)
|
self._mqtt_conectado = False
|
||||||
|
self._heartbeat_ok = False
|
||||||
|
self.sub = False
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] Erro ao conectar MQTT: rc={rc}")
|
||||||
|
|
||||||
def _on_disconnect(self, client, userdata, rc):
|
def _on_disconnect(self, client, userdata, rc):
|
||||||
|
cid = getattr(client, "_agro_client_local_id", "?")
|
||||||
|
|
||||||
with self._mqtt_lock:
|
with self._mqtt_lock:
|
||||||
|
if not self._is_active_client_locked(client):
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] on_disconnect IGNORADO (client antigo) rc={rc}")
|
||||||
|
return
|
||||||
|
|
||||||
self._mqtt_conectado = False
|
self._mqtt_conectado = False
|
||||||
self._mqtt_connecting = False
|
self._mqtt_connecting = False
|
||||||
|
self._heartbeat_ok = False
|
||||||
self.sub = False
|
self.sub = False
|
||||||
print("[HEARTBEAT] MQTT desconectado!", rc)
|
self._mqtt = None
|
||||||
|
|
||||||
# se rc != 0 significa desconexão inesperada
|
# adiciona isso:
|
||||||
if rc != 0:
|
self._mqtt_last_attempt_ts = time.time()
|
||||||
print("[HEARTBEAT] Desconexão inesperada — pode ter perdido o link 900 MHz")
|
self._mqtt_backoff_s = max(self._mqtt_backoff_s, 5.0)
|
||||||
|
|
||||||
# Marca como "sem heartbeat" instantâneo (opcional)
|
self._mqtt_mostra_log(f"[client={cid}] MQTT desconectado! rc={rc} | t={time.time():.3f}")
|
||||||
# Aqui podemos forçar atraso grande
|
|
||||||
self.last_heartbeat_ts = 0
|
|
||||||
|
|
||||||
def _on_message(self, client, userdata, msg):
|
def _on_message(self, client, userdata, msg):
|
||||||
topic = msg.topic
|
cid = getattr(client, "_agro_client_local_id", "?")
|
||||||
if topic == f"agrobot/v1/rover/{self.rover_id}/heartbeat":
|
|
||||||
self.last_heartbeat_ts = time.time()
|
with self._mqtt_lock:
|
||||||
#print("[HEARTBEAT] recebido")
|
if not self._is_active_client_locked(client):
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] on_message IGNORADO (client antigo)")
|
||||||
|
return
|
||||||
|
|
||||||
|
if msg.topic == f"agrobot/v1/rover/{self.rover_id}/heartbeat":
|
||||||
|
now = time.time()
|
||||||
|
with self._mqtt_lock:
|
||||||
|
self.last_heartbeat_ts = now
|
||||||
|
self._heartbeat_ok = True
|
||||||
|
|
||||||
|
self._mqtt_mostra_log(f"[client={cid}] heartbeat recebido | t={now:.3f}")
|
||||||
|
|
||||||
|
|
||||||
def _reset_janelas(self):
|
def _reset_janelas(self):
|
||||||
for d in (
|
for d in (
|
||||||
|
|
@ -654,6 +741,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
distancia_base = m.get("distancia_base", -1)
|
distancia_base = m.get("distancia_base", -1)
|
||||||
self._ajustar_limites_banda_por_distancia(distancia_m=distancia_base)
|
self._ajustar_limites_banda_por_distancia(distancia_m=distancia_base)
|
||||||
|
|
||||||
|
self._check_mqtt_heartbeat()
|
||||||
self._start_mqtt_heartbeat_async()
|
self._start_mqtt_heartbeat_async()
|
||||||
|
|
||||||
SAUDE_MIN_ALERTA = 80
|
SAUDE_MIN_ALERTA = 80
|
||||||
|
|
|
||||||
|
|
@ -865,10 +865,10 @@ namespace OperationControl.Controls
|
||||||
return dict.TryGetValue(fieldName, out var v) ? v : null;
|
return dict.TryGetValue(fieldName, out var v) ? v : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgroBase.Models.MapaFeatureCollectionModel CriarDadosMapa()
|
public AgroBase.Models.MapaFeatureCollectionModel CriarDadosMapa(bool selecionadas = false)
|
||||||
{
|
{
|
||||||
AgroBase.Models.MapaFeatureCollectionModel _mapa = new AgroBase.Models.MapaFeatureCollectionModel() { type = "FeatureCollection", features = new List<AgroBase.Models.MapaFeatureModel>() };
|
AgroBase.Models.MapaFeatureCollectionModel _mapa = new AgroBase.Models.MapaFeatureCollectionModel() { type = "FeatureCollection", features = new List<AgroBase.Models.MapaFeatureModel>() };
|
||||||
foreach (var x in RuasMapaCarregado.Where(x => x.Selected))
|
foreach (var x in RuasMapaCarregado.Where(x => selecionadas ? x.Selected : true))
|
||||||
{
|
{
|
||||||
string id = ((dynamic)x.Feature).Id.ToString();
|
string id = ((dynamic)x.Feature).Id.ToString();
|
||||||
string type = ((dynamic)x.Feature).Geometry.GeometryType;
|
string type = ((dynamic)x.Feature).Geometry.GeometryType;
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ namespace OperationControl.Models
|
||||||
|
|
||||||
public static readonly string BaseMarkerID = "BASE";
|
public static readonly string BaseMarkerID = "BASE";
|
||||||
private static readonly object _RoversLock = new object();
|
private static readonly object _RoversLock = new object();
|
||||||
private static double TempoRoverVivo = 5.0;
|
public static double TempoRoverVivo = 5.0;
|
||||||
private static bool AdicionandoNovoRover = false;
|
private static bool AdicionandoNovoRover = false;
|
||||||
private static bool AtualizandoListaRovers = false;
|
private static bool AtualizandoListaRovers = false;
|
||||||
public static AgroBase.Models.Enums.StatusModulo StatusBase
|
public static AgroBase.Models.Enums.StatusModulo StatusBase
|
||||||
|
|
@ -153,7 +153,7 @@ namespace OperationControl.Models
|
||||||
OperacaoParametrosModel rover;
|
OperacaoParametrosModel rover;
|
||||||
lock (_RoversLock)
|
lock (_RoversLock)
|
||||||
{
|
{
|
||||||
rover = RoversNaRede.FirstOrDefault(x => x.RoverId == device_id);
|
rover = RoversNaRede?.FirstOrDefault(x => x.RoverId == device_id);
|
||||||
if (rover != null)
|
if (rover != null)
|
||||||
{
|
{
|
||||||
obj.Momento = DateTime.Now;
|
obj.Momento = DateTime.Now;
|
||||||
|
|
@ -189,10 +189,8 @@ namespace OperationControl.Models
|
||||||
var rover = RoversNaRede.FirstOrDefault(x => x.RoverId == device_id);
|
var rover = RoversNaRede.FirstOrDefault(x => x.RoverId == device_id);
|
||||||
if (rover != null)
|
if (rover != null)
|
||||||
{
|
{
|
||||||
obj.UltimoContato = DateTime.Now;
|
rover = obj;
|
||||||
obj.IP = rover.IP;
|
rover.UltimoContato = DateTime.Now;
|
||||||
RoversNaRede.Remove(rover);
|
|
||||||
RoversNaRede.Add(obj);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
((App)Application.Current).Shell.Main?.AtualizarDadosTela_ParametrosOperacao(obj.Controle);
|
((App)Application.Current).Shell.Main?.AtualizarDadosTela_ParametrosOperacao(obj.Controle);
|
||||||
|
|
@ -253,6 +251,7 @@ namespace OperationControl.Models
|
||||||
((App)Application.Current).Shell.Main?.RemoverAlerta(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb);
|
((App)Application.Current).Shell.Main?.RemoverAlerta(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb);
|
||||||
((App)Application.Current).Shell.RemoverAlertaDock(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb);
|
((App)Application.Current).Shell.RemoverAlertaDock(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb);
|
||||||
}
|
}
|
||||||
|
((App)Application.Current).Shell.Dock?._vm?.AtualizarBarraSuperior(rover);
|
||||||
}
|
}
|
||||||
|
|
||||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
|
|
||||||
|
|
@ -746,12 +746,12 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento.Diagnostico
|
||||||
|
|
||||||
case T_Code.Ipb:
|
case T_Code.Ipb:
|
||||||
var conexao = rover.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
|
var conexao = rover.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
|
||||||
AddOrAppendSerie("Latência", "ms", conexao?.detalhes?["avg_rtt"]?.Value<double>());
|
AddOrAppendSerie("Latência", "ms", conexao?.detalhes?["avg_rtt"]?.Value<double?>());
|
||||||
AddOrAppendSerie("Jitter", "ms", conexao?.detalhes?["jitter"]?.Value<double>());
|
AddOrAppendSerie("Jitter", "ms", conexao?.detalhes?["jitter"]?.Value<double?>());
|
||||||
AddOrAppendSerie("Timeout", "%", conexao?.detalhes?["tmout_pct"]?.Value<double>());
|
AddOrAppendSerie("Timeout", "%", conexao?.detalhes?["tmout_pct"]?.Value<double?>());
|
||||||
AddOrAppendSerie("Perda", "%", conexao?.detalhes?["loss_pct"]?.Value<double>());
|
AddOrAppendSerie("Perda", "%", conexao?.detalhes?["loss_pct"]?.Value<double?>());
|
||||||
AddOrAppendSerie("Banda", "Mbps", conexao?.detalhes?["bw_total_mbps"]?.Value<double>());
|
AddOrAppendSerie("Banda", "Mbps", conexao?.detalhes?["bw_total_mbps"]?.Value<double?>());
|
||||||
AddOrAppendSerie("Banda %", "%", conexao?.detalhes?["bw_util_pct"]?.Value<double>());
|
AddOrAppendSerie("Banda %", "%", conexao?.detalhes?["bw_util_pct"]?.Value<double?>());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_Code.Gps:
|
case T_Code.Gps:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using AgroBase.Models;
|
using AgroBase.Models;
|
||||||
using AgroBase.Models.Operacoes;
|
using AgroBase.Models.Operacoes;
|
||||||
|
using OperationControl.Controls;
|
||||||
using OperationControl.Helpers;
|
using OperationControl.Helpers;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
@ -51,6 +52,11 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
|
||||||
Parametros = origem;
|
Parametros = origem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ProcessarStreetMapClicked(MapViewControl.StreetMapClickedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
private void OnPropertyChanged(string nome)
|
private void OnPropertyChanged(string nome)
|
||||||
|
|
|
||||||
|
|
@ -366,8 +366,26 @@ namespace OperationControl.ViewModels
|
||||||
if (rover == null || obj == null)
|
if (rover == null || obj == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (rover.RoverId != VariaveisControleOperacao.SelectedRoverId)
|
||||||
|
return;
|
||||||
|
|
||||||
var eventos = new List<MotivoTopBarModel>();
|
var eventos = new List<MotivoTopBarModel>();
|
||||||
|
|
||||||
|
double tempoSemResposta = (DateTime.Now - rover.UltimoContato).TotalSeconds;
|
||||||
|
bool semComunicacao = tempoSemResposta >= VariaveisControleOperacao.TempoRoverVivo;
|
||||||
|
|
||||||
|
if (semComunicacao)
|
||||||
|
{
|
||||||
|
eventos.Add(new MotivoTopBarModel
|
||||||
|
{
|
||||||
|
Prioridade = 2000,
|
||||||
|
Fonte = "Comunicacao",
|
||||||
|
Titulo = "PERDA DE COMUNICAÇÃO COM O ROVER",
|
||||||
|
Descricao = $"Sem resposta do rover há {tempoSemResposta:F1} s. Tentando reconectar automaticamente.",
|
||||||
|
StatusVisual = StatusModulo.Desconectado
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (obj.ModulosSaude?.All(x => (x?.status ?? StatusModulo.Desconectado) == StatusModulo.Desconectado) ?? false)
|
if (obj.ModulosSaude?.All(x => (x?.status ?? StatusModulo.Desconectado) == StatusModulo.Desconectado) ?? false)
|
||||||
{
|
{
|
||||||
// ============================
|
// ============================
|
||||||
|
|
@ -535,6 +553,7 @@ namespace OperationControl.ViewModels
|
||||||
// Badge curto
|
// Badge curto
|
||||||
badge = principal.Fonte switch
|
badge = principal.Fonte switch
|
||||||
{
|
{
|
||||||
|
"Comunicacao" => "DESCONECTADO",
|
||||||
"Emergencia" => "EMERGÊNCIA",
|
"Emergencia" => "EMERGÊNCIA",
|
||||||
"Pausa" => "PAUSADO",
|
"Pausa" => "PAUSADO",
|
||||||
"Trajetoria" => "TRAJETÓRIA",
|
"Trajetoria" => "TRAJETÓRIA",
|
||||||
|
|
@ -546,6 +565,7 @@ namespace OperationControl.ViewModels
|
||||||
// Resumo curto
|
// Resumo curto
|
||||||
resumo = principal.Fonte switch
|
resumo = principal.Fonte switch
|
||||||
{
|
{
|
||||||
|
"Comunicacao" => "Sem telemetria",
|
||||||
"Emergencia" => "Parada imediata",
|
"Emergencia" => "Parada imediata",
|
||||||
"Pausa" => "Aguardando retomada",
|
"Pausa" => "Aguardando retomada",
|
||||||
"Trajetoria" => "Operação bloqueada",
|
"Trajetoria" => "Operação bloqueada",
|
||||||
|
|
@ -968,6 +988,15 @@ namespace OperationControl.ViewModels
|
||||||
}
|
}
|
||||||
private void OnSalvarParametrosSolicitado(OperacaoParametrosModel parametros)
|
private void OnSalvarParametrosSolicitado(OperacaoParametrosModel parametros)
|
||||||
{
|
{
|
||||||
|
var dadosMapa = _viewOperacaoCenter?.Mapa?.CriarDadosMapa();
|
||||||
|
var ruasPercorrer = _viewOperacaoCenter?.Mapa?.RuasMapaCarregado?.Where(x => x.Selected)?.Select(x => x.Id)?.ToList() ?? new List<string>();
|
||||||
|
|
||||||
|
if ((dadosMapa?.features?.Count ?? 0) == 0 || !ruasPercorrer.Any())
|
||||||
|
{
|
||||||
|
System.Windows.MessageBox.Show("Selecione as ruas no mapa onde o equipamento irá operar", "Ruas não selecionadas", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (VariaveisControleOperacao.RoverEmFoco == null)
|
if (VariaveisControleOperacao.RoverEmFoco == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ namespace OperationControl.Views.Operacao
|
||||||
private void MAP_StreetMapClicked(object? sender, MapViewControl.StreetMapClickedEventArgs e)
|
private void MAP_StreetMapClicked(object? sender, MapViewControl.StreetMapClickedEventArgs e)
|
||||||
{
|
{
|
||||||
_vm.ProcessarStreetMapClicked(e);
|
_vm.ProcessarStreetMapClicked(e);
|
||||||
|
((App)System.Windows.Application.Current).Shell.Dock?._vm?._viewOperacaoRight?.areaParametrizacao?._vm?.ProcessarStreetMapClicked(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue