Ajustes em IMU, IPB, Alertas, Top Bar, Left Bar, Controle, Redis
This commit is contained in:
parent
3f55ed4fca
commit
a7ac637032
|
|
@ -170,10 +170,17 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
public double timestamp { get; set; }
|
public double timestamp { get; set; }
|
||||||
public double roll { get; set; }
|
public double roll { get; set; }
|
||||||
|
public double roll_seg { get; set; }
|
||||||
public double pitch { get; set; }
|
public double pitch { get; set; }
|
||||||
|
public double pitch_seg { get; set; }
|
||||||
public double yaw { get; set; }
|
public double yaw { get; set; }
|
||||||
public double yaw_fixed { get; set; }
|
public double yaw_fixed { get; set; }
|
||||||
public double vel_mps { get; set; }
|
public double vel_mps { get; set; }
|
||||||
|
public bool em_movimento { get; set; }
|
||||||
|
public double movimento_idx { get; set; }
|
||||||
|
public double rugosidade_idx { get; set; }
|
||||||
|
public double impacto_idx { get; set; }
|
||||||
|
public double estabilidade_idx { get; set; }
|
||||||
public double latencia { get; set; }
|
public double latencia { get; set; }
|
||||||
public double frequencia { get; set; }
|
public double frequencia { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2407,6 +2407,11 @@ namespace AgroBase.Models
|
||||||
InclinacaoFrontal = imu_iniciado ? sIMU.roll : 0,
|
InclinacaoFrontal = imu_iniciado ? sIMU.roll : 0,
|
||||||
Rotacao = imu_iniciado ? sIMU.yaw : 0,
|
Rotacao = imu_iniciado ? sIMU.yaw : 0,
|
||||||
RotacaoCorrigida = imu_iniciado ? sIMU.yaw_fixed : 0,
|
RotacaoCorrigida = imu_iniciado ? sIMU.yaw_fixed : 0,
|
||||||
|
EmMovimento = imu_iniciado ? sIMU.em_movimento : false,
|
||||||
|
IndiceMovimento = imu_iniciado ? sIMU.movimento_idx : 0,
|
||||||
|
IndiceRugosidade = imu_iniciado ? sIMU.rugosidade_idx : 0,
|
||||||
|
IndiceEstabilidade = imu_iniciado ? sIMU.estabilidade_idx : 0,
|
||||||
|
IndiceImpacto = imu_iniciado ? sIMU.impacto_idx : 0,
|
||||||
} : new OperacaoSensoriamentoLogImuModel();
|
} : new OperacaoSensoriamentoLogImuModel();
|
||||||
|
|
||||||
var dadosAtuador = _DispAtu != null ? new OperacaoSensoriamentoLogAtuModel()
|
var dadosAtuador = _DispAtu != null ? new OperacaoSensoriamentoLogAtuModel()
|
||||||
|
|
@ -2693,6 +2698,11 @@ namespace AgroBase.Models
|
||||||
public double InclinacaoFrontal { get; set; }
|
public double InclinacaoFrontal { get; set; }
|
||||||
public double Rotacao { get; set; }
|
public double Rotacao { get; set; }
|
||||||
public double RotacaoCorrigida { get; set; }
|
public double RotacaoCorrigida { get; set; }
|
||||||
|
public bool EmMovimento { get; set; }
|
||||||
|
public double IndiceMovimento { get; set; }
|
||||||
|
public double IndiceRugosidade { get; set; }
|
||||||
|
public double IndiceEstabilidade { get; set; }
|
||||||
|
public double IndiceImpacto { get; set; }
|
||||||
|
|
||||||
public OperacaoSensoriamentoLogImuModel Clone()
|
public OperacaoSensoriamentoLogImuModel Clone()
|
||||||
{
|
{
|
||||||
|
|
@ -2703,6 +2713,11 @@ namespace AgroBase.Models
|
||||||
InclinacaoFrontal = InclinacaoFrontal,
|
InclinacaoFrontal = InclinacaoFrontal,
|
||||||
Rotacao = Rotacao,
|
Rotacao = Rotacao,
|
||||||
RotacaoCorrigida = RotacaoCorrigida,
|
RotacaoCorrigida = RotacaoCorrigida,
|
||||||
|
EmMovimento = EmMovimento,
|
||||||
|
IndiceMovimento = IndiceMovimento,
|
||||||
|
IndiceImpacto = IndiceImpacto,
|
||||||
|
IndiceEstabilidade = IndiceEstabilidade,
|
||||||
|
IndiceRugosidade = IndiceRugosidade,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -402,6 +402,11 @@ namespace AgroBase.Models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void MostrarLog(string Mensagem)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[AGROBASE] {Mensagem}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace AgroBase.Services.Operadores
|
||||||
public static List<ManagerWorkerMessageResponseModulosPendentesModel> ModulosSaude = new List<ManagerWorkerMessageResponseModulosPendentesModel>();
|
public static List<ManagerWorkerMessageResponseModulosPendentesModel> ModulosSaude = new List<ManagerWorkerMessageResponseModulosPendentesModel>();
|
||||||
|
|
||||||
private bool DebugMode = true;
|
private bool DebugMode = true;
|
||||||
private static bool AtualizandoRedis = false;
|
//private static bool AtualizandoRedis = false;
|
||||||
|
|
||||||
public void MostrarLog(string message, int limite = 500)
|
public void MostrarLog(string message, int limite = 500)
|
||||||
{
|
{
|
||||||
|
|
@ -81,12 +81,21 @@ namespace AgroBase.Services.Operadores
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AtualizarSaude()
|
public void AtualizarSaude()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Saude = OperadoresModels.AtualizarSaude(CtxKey.DadosHealthWorker);
|
Saude = OperadoresModels.AtualizarSaude(CtxKey.DadosHealthWorker);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarSaude: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void AtualizarDadosContextoLocal()
|
private static void AtualizarDadosContextoLocal()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
RedisService.AtualizarCampos(
|
RedisService.AtualizarCampos(
|
||||||
CtxKey.DadosOperacao,
|
CtxKey.DadosOperacao,
|
||||||
|
|
@ -99,12 +108,19 @@ namespace AgroBase.Services.Operadores
|
||||||
("pausa", Variaveis.OperacaoEmAndamento.Pausa)
|
("pausa", Variaveis.OperacaoEmAndamento.Pausa)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosContextoLocal: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void AtualizarDadosContexto()
|
public static void AtualizarDadosContexto()
|
||||||
{
|
{
|
||||||
if (AtualizandoRedis)
|
try
|
||||||
return;
|
{
|
||||||
AtualizandoRedis = true;
|
//if (AtualizandoRedis)
|
||||||
|
// return;
|
||||||
|
//AtualizandoRedis = true;
|
||||||
|
|
||||||
AtualizarDadosContextoLocal();
|
AtualizarDadosContextoLocal();
|
||||||
AtualizarDadosModulos();
|
AtualizarDadosModulos();
|
||||||
|
|
@ -114,7 +130,7 @@ namespace AgroBase.Services.Operadores
|
||||||
|
|
||||||
if (_Sensoriamento == null)
|
if (_Sensoriamento == null)
|
||||||
{
|
{
|
||||||
AtualizandoRedis = false;
|
//AtualizandoRedis = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,10 +182,17 @@ namespace AgroBase.Services.Operadores
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
AtualizandoRedis = false;
|
//AtualizandoRedis = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosContexto: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AtualizarDadosEquipamento()
|
private static void AtualizarDadosEquipamento()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
RedisService.AtualizarCampos(
|
RedisService.AtualizarCampos(
|
||||||
CtxKey.DadosEquipamento,
|
CtxKey.DadosEquipamento,
|
||||||
|
|
@ -197,21 +220,28 @@ namespace AgroBase.Services.Operadores
|
||||||
("angulo_pitch_max", VariaveisEquipamento.AnguloInclinacaoPitchMax)
|
("angulo_pitch_max", VariaveisEquipamento.AnguloInclinacaoPitchMax)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosEquipamento: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void AtualizarDadosOperacao(bool forcar = false)
|
public static void AtualizarDadosOperacao(bool forcar = false)
|
||||||
{
|
{
|
||||||
if (AtualizandoRedis && !forcar)
|
try
|
||||||
return;
|
{
|
||||||
|
//if (AtualizandoRedis && !forcar)
|
||||||
|
// return;
|
||||||
|
|
||||||
while (AtualizandoRedis && forcar) {}
|
//while (AtualizandoRedis && forcar) {}
|
||||||
|
|
||||||
AtualizandoRedis = true;
|
//AtualizandoRedis = true;
|
||||||
|
|
||||||
AtualizarDadosEquipamento();
|
AtualizarDadosEquipamento();
|
||||||
|
|
||||||
if (Variaveis.OperacaoEmAndamento.DispMvd == null)
|
if (Variaveis.OperacaoEmAndamento.DispMvd == null)
|
||||||
{
|
{
|
||||||
AtualizandoRedis = false;
|
//AtualizandoRedis = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,7 +311,7 @@ namespace AgroBase.Services.Operadores
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS && (Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?.Count() ?? 0) > 0)
|
if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS && (Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?.Any() ?? false))
|
||||||
{
|
{
|
||||||
var comando = new
|
var comando = new
|
||||||
{
|
{
|
||||||
|
|
@ -292,10 +322,17 @@ namespace AgroBase.Services.Operadores
|
||||||
|
|
||||||
//Console.WriteLine("Dados de operacao atualizados com sucesso!");
|
//Console.WriteLine("Dados de operacao atualizados com sucesso!");
|
||||||
|
|
||||||
AtualizandoRedis = false;
|
//AtualizandoRedis = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosOperacao: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AtualizarDadosControleOperacao()
|
public static void AtualizarDadosControleOperacao()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var pControle = Variaveis.OperacaoEmAndamento.Parametros.Controle;
|
var pControle = Variaveis.OperacaoEmAndamento.Parametros.Controle;
|
||||||
if (pControle == null)
|
if (pControle == null)
|
||||||
|
|
@ -312,8 +349,15 @@ namespace AgroBase.Services.Operadores
|
||||||
("registrar_dados_pos_processamento", pControle.RegistrarDadosPosProcessamento)
|
("registrar_dados_pos_processamento", pControle.RegistrarDadosPosProcessamento)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosControleOperacao: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void AtualizarDadosModulos()
|
private static void AtualizarDadosModulos()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return;
|
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return;
|
||||||
if (Variaveis.OperacaoEmAndamento.DispAtu == null) return;
|
if (Variaveis.OperacaoEmAndamento.DispAtu == null) return;
|
||||||
|
|
@ -835,7 +879,11 @@ namespace AgroBase.Services.Operadores
|
||||||
("iniciado", Variaveis.MqttServiceBase?.StatusConexao() ?? false),
|
("iniciado", Variaveis.MqttServiceBase?.StatusConexao() ?? false),
|
||||||
("distancia_base", distanciaBase)
|
("distancia_base", distanciaBase)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosModulos: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
from collections import deque
|
||||||
from ahrs.filters import Madgwick
|
from ahrs.filters import Madgwick
|
||||||
from scipy.spatial.transform import Rotation as R
|
from scipy.spatial.transform import Rotation as R
|
||||||
from shared.contexto_global_redis import ContextoGlobalRedis
|
from shared.contexto_global_redis import ContextoGlobalRedis
|
||||||
|
|
@ -11,26 +12,53 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
def __init__(self, queue=None, freq=100, angulo_inicial=26.3):
|
def __init__(self, queue=None, freq=100, angulo_inicial=26.3):
|
||||||
self.freq = freq
|
self.freq = freq
|
||||||
|
|
||||||
if queue is None: return
|
if queue is None:
|
||||||
|
return
|
||||||
|
|
||||||
self.imu_queue = queue
|
self.imu_queue = queue
|
||||||
self.filtro_imu = Madgwick(beta=0.8, frequency=freq)
|
|
||||||
|
|
||||||
self.roll_inicial = 90 - angulo_inicial # graus
|
# beta bem mais conservador
|
||||||
|
self.filtro_imu = Madgwick(beta=0.05, frequency=freq)
|
||||||
|
|
||||||
|
self.roll_inicial = 90 - angulo_inicial
|
||||||
self.pitch_inicial = 0.0
|
self.pitch_inicial = 0.0
|
||||||
self.yaw_inicial = 0.0
|
self.yaw_inicial = 0.0
|
||||||
|
|
||||||
r = R.from_euler('xyz', [self.roll_inicial, self.pitch_inicial, self.yaw_inicial], degrees=True)
|
r = R.from_euler('xyz', [self.roll_inicial, self.pitch_inicial, self.yaw_inicial], degrees=True)
|
||||||
q = r.as_quat() # formato: [x, y, z, w]
|
q = r.as_quat()
|
||||||
self.q = np.array([q[3], q[0], q[1], q[2]]) # reordenando para [w, x, y, z] como espera o filtro
|
self.q = np.array([q[3], q[0], q[1], q[2]], dtype=np.float64)
|
||||||
|
|
||||||
self.imu_em_falha = False
|
self.imu_em_falha = False
|
||||||
self.tempo_erro = 0
|
self.tempo_erro = 0
|
||||||
|
|
||||||
self.v_world = np.zeros(3, dtype=np.float32)
|
self.v_world = np.zeros(3, dtype=np.float64)
|
||||||
self.last_imu_ts = None
|
|
||||||
self.g = 9.80665
|
self.g = 9.80665
|
||||||
|
|
||||||
|
self.last_loop_ts = None
|
||||||
|
|
||||||
|
# filtros de segurança
|
||||||
|
self.roll_seg = 0.0
|
||||||
|
self.pitch_seg = 0.0
|
||||||
|
self.alpha_seg = 0.12
|
||||||
|
|
||||||
|
# mediana curta para matar espinhos
|
||||||
|
self.roll_hist = deque(maxlen=5)
|
||||||
|
self.pitch_hist = deque(maxlen=5)
|
||||||
|
|
||||||
|
self.acc_norm_hist = deque(maxlen=100) # ~1s
|
||||||
|
self.acc_z_hist = deque(maxlen=100) # ~1s
|
||||||
|
self.gyro_norm_hist = deque(maxlen=60) # ~0.6s
|
||||||
|
self.roll_seg_hist = deque(maxlen=60) # ~0.6s
|
||||||
|
self.pitch_seg_hist = deque(maxlen=60) # ~0.6s
|
||||||
|
self.impacto_hist = deque(maxlen=20) # ~0.2s para pico recente
|
||||||
|
self.em_movimento = False
|
||||||
|
self.movimento_idx = 0.0
|
||||||
|
self.rugosidade_idx = 0.0
|
||||||
|
self.impacto_idx = 0.0
|
||||||
|
self.estabilidade_idx = 100.0
|
||||||
|
self.last_redis_ts = 0.0
|
||||||
|
self.redis_period = 0.10 # 100 ms
|
||||||
|
|
||||||
self.ativo = True
|
self.ativo = True
|
||||||
self.imu_thread = threading.Thread(target=self.imu_task_loop, daemon=True)
|
self.imu_thread = threading.Thread(target=self.imu_task_loop, daemon=True)
|
||||||
self.imu_thread.start()
|
self.imu_thread.start()
|
||||||
|
|
@ -42,48 +70,185 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
self.ativo = False
|
self.ativo = False
|
||||||
self.mostrar_log("Task parada")
|
self.mostrar_log("Task parada")
|
||||||
|
|
||||||
|
def _mediana_curta(self, hist, valor):
|
||||||
|
hist.append(valor)
|
||||||
|
return float(np.median(hist))
|
||||||
|
|
||||||
|
def clamp(self, valor, vmin, vmax):
|
||||||
|
return max(vmin, min(vmax, valor))
|
||||||
|
|
||||||
|
def norm_to_100(self, valor, faixa_min, faixa_max):
|
||||||
|
if faixa_max <= faixa_min:
|
||||||
|
return 0.0
|
||||||
|
x = (valor - faixa_min) / (faixa_max - faixa_min)
|
||||||
|
return self.clamp(x * 100.0, 0.0, 100.0)
|
||||||
|
|
||||||
|
def rms(self, valores):
|
||||||
|
if not valores:
|
||||||
|
return 0.0
|
||||||
|
arr = np.asarray(valores, dtype=np.float64)
|
||||||
|
return float(np.sqrt(np.mean(arr ** 2)))
|
||||||
|
|
||||||
|
def stddev(self, valores):
|
||||||
|
if not valores:
|
||||||
|
return 0.0
|
||||||
|
arr = np.asarray(valores, dtype=np.float64)
|
||||||
|
return float(np.std(arr))
|
||||||
|
|
||||||
|
def _atualizar_indices(self, velocidade_mps, gx, gy, gz, a_lin):
|
||||||
|
acc_lin_norm = float(np.linalg.norm(a_lin))
|
||||||
|
gyro_norm_deg = float(np.rad2deg(np.linalg.norm([gx, gy, gz])))
|
||||||
|
|
||||||
|
# Históricos
|
||||||
|
self.acc_norm_hist.append(acc_lin_norm)
|
||||||
|
self.acc_z_hist.append(float(a_lin[2]))
|
||||||
|
self.gyro_norm_hist.append(gyro_norm_deg)
|
||||||
|
self.roll_seg_hist.append(float(self.roll_seg))
|
||||||
|
self.pitch_seg_hist.append(float(self.pitch_seg))
|
||||||
|
self.impacto_hist.append(acc_lin_norm)
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# 1) Flag em movimento
|
||||||
|
# ============================
|
||||||
|
vel_ok = velocidade_mps > 0.05
|
||||||
|
gyro_ok = gyro_norm_deg > 3.0
|
||||||
|
acc_ok = acc_lin_norm > 0.20
|
||||||
|
self.em_movimento = bool(vel_ok or gyro_ok or acc_ok)
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# 2) Movimento idx
|
||||||
|
# ============================
|
||||||
|
vel_score = self.norm_to_100(velocidade_mps, 0.0, 1.8)
|
||||||
|
gyro_score = self.norm_to_100(gyro_norm_deg, 0.0, 40.0)
|
||||||
|
acc_score = self.norm_to_100(acc_lin_norm, 0.0, 1.5)
|
||||||
|
|
||||||
|
movimento_idx = (
|
||||||
|
0.50 * vel_score +
|
||||||
|
0.25 * gyro_score +
|
||||||
|
0.25 * acc_score
|
||||||
|
)
|
||||||
|
self.movimento_idx = round(self.clamp(movimento_idx, 0.0, 100.0), 1)
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# 3) Rugosidade idx
|
||||||
|
# ============================
|
||||||
|
rug_z = self.rms(self.acc_z_hist)
|
||||||
|
rug_total = self.stddev(self.acc_norm_hist)
|
||||||
|
|
||||||
|
rug_z_score = self.norm_to_100(rug_z, 0.02, 0.80)
|
||||||
|
rug_total_score = self.norm_to_100(rug_total, 0.01, 0.60)
|
||||||
|
|
||||||
|
rugosidade_idx = (
|
||||||
|
0.65 * rug_z_score +
|
||||||
|
0.35 * rug_total_score
|
||||||
|
)
|
||||||
|
self.rugosidade_idx = round(self.clamp(rugosidade_idx, 0.0, 100.0), 1)
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# 4) Impacto idx
|
||||||
|
# ============================
|
||||||
|
pico_impacto = max(self.impacto_hist) if self.impacto_hist else 0.0
|
||||||
|
impacto_bruto = self.norm_to_100(pico_impacto, 0.4, 4.0)
|
||||||
|
|
||||||
|
# decaimento suave
|
||||||
|
self.impacto_idx = max(impacto_bruto, self.impacto_idx * 0.85)
|
||||||
|
self.impacto_idx = round(self.clamp(self.impacto_idx, 0.0, 100.0), 1)
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# 5) Estabilidade idx
|
||||||
|
# ============================
|
||||||
|
roll_std = self.stddev(self.roll_seg_hist)
|
||||||
|
pitch_std = self.stddev(self.pitch_seg_hist)
|
||||||
|
osc_ang = float(np.sqrt(roll_std**2 + pitch_std**2))
|
||||||
|
|
||||||
|
osc_score = self.norm_to_100(osc_ang, 0.2, 8.0)
|
||||||
|
|
||||||
|
instabilidade = (
|
||||||
|
0.60 * osc_score +
|
||||||
|
0.40 * self.rugosidade_idx
|
||||||
|
)
|
||||||
|
|
||||||
|
estabilidade_idx = 100.0 - instabilidade
|
||||||
|
self.estabilidade_idx = round(self.clamp(estabilidade_idx, 0.0, 100.0), 1)
|
||||||
|
|
||||||
def imu_task_loop(self):
|
def imu_task_loop(self):
|
||||||
t0 = time.time()
|
t0 = time.perf_counter()
|
||||||
|
|
||||||
while self.ativo:
|
while self.ativo:
|
||||||
latencia = 0
|
latencia = 0.0
|
||||||
try:
|
try:
|
||||||
agora = time.time()
|
agora = time.perf_counter()
|
||||||
t_tick = agora - t0
|
t_tick = agora - t0
|
||||||
t0 = agora
|
t0 = agora
|
||||||
f_tick = 1.0 / (t_tick + 1e-9)
|
f_tick = 1.0 / max(t_tick, 1e-6)
|
||||||
|
|
||||||
imuData = self.imu_queue.tryGet()
|
imuData = self.imu_queue.tryGet()
|
||||||
|
|
||||||
if imuData is not None and self.imu_em_falha:
|
if imuData is not None and self.imu_em_falha:
|
||||||
self.mostrar_log("Reconectado com sucesso, voltando ao modo normal")
|
self.mostrar_log("Reconectado com sucesso, voltando ao modo normal")
|
||||||
self.imu_em_falha = False
|
self.imu_em_falha = False
|
||||||
|
|
||||||
if imuData is not None:
|
if imuData is not None and len(imuData.packets) > 0:
|
||||||
# dt estimado por pacote
|
now = time.perf_counter()
|
||||||
now = time.time()
|
if self.last_loop_ts is None:
|
||||||
if self.last_imu_ts is None:
|
dt_total = 1.0 / self.freq
|
||||||
dt = 1.0 / max(1e-3, f_tick) # fallback
|
|
||||||
else:
|
else:
|
||||||
dt = max(0.0, now - self.last_imu_ts)
|
dt_total = max(1e-4, now - self.last_loop_ts)
|
||||||
self.last_imu_ts = now
|
self.last_loop_ts = now
|
||||||
dt = min(dt, 0.05) # clamp anti-bursts (<=50 ms)
|
|
||||||
self.filtro_imu.Dt = float(dt)
|
dt_total = min(dt_total, 0.05)
|
||||||
|
num_packets = max(1, len(imuData.packets))
|
||||||
|
dt_packet = max(1e-4, dt_total / num_packets)
|
||||||
|
|
||||||
for packet in imuData.packets:
|
for packet in imuData.packets:
|
||||||
accel = packet.acceleroMeter
|
accel = packet.acceleroMeter
|
||||||
gyro = packet.gyroscope
|
gyro = packet.gyroscope
|
||||||
|
|
||||||
ax = accel.x
|
ax = float(accel.x)
|
||||||
ay = accel.y
|
ay = float(accel.y)
|
||||||
az = accel.z
|
az = float(accel.z)
|
||||||
|
|
||||||
gx = np.deg2rad(gyro.x)
|
gx = float(np.deg2rad(gyro.x))
|
||||||
gy = np.deg2rad(gyro.y)
|
gy = float(np.deg2rad(gyro.y))
|
||||||
gz = np.deg2rad(gyro.z)
|
gz = float(np.deg2rad(gyro.z))
|
||||||
|
|
||||||
|
a_body = np.array([ax, ay, az], dtype=np.float64)
|
||||||
|
acc_norm = np.linalg.norm(a_body)
|
||||||
|
|
||||||
|
self.filtro_imu.Dt = float(dt_packet)
|
||||||
|
|
||||||
|
# Confia no acelerômetro só quando ele parece representar gravidade
|
||||||
|
usa_acc = abs(acc_norm - self.g) < 0.20
|
||||||
|
|
||||||
|
if usa_acc:
|
||||||
self.q = self.filtro_imu.updateIMU(
|
self.q = self.filtro_imu.updateIMU(
|
||||||
q=self.q,
|
q=self.q,
|
||||||
gyr=np.array([gx, gy, gz]),
|
gyr=np.array([gx, gy, gz], dtype=np.float64),
|
||||||
acc=np.array([ax, ay, az])
|
acc=a_body
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# fallback: mantém última orientação integrando com gyro "na mão"
|
||||||
|
# caso sua lib não aceite acc=None
|
||||||
|
omega = np.array([gx, gy, gz], dtype=np.float64)
|
||||||
|
omega_norm = np.linalg.norm(omega)
|
||||||
|
if omega_norm > 1e-9:
|
||||||
|
theta = omega_norm * dt_packet
|
||||||
|
axis = omega / omega_norm
|
||||||
|
dq_xyz = axis * np.sin(theta / 2.0)
|
||||||
|
dq_w = np.cos(theta / 2.0)
|
||||||
|
dq = np.array([dq_w, dq_xyz[0], dq_xyz[1], dq_xyz[2]], dtype=np.float64)
|
||||||
|
|
||||||
|
w1, x1, y1, z1 = self.q
|
||||||
|
w2, x2, y2, z2 = dq
|
||||||
|
self.q = np.array([
|
||||||
|
w1*w2 - x1*x2 - y1*y2 - z1*z2,
|
||||||
|
w1*x2 + x1*w2 + y1*z2 - z1*y2,
|
||||||
|
w1*y2 - x1*z2 + y1*w2 + z1*x2,
|
||||||
|
w1*z2 + x1*y2 - y1*x2 + z1*w2
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
self.q /= max(np.linalg.norm(self.q), 1e-9)
|
||||||
|
|
||||||
r = R.from_quat([self.q[1], self.q[2], self.q[3], self.q[0]])
|
r = R.from_quat([self.q[1], self.q[2], self.q[3], self.q[0]])
|
||||||
roll, pitch, yaw = r.as_euler('xyz', degrees=True)
|
roll, pitch, yaw = r.as_euler('xyz', degrees=True)
|
||||||
|
|
||||||
|
|
@ -91,48 +256,67 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
pitch -= self.pitch_inicial
|
pitch -= self.pitch_inicial
|
||||||
yaw -= self.yaw_inicial
|
yaw -= self.yaw_inicial
|
||||||
|
|
||||||
|
# mata picos curtíssimos
|
||||||
|
roll = self._mediana_curta(self.roll_hist, roll)
|
||||||
|
pitch = self._mediana_curta(self.pitch_hist, pitch)
|
||||||
|
|
||||||
|
# filtro leve para segurança
|
||||||
|
self.roll_seg = (1.0 - self.alpha_seg) * self.roll_seg + self.alpha_seg * roll
|
||||||
|
self.pitch_seg = (1.0 - self.alpha_seg) * self.pitch_seg + self.alpha_seg * pitch
|
||||||
|
|
||||||
|
# velocidade linear
|
||||||
Rwb = r.as_matrix()
|
Rwb = r.as_matrix()
|
||||||
|
|
||||||
# 1) aceleração no mundo
|
|
||||||
a_body = np.array([ax, ay, az], dtype=np.float64)
|
|
||||||
a_world = Rwb @ a_body
|
a_world = Rwb @ a_body
|
||||||
|
|
||||||
# 2) remove gravidade
|
|
||||||
a_lin = a_world - np.array([0.0, 0.0, self.g], dtype=np.float64)
|
a_lin = a_world - np.array([0.0, 0.0, self.g], dtype=np.float64)
|
||||||
|
|
||||||
# 3) filtra ruído muito baixo (deadzone)
|
dead = 0.08
|
||||||
dead = 0.05 # m/s²
|
|
||||||
a_lin[np.abs(a_lin) < dead] = 0.0
|
a_lin[np.abs(a_lin) < dead] = 0.0
|
||||||
|
|
||||||
# 4) ZUPT (Zero-velocity update) se parado:
|
parado = (
|
||||||
# - giros muito baixos e módulo da aceleração ~ g
|
np.linalg.norm([gx, gy, gz]) < np.deg2rad(1.5) and
|
||||||
if (np.linalg.norm([gx, gy, gz]) < np.deg2rad(2.0)
|
abs(np.linalg.norm(a_body) - self.g) < 0.10
|
||||||
and abs(np.linalg.norm(a_body) - self.g) < 0.12):
|
)
|
||||||
# parado: puxe para zero agressivo
|
|
||||||
self.v_world *= 0.2
|
if parado:
|
||||||
else:
|
self.v_world *= 0.1
|
||||||
# 5) integra velocidade
|
else:
|
||||||
self.v_world += (a_lin * dt)
|
self.v_world += (a_lin * dt_packet)
|
||||||
|
|
||||||
# 6) projeta no plano do chão (z=0) e computa velocidade escalar
|
|
||||||
v_planar = self.v_world.copy()
|
v_planar = self.v_world.copy()
|
||||||
v_planar[2] = 0.0
|
v_planar[2] = 0.0
|
||||||
velocidade_mps = float(np.linalg.norm(v_planar))
|
velocidade_mps = float(np.linalg.norm(v_planar))
|
||||||
|
|
||||||
t1 = time.time()
|
self._atualizar_indices(
|
||||||
latencia = t1 - t0
|
velocidade_mps=velocidade_mps,
|
||||||
f_exec = 1.0 / (latencia + 1e-9)
|
gx=gx, gy=gy, gz=gz,
|
||||||
|
a_lin=a_lin
|
||||||
|
)
|
||||||
|
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
latencia = t1 - agora
|
||||||
|
|
||||||
|
t_pub = time.perf_counter()
|
||||||
|
if (t_pub - self.last_redis_ts) >= self.redis_period:
|
||||||
|
self.last_redis_ts = t_pub
|
||||||
|
|
||||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||||
ContextoGlobalRedis.ModKey(T_Code.Imu),
|
ContextoGlobalRedis.ModKey(T_Code.Imu),
|
||||||
roll=round(-roll, 2), # frontal
|
roll=round(-roll, 2),
|
||||||
pitch=round(pitch, 2), # lateral
|
pitch=round(pitch, 2),
|
||||||
yaw=round(yaw, 2),
|
yaw=round(yaw, 2),
|
||||||
|
roll_seg=round(-self.roll_seg, 2),
|
||||||
|
pitch_seg=round(self.pitch_seg, 2),
|
||||||
vel_mps=round(velocidade_mps, 3),
|
vel_mps=round(velocidade_mps, 3),
|
||||||
timestamp=(t1 * 1000),
|
em_movimento=self.em_movimento,
|
||||||
|
movimento_idx=round(self.movimento_idx, 1),
|
||||||
|
rugosidade_idx=round(self.rugosidade_idx, 1),
|
||||||
|
impacto_idx=round(self.impacto_idx, 1),
|
||||||
|
estabilidade_idx=round(self.estabilidade_idx, 1),
|
||||||
|
timestamp=(time.time() * 1000),
|
||||||
latencia=latencia,
|
latencia=latencia,
|
||||||
frequencia=f_tick
|
frequencia=f_tick
|
||||||
)
|
)
|
||||||
#self.mostrar_log(f"IMU | Roll: {roll:.2f} | Pitch: {pitch:.2f} | Yaw: {yaw:.2f} | Freq: {f_tick:.2f} Hz | Latência: {latencia:.3f} s")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"Erro no loop: {e}")
|
self.mostrar_log(f"Erro no loop: {e}")
|
||||||
if not self.imu_em_falha:
|
if not self.imu_em_falha:
|
||||||
|
|
@ -143,14 +327,12 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
self.ativo = False
|
self.ativo = False
|
||||||
finally:
|
finally:
|
||||||
if self.imu_em_falha:
|
if self.imu_em_falha:
|
||||||
# Espera mais tempo pra evitar flood de erro
|
|
||||||
time.sleep(5.0)
|
time.sleep(5.0)
|
||||||
else:
|
else:
|
||||||
delay_corrigido = max(0, (1.0 / self.freq) - latencia)
|
delay_corrigido = max(0.0, (1.0 / self.freq) - latencia)
|
||||||
time.sleep(delay_corrigido)
|
time.sleep(delay_corrigido)
|
||||||
|
|
||||||
def atualizar_saude(self):
|
def atualizar_saude(self):
|
||||||
#self.mostrar_log("Atualizando saude")
|
|
||||||
try:
|
try:
|
||||||
modulo = ContextoGlobalRedis.get_modulo(T_Code.Imu)
|
modulo = ContextoGlobalRedis.get_modulo(T_Code.Imu)
|
||||||
FREQ_BASE = self.freq
|
FREQ_BASE = self.freq
|
||||||
|
|
@ -178,7 +360,6 @@ class IMUCamera(ModuloDiagnosticoBase):
|
||||||
if p > 0:
|
if p > 0:
|
||||||
motivos.append(f"Frequência abaixo do ideal: {freq_hz:.2f} Hz (penalidade {p}%)")
|
motivos.append(f"Frequência abaixo do ideal: {freq_hz:.2f} Hz (penalidade {p}%)")
|
||||||
|
|
||||||
# Penaliza se latência está alta (> 0.5s)
|
|
||||||
if latencia > 0.5:
|
if latencia > 0.5:
|
||||||
p = int(min(latencia * 100, 30))
|
p = int(min(latencia * 100, 30))
|
||||||
saude -= p
|
saude -= p
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import time
|
||||||
import threading
|
import threading
|
||||||
import statistics
|
import statistics
|
||||||
import socket
|
import socket
|
||||||
|
import numpy as np
|
||||||
import psutil
|
import psutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
@ -82,6 +83,8 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
self._ping_timeout_ms = 400 # recomendado: 300–500
|
self._ping_timeout_ms = 400 # recomendado: 300–500
|
||||||
self._ping_count = 2 # recomendado: 1 (janela já suaviza)
|
self._ping_count = 2 # recomendado: 1 (janela já suaviza)
|
||||||
self._ping_min_interval = 0.6 # não precisa pingar a cada 100ms
|
self._ping_min_interval = 0.6 # não precisa pingar a cada 100ms
|
||||||
|
self._hb_last_rx_monotonic = 0.0
|
||||||
|
self._last_ping_sample_applied_ts = 0.0
|
||||||
|
|
||||||
self.bw_max_mbps = 10.0 # capacidade aproximada do link (ajuste por teste)
|
self.bw_max_mbps = 10.0 # capacidade aproximada do link (ajuste por teste)
|
||||||
self.bw_safe_mbps = 05.0 # alvo saudável (até aqui não penaliza)
|
self.bw_safe_mbps = 05.0 # alvo saudável (até aqui não penaliza)
|
||||||
|
|
@ -366,6 +369,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
with self._mqtt_lock:
|
with self._mqtt_lock:
|
||||||
self.last_heartbeat_ts = now
|
self.last_heartbeat_ts = now
|
||||||
|
self._hb_last_rx_monotonic = time.perf_counter()
|
||||||
self._heartbeat_ok = True
|
self._heartbeat_ok = True
|
||||||
|
|
||||||
self._mqtt_mostra_log(f"[client={cid}] heartbeat recebido | t={now:.3f}")
|
self._mqtt_mostra_log(f"[client={cid}] heartbeat recebido | t={now:.3f}")
|
||||||
|
|
@ -449,45 +453,6 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
self.last_bw_counters = counters
|
self.last_bw_counters = counters
|
||||||
self.last_bw_ts = now
|
self.last_bw_ts = now
|
||||||
|
|
||||||
def _ping_once_old(self, pings=5, timeout=500):
|
|
||||||
"""
|
|
||||||
Retorna RTT em ms se ok, ou None se timeout.
|
|
||||||
Aqui exemplo pra Windows usando 'ping -n 1'.
|
|
||||||
Ajuste pro teu ambiente se precisar.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
base_ip = self.get_base_ip()
|
|
||||||
if base_ip is None:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
# timeout de 1000 ms
|
|
||||||
proc = subprocess.run(
|
|
||||||
["ping", "-n", f"{pings}", "-w", f"{timeout}", base_ip],
|
|
||||||
capture_output=True, text=True
|
|
||||||
)
|
|
||||||
if proc.returncode != 0:
|
|
||||||
print("timeout")
|
|
||||||
return None, 100
|
|
||||||
|
|
||||||
print(proc.stdout)
|
|
||||||
|
|
||||||
perda = None
|
|
||||||
media_tempo = None
|
|
||||||
for line in proc.stdout.splitlines():
|
|
||||||
line = line.lower()
|
|
||||||
if ("%" in line and perda is None):
|
|
||||||
perda = line.split('(')[1].split('%')[0]
|
|
||||||
perda = float(perda) if perda.isdigit() else None
|
|
||||||
elif (" = " in line and "ms" in line and media_tempo is None):
|
|
||||||
media_tempo = line.split(' = ')[3].split('ms')[0]
|
|
||||||
media_tempo = float(media_tempo) if media_tempo.isdigit() else None
|
|
||||||
if perda is not None and media_tempo is not None:
|
|
||||||
break
|
|
||||||
return media_tempo, perda
|
|
||||||
except Exception:
|
|
||||||
print("erro timeout")
|
|
||||||
return None, 100
|
|
||||||
|
|
||||||
def _ping_once(self, base_ip: str, pings: int, timeout_ms: int):
|
def _ping_once(self, base_ip: str, pings: int, timeout_ms: int):
|
||||||
"""
|
"""
|
||||||
Retorna (ok: bool, avg_rtt_ms: float|None, loss_pct: float)
|
Retorna (ok: bool, avg_rtt_ms: float|None, loss_pct: float)
|
||||||
|
|
@ -771,6 +736,9 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
# 3) usa snapshot pra alimentar suas janelas rtts/loses/timeouts
|
# 3) usa snapshot pra alimentar suas janelas rtts/loses/timeouts
|
||||||
ok, rtt, loss, ping_ts = self._get_ping_snapshot()
|
ok, rtt, loss, ping_ts = self._get_ping_snapshot()
|
||||||
#print(f"ok: {ok}, rtt: {rtt}, loss: {loss}, ping_ts: {ping_ts}")
|
#print(f"ok: {ok}, rtt: {rtt}, loss: {loss}, ping_ts: {ping_ts}")
|
||||||
|
if ping_ts > self._last_ping_sample_applied_ts:
|
||||||
|
self._last_ping_sample_applied_ts = ping_ts
|
||||||
|
|
||||||
self.timeouts_f.append(loss == 100)
|
self.timeouts_f.append(loss == 100)
|
||||||
self.timeouts_s.append(loss == 100)
|
self.timeouts_s.append(loss == 100)
|
||||||
|
|
||||||
|
|
@ -782,14 +750,6 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
self.loses_f.append(loss)
|
self.loses_f.append(loss)
|
||||||
self.loses_s.append(loss)
|
self.loses_s.append(loss)
|
||||||
|
|
||||||
#(rtt, loss) = self._ping_once_old(pings=3, timeout=200)
|
|
||||||
#print(f"rtt: {rtt}, loss: {loss}")
|
|
||||||
#self.timeouts.append(loss == 100)
|
|
||||||
#if rtt is not None:
|
|
||||||
# self.rtts.append(rtt)
|
|
||||||
#if loss is not None:
|
|
||||||
# self.loses.append(loss)
|
|
||||||
|
|
||||||
self._update_nic_errors()
|
self._update_nic_errors()
|
||||||
self._update_nic_bandwidth()
|
self._update_nic_bandwidth()
|
||||||
|
|
||||||
|
|
@ -1118,21 +1078,21 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
risk_score = max(0.0, min(100.0, risk_score))
|
risk_score = max(0.0, min(100.0, risk_score))
|
||||||
|
|
||||||
degradado_agora = (
|
degradado_agora = (
|
||||||
loss_pct_s >= 5 or
|
loss_pct_s >= 7 or
|
||||||
tmout_pct_s >= 4 or
|
tmout_pct_s >= 6 or
|
||||||
atraso_s >= 4.0 or
|
atraso_s >= 5.0 or
|
||||||
(loss_pct_f >= 8 and loss_pct_s >= 3) or
|
(loss_pct_f >= 12 and loss_pct_s >= 4) or
|
||||||
(tmout_pct_f >= 10 and tmout_pct_s >= 3) or
|
(tmout_pct_f >= 15 and tmout_pct_s >= 4) or
|
||||||
(jitter_s >= 35 and loss_pct_s >= 3)
|
(jitter_s >= 45 and loss_pct_s >= 4)
|
||||||
)
|
)
|
||||||
|
|
||||||
critico_agora = (
|
critico_agora = (
|
||||||
loss_pct_s >= 10 or
|
loss_pct_s >= 15 or
|
||||||
tmout_pct_s >= 8 or
|
tmout_pct_s >= 12 or
|
||||||
atraso_s >= 6.0 or
|
atraso_s >= 8.0 or
|
||||||
(loss_pct_f >= 15 and loss_pct_s >= 5) or
|
(loss_pct_f >= 20 and loss_pct_s >= 6) or
|
||||||
(tmout_pct_f >= 20 and tmout_pct_s >= 5) or
|
(tmout_pct_f >= 25 and tmout_pct_s >= 6) or
|
||||||
((loss_pct_s >= 6) and (atraso_s >= 5.0))
|
((loss_pct_s >= 8) and (atraso_s >= 6.0))
|
||||||
)
|
)
|
||||||
|
|
||||||
colapso_agora = (
|
colapso_agora = (
|
||||||
|
|
@ -1216,11 +1176,12 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
# Condição 2: Cerca invisível de rede violada
|
# Condição 2: Cerca invisível de rede violada
|
||||||
dist_violando_cerca = (
|
dist_violando_cerca = (
|
||||||
self.link_state == "DEGRADED" and
|
self.link_state == "DEGRADED" and
|
||||||
|
self._cycles_degraded >= 15 and
|
||||||
|
risk_score >= 70 and
|
||||||
self._distancia_inicio_degradado is not None and
|
self._distancia_inicio_degradado is not None and
|
||||||
distancia_base is not None and
|
distancia_base is not None and
|
||||||
distancia_base >= 0 and
|
distancia_base >= 0 and
|
||||||
self._max_distancia_desde_degradado > self._distancia_inicio_degradado + 2.0 and
|
distancia_base > self._distancia_inicio_degradado + 5.0
|
||||||
distancia_base > self._distancia_inicio_degradado + 1.0
|
|
||||||
)
|
)
|
||||||
if dist_violando_cerca:
|
if dist_violando_cerca:
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
|
|
@ -1338,3 +1299,5 @@ class ModuloIPBribge(ModuloDiagnosticoBase):
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Erro ao atualizar saude do modulo {self.t_code.name}: {e}")
|
print(f"Erro ao atualizar saude do modulo {self.t_code.name}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ def verifica_controle_liberado(ignorar_parada=False):
|
||||||
_equipamento = ContextoGlobalRedis.get_equipamento()
|
_equipamento = ContextoGlobalRedis.get_equipamento()
|
||||||
ang_roll_max = _equipamento.get("angulo_roll_max", 15.0)
|
ang_roll_max = _equipamento.get("angulo_roll_max", 15.0)
|
||||||
ang_pitch_max = _equipamento.get("angulo_pitch_max", 30.0)
|
ang_pitch_max = _equipamento.get("angulo_pitch_max", 30.0)
|
||||||
inclinacao_perigosa = abs(_imu.get("pitch", 0.0)) > ang_pitch_max or abs(_imu.get("roll", 0.0)) > ang_roll_max
|
inclinacao_perigosa = abs(_imu.get("pitch_seg", 0.0)) > ang_pitch_max or abs(_imu.get("roll_seg", 0.0)) > ang_roll_max
|
||||||
|
|
||||||
if not imu_operante:
|
if not imu_operante:
|
||||||
motivos.append(f"IMU mandatório não operante: {_imu_saude.name}")
|
motivos.append(f"IMU mandatório não operante: {_imu_saude.name}")
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,27 @@ namespace OperationControl.Models
|
||||||
Angulo = 0,
|
Angulo = 0,
|
||||||
PercentualVelocidadeSPKmh = 0,
|
PercentualVelocidadeSPKmh = 0,
|
||||||
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras
|
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras
|
||||||
|
},
|
||||||
|
Gnss = new AgroBase.Models.Operacoes.OperacaoParametrosDadosGNSSModel()
|
||||||
|
{
|
||||||
|
Latitude = -22.17254631649402,
|
||||||
|
Longitude = -47.395203906184044,
|
||||||
|
OrientacaoReal = 187
|
||||||
|
},
|
||||||
|
ModulosSaude = new List<AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel>()
|
||||||
|
{
|
||||||
|
new AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel()
|
||||||
|
{
|
||||||
|
modulo = T_Code.Gps,
|
||||||
|
saude = 0,
|
||||||
|
status = StatusModulo.Desconectado,
|
||||||
|
},
|
||||||
|
new AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel()
|
||||||
|
{
|
||||||
|
modulo = T_Code.Can,
|
||||||
|
saude = 70,
|
||||||
|
status = StatusModulo.Alerta,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
r.Controle = new AgroBase.Models.Operacoes.OperacaoParametrosControleModel()
|
r.Controle = new AgroBase.Models.Operacoes.OperacaoParametrosControleModel()
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,7 @@ namespace OperationControl.Models
|
||||||
return Variaveis.UdpChannel.SendBurstAsync(0x01, msg.ToBytes(), count: 6, intervalMs: 25, requestAck: false);
|
return Variaveis.UdpChannel.SendBurstAsync(0x01, msg.ToBytes(), count: 6, intervalMs: 25, requestAck: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnviarComandoReferenciamento(string mod_id = null)
|
public static void EnviarComandoReferenciamento(string? mod_id = null)
|
||||||
{
|
{
|
||||||
EnviarDadosControle(SelectedRoverId, new OperacaoComandoBaseModel()
|
EnviarDadosControle(SelectedRoverId, new OperacaoComandoBaseModel()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
|
||||||
LimparTeclas();
|
LimparTeclas();
|
||||||
EnviarNeutros();
|
EnviarNeutros();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco.IP, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -750,8 +750,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento.Diagnostico
|
||||||
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", "%", 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:
|
||||||
|
|
@ -768,6 +767,14 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento.Diagnostico
|
||||||
AddOrAppendSerie("Corrente", "A", bat?.CorrenteInstantanea);
|
AddOrAppendSerie("Corrente", "A", bat?.CorrenteInstantanea);
|
||||||
AddOrAppendSerie("Temperatura", "°C", bat?.Temperatura);
|
AddOrAppendSerie("Temperatura", "°C", bat?.Temperatura);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_Code.Imu:
|
||||||
|
var imu = rover.Imu;
|
||||||
|
AddOrAppendSerie("Movimento", "", imu?.IndiceMovimento);
|
||||||
|
AddOrAppendSerie("Estabilidade", "", imu?.IndiceEstabilidade);
|
||||||
|
AddOrAppendSerie("Rugosidade", "", imu?.IndiceRugosidade);
|
||||||
|
AddOrAppendSerie("Impacto", "", imu?.IndiceImpacto);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
|
||||||
public event Action<TipoFrameCamera>? CameraErvasFrameTipoAlterado;
|
public event Action<TipoFrameCamera>? CameraErvasFrameTipoAlterado;
|
||||||
|
|
||||||
public ICommand RetornoBaseCommand { get; }
|
public ICommand RetornoBaseCommand { get; }
|
||||||
|
public ICommand ReferenciamentoCommand { get; }
|
||||||
|
|
||||||
public MonitoramentoViewModel()
|
public MonitoramentoViewModel()
|
||||||
{
|
{
|
||||||
|
|
@ -123,6 +124,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
|
||||||
CameraErvasFrameTipoSelecionado = CameraErvasFrameTipos.FirstOrDefault();
|
CameraErvasFrameTipoSelecionado = CameraErvasFrameTipos.FirstOrDefault();
|
||||||
|
|
||||||
RetornoBaseCommand = new RelayCommand(_ => ExecutarRetornoBase(), _ => PodeExecutarRetornoBase());
|
RetornoBaseCommand = new RelayCommand(_ => ExecutarRetornoBase(), _ => PodeExecutarRetornoBase());
|
||||||
|
ReferenciamentoCommand = new RelayCommand(_ => ExecutarReferenciamento(), _ => PodeExecutarReferenciamento());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AtualizarDadosCameras(CameraWorkerItemModel? snr, CameraWorkerItemModel? cam, StatusCarroMapa? status_carro, double? pct_ervas)
|
public void AtualizarDadosCameras(CameraWorkerItemModel? snr, CameraWorkerItemModel? cam, StatusCarroMapa? status_carro, double? pct_ervas)
|
||||||
|
|
@ -165,6 +167,19 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExecutarReferenciamento()
|
||||||
|
{
|
||||||
|
if (System.Windows.MessageBox.Show("Deseja executar o referenciamento do equipamento?", "Referenciamento", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
VariaveisControleOperacao.EnviarComandoReferenciamento();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool PodeExecutarReferenciamento()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
|
|
|
||||||
|
|
@ -209,37 +209,45 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
if (ind.status == StatusModulo.Operante)
|
if (ind.status == StatusModulo.Operante)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool mandatorio = ind.em_uso;
|
||||||
|
|
||||||
|
if (mandatorio)
|
||||||
|
{
|
||||||
alertasEsperados.Add(new AlertaModel
|
alertasEsperados.Add(new AlertaModel
|
||||||
{
|
{
|
||||||
Rover_ID = rover.RoverId,
|
Rover_ID = rover.RoverId,
|
||||||
Modulo = mod.modulo,
|
Modulo = mod.modulo,
|
||||||
Mod_ID = ind.label,
|
Mod_ID = ind.label,
|
||||||
Severidade = DeParaStatus(ind.status, ind.em_uso),
|
Severidade = DeParaStatus(ind.status, mandatorio),
|
||||||
Mensagem = string.Join(", ", ind.motivos ?? new List<string>())
|
Mensagem = string.Join(", ", ind.motivos ?? new List<string>())
|
||||||
// NÃO define Timestamp aqui ainda
|
// NÃO define Timestamp aqui ainda
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mod.status == StatusModulo.Operante)
|
if (mod.status == StatusModulo.Operante)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool mandatorio = rover.ModulosMandatorios?.Any(x =>
|
bool mandatorio = rover.ModulosMandatorios?.Any(x =>
|
||||||
x.Dispositivo == mod.modulo && x.Mandatorio) ?? false;
|
x.Dispositivo == mod.modulo && x.Utilizar) ?? false;
|
||||||
|
|
||||||
|
if (mandatorio)
|
||||||
|
{
|
||||||
alertasEsperados.Add(new AlertaModel
|
alertasEsperados.Add(new AlertaModel
|
||||||
{
|
{
|
||||||
Rover_ID = rover.RoverId,
|
Rover_ID = rover.RoverId,
|
||||||
Modulo = mod.modulo,
|
Modulo = mod.modulo,
|
||||||
Mod_ID = null,
|
Mod_ID = null,
|
||||||
Severidade = DeParaStatus(mod.status, mandatorio),
|
Severidade = DeParaStatus(mod.status, mandatorio),
|
||||||
Mensagem = string.Join(", ", mod.motivos ?? new List<string>())
|
Mensagem = string.Join(", ", mod.motivos ?? new List<string>() { "Motivo desconhecido" })
|
||||||
// NÃO define Timestamp aqui ainda
|
// NÃO define Timestamp aqui ainda
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var mapaEsperado = alertasEsperados.ToDictionary(
|
var mapaEsperado = alertasEsperados.ToDictionary(
|
||||||
x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID),
|
x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using Brush = System.Windows.Media.Brush;
|
||||||
using Brushes = System.Windows.Media.Brushes;
|
using Brushes = System.Windows.Media.Brushes;
|
||||||
using SolidColorBrush = System.Windows.Media.SolidColorBrush;
|
using SolidColorBrush = System.Windows.Media.SolidColorBrush;
|
||||||
using Color = System.Windows.Media.Color;
|
using Color = System.Windows.Media.Color;
|
||||||
|
using AgroBase.Models.Operacoes;
|
||||||
|
|
||||||
namespace OperationControl.ViewModels.Views.Operacao
|
namespace OperationControl.ViewModels.Views.Operacao
|
||||||
{
|
{
|
||||||
|
|
@ -191,6 +192,21 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _distanciaBase = "--";
|
||||||
|
public string DistanciaBase
|
||||||
|
{
|
||||||
|
get => DistanciaOuTracos(_distanciaBase);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_distanciaBase != value)
|
||||||
|
{
|
||||||
|
_distanciaBase = value;
|
||||||
|
OnPropertyChanged(nameof(DistanciaBase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Brush CorTemperatura =>
|
public Brush CorTemperatura =>
|
||||||
!RoverSelecionado ? Brushes.Gray :
|
!RoverSelecionado ? Brushes.Gray :
|
||||||
_temperaturaNumerica >= 55 ? Brushes.OrangeRed :
|
_temperaturaNumerica >= 55 ? Brushes.OrangeRed :
|
||||||
|
|
@ -217,9 +233,19 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
!RoverSelecionado ? Brushes.Gray :
|
!RoverSelecionado ? Brushes.Gray :
|
||||||
CorPorTexto(_saudeGnss);
|
CorPorTexto(_saudeGnss);
|
||||||
|
|
||||||
|
public Brush CorDistBase =>
|
||||||
|
!RoverSelecionado ? Brushes.Gray :
|
||||||
|
_distanciaBaseNumerico <= 50 ? Brushes.LimeGreen :
|
||||||
|
_distanciaBaseNumerico <= 150 ? Brushes.Green :
|
||||||
|
_distanciaBaseNumerico <= 400 ? Brushes.YellowGreen :
|
||||||
|
_distanciaBaseNumerico <= 800 ? Brushes.Gold :
|
||||||
|
_distanciaBaseNumerico <= 1200 ? Brushes.OrangeRed :
|
||||||
|
Brushes.DarkRed;
|
||||||
|
|
||||||
private double _temperaturaNumerica;
|
private double _temperaturaNumerica;
|
||||||
private double _bateriaNumerica;
|
private double _bateriaNumerica;
|
||||||
private double _reservatorioNumerico;
|
private double _reservatorioNumerico;
|
||||||
|
private double _distanciaBaseNumerico;
|
||||||
|
|
||||||
public OperacaoLeftViewModel()
|
public OperacaoLeftViewModel()
|
||||||
{
|
{
|
||||||
|
|
@ -237,6 +263,7 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
_temperaturaNumerica = 0;
|
_temperaturaNumerica = 0;
|
||||||
_bateriaNumerica = 0;
|
_bateriaNumerica = 0;
|
||||||
_reservatorioNumerico = 0;
|
_reservatorioNumerico = 0;
|
||||||
|
_distanciaBaseNumerico = 0;
|
||||||
|
|
||||||
Velocidade = "--";
|
Velocidade = "--";
|
||||||
AnguloDirecional = "--";
|
AnguloDirecional = "--";
|
||||||
|
|
@ -246,31 +273,35 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
Reservatorio = "--";
|
Reservatorio = "--";
|
||||||
SaudeRede = "--";
|
SaudeRede = "--";
|
||||||
SaudeGnss = "--";
|
SaudeGnss = "--";
|
||||||
|
DistanciaBase = "--";
|
||||||
|
|
||||||
OnPropertyChanged(nameof(CorTemperatura));
|
OnPropertyChanged(nameof(CorTemperatura));
|
||||||
OnPropertyChanged(nameof(CorBateria));
|
OnPropertyChanged(nameof(CorBateria));
|
||||||
OnPropertyChanged(nameof(CorReservatorio));
|
OnPropertyChanged(nameof(CorReservatorio));
|
||||||
OnPropertyChanged(nameof(CorRede));
|
OnPropertyChanged(nameof(CorRede));
|
||||||
OnPropertyChanged(nameof(CorGnss));
|
OnPropertyChanged(nameof(CorGnss));
|
||||||
|
OnPropertyChanged(nameof(CorDistBase));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AtualizarRover(string nomeRover, double velocidadeKmh, double anguloDirecionalGraus, TipoMovimentoDirecional tipoMovimento, double temperaturaC, double bateriaPct, double reservatorioPct, string saudeRede, string saudeGnss, bool op_iniciada)
|
public void AtualizarRover(string descricao, OperacaoParametrosDadosControleModel? controle, double temperaturaC, double bateriaPct, double reservatorioPct, string saudeRede, string saudeGnss, double distanciaBase, bool op_iniciada)
|
||||||
{
|
{
|
||||||
RoverSelecionado = true;
|
RoverSelecionado = true;
|
||||||
NomeRover = nomeRover;
|
NomeRover = descricao;
|
||||||
|
|
||||||
_temperaturaNumerica = temperaturaC;
|
_temperaturaNumerica = temperaturaC;
|
||||||
_bateriaNumerica = bateriaPct;
|
_bateriaNumerica = bateriaPct;
|
||||||
_reservatorioNumerico = reservatorioPct;
|
_reservatorioNumerico = reservatorioPct;
|
||||||
|
_distanciaBaseNumerico = distanciaBase;
|
||||||
|
|
||||||
Velocidade = velocidadeKmh.ToString("0.0");
|
Velocidade = $"{controle?.PercentualVelocidadeSPKmh ?? 0:F2}";
|
||||||
AnguloDirecional = anguloDirecionalGraus.ToString("0.0");
|
AnguloDirecional = $"{controle?.Angulo ?? 0:F1}";
|
||||||
TipoMovimento = tipoMovimento;
|
TipoMovimento = controle?.TipoMovimento ?? TipoMovimentoDirecional.RodasDianteiras;
|
||||||
Temperatura = temperaturaC.ToString("0.0");
|
Temperatura = temperaturaC.ToString("0.0");
|
||||||
Bateria = bateriaPct.ToString("0");
|
Bateria = bateriaPct.ToString("0");
|
||||||
Reservatorio = reservatorioPct.ToString("0");
|
Reservatorio = reservatorioPct.ToString("0");
|
||||||
SaudeRede = saudeRede;
|
SaudeRede = saudeRede;
|
||||||
SaudeGnss = saudeGnss;
|
SaudeGnss = saudeGnss;
|
||||||
|
DistanciaBase = $"{distanciaBase:F2}";
|
||||||
|
|
||||||
OperacaoEmExecucao = op_iniciada;
|
OperacaoEmExecucao = op_iniciada;
|
||||||
|
|
||||||
|
|
@ -279,6 +310,7 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
OnPropertyChanged(nameof(CorReservatorio));
|
OnPropertyChanged(nameof(CorReservatorio));
|
||||||
OnPropertyChanged(nameof(CorRede));
|
OnPropertyChanged(nameof(CorRede));
|
||||||
OnPropertyChanged(nameof(CorGnss));
|
OnPropertyChanged(nameof(CorGnss));
|
||||||
|
OnPropertyChanged(nameof(CorDistBase));
|
||||||
OnPropertyChanged(nameof(OperacaoEmExecucao));
|
OnPropertyChanged(nameof(OperacaoEmExecucao));
|
||||||
OnPropertyChanged(nameof(TextoBotaoOperacao));
|
OnPropertyChanged(nameof(TextoBotaoOperacao));
|
||||||
OnPropertyChanged(nameof(CorBotaoOperacao));
|
OnPropertyChanged(nameof(CorBotaoOperacao));
|
||||||
|
|
@ -331,6 +363,7 @@ namespace OperationControl.ViewModels.Views.Operacao
|
||||||
private string VelocidadeValor(string valor) => RoverSelecionado ? $"{valor} km/h" : "--";
|
private string VelocidadeValor(string valor) => RoverSelecionado ? $"{valor} km/h" : "--";
|
||||||
private string TemperaturaValor(string valor) => RoverSelecionado ? $"{valor} °C" : "--";
|
private string TemperaturaValor(string valor) => RoverSelecionado ? $"{valor} °C" : "--";
|
||||||
private string PercentualOuTracos(string valor) => RoverSelecionado ? $"{valor}%" : "--";
|
private string PercentualOuTracos(string valor) => RoverSelecionado ? $"{valor}%" : "--";
|
||||||
|
private string DistanciaOuTracos(string valor) => RoverSelecionado ? $"{valor} m" : "--";
|
||||||
|
|
||||||
private Brush CorPorTexto(string valor)
|
private Brush CorPorTexto(string valor)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -372,7 +372,7 @@ namespace OperationControl.ViewModels
|
||||||
var eventos = new List<MotivoTopBarModel>();
|
var eventos = new List<MotivoTopBarModel>();
|
||||||
|
|
||||||
double tempoSemResposta = (DateTime.Now - rover.UltimoContato).TotalSeconds;
|
double tempoSemResposta = (DateTime.Now - rover.UltimoContato).TotalSeconds;
|
||||||
bool semComunicacao = tempoSemResposta >= VariaveisControleOperacao.TempoRoverVivo;
|
bool semComunicacao = !AppShell.Mock && tempoSemResposta >= VariaveisControleOperacao.TempoRoverVivo;
|
||||||
|
|
||||||
if (semComunicacao)
|
if (semComunicacao)
|
||||||
{
|
{
|
||||||
|
|
@ -394,7 +394,7 @@ namespace OperationControl.ViewModels
|
||||||
eventos.Add(new MotivoTopBarModel
|
eventos.Add(new MotivoTopBarModel
|
||||||
{
|
{
|
||||||
Prioridade = 1100,
|
Prioridade = 1100,
|
||||||
Fonte = "Núcleo Central de Processamento",
|
Fonte = "NCP",
|
||||||
Titulo = "NÚCLEO CENTRAL DE PROCESSAMENTO DESCONECTADO",
|
Titulo = "NÚCLEO CENTRAL DE PROCESSAMENTO DESCONECTADO",
|
||||||
Descricao = "O núcleo central de processamento foi desconectado, tentando reconectar...",
|
Descricao = "O núcleo central de processamento foi desconectado, tentando reconectar...",
|
||||||
StatusVisual = StatusModulo.Falha
|
StatusVisual = StatusModulo.Falha
|
||||||
|
|
@ -475,13 +475,35 @@ namespace OperationControl.ViewModels
|
||||||
// 5) Módulos com condições operacionais críticas
|
// 5) Módulos com condições operacionais críticas
|
||||||
// ============================
|
// ============================
|
||||||
var modulosCriticos = obj.ModulosSaude?
|
var modulosCriticos = obj.ModulosSaude?
|
||||||
.Where(mod => mod?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false)
|
.Where(mod =>
|
||||||
|
(mod?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false) ||
|
||||||
|
(
|
||||||
|
!new List<StatusModulo>() { StatusModulo.Operante, StatusModulo.Alerta }.Contains(mod?.status ?? StatusModulo.Desconectado) &&
|
||||||
|
(rover?.ModulosMandatorios?.Any(x =>
|
||||||
|
(x?.Dispositivo ?? T_Code.Vzo) == (mod?.modulo ?? T_Code.Vzo) &&
|
||||||
|
(x?.Utilizar ?? false) &&
|
||||||
|
(x?.Mandatorio ?? false)
|
||||||
|
) ?? false)
|
||||||
|
)
|
||||||
|
)
|
||||||
.ToList() ?? new List<AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel>();
|
.ToList() ?? new List<AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel>();
|
||||||
|
|
||||||
foreach (var modulo in modulosCriticos)
|
foreach (var modulo in modulosCriticos)
|
||||||
{
|
{
|
||||||
var nomeModulo = (modulo?.modulo ?? T_Code.Vzo).ToString();
|
var nomeModulo = (modulo?.modulo ?? T_Code.Vzo).ToString();
|
||||||
|
|
||||||
|
bool temCondicaoSevera = modulo?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false;
|
||||||
|
bool moduloMandatorioNaoOperante =
|
||||||
|
!new List<StatusModulo>() { StatusModulo.Operante, StatusModulo.Alerta }.Contains(modulo?.status ?? StatusModulo.Desconectado) &&
|
||||||
|
(rover?.ModulosMandatorios?.Any(x =>
|
||||||
|
(x?.Dispositivo ?? T_Code.Vzo) == (modulo?.modulo ?? T_Code.Vzo) &&
|
||||||
|
(x?.Utilizar ?? false) &&
|
||||||
|
(x?.Mandatorio ?? false)
|
||||||
|
) ?? false);
|
||||||
|
|
||||||
|
// Caso 1: condições operacionais severas
|
||||||
|
if (temCondicaoSevera)
|
||||||
|
{
|
||||||
var descricoesCriticas = modulo?.condicoes_operacionais?
|
var descricoesCriticas = modulo?.condicoes_operacionais?
|
||||||
.Where(cond => cond?.severidade > 90)
|
.Where(cond => cond?.severidade > 90)
|
||||||
.Select(cond => cond?.descricao ?? "")
|
.Select(cond => cond?.descricao ?? "")
|
||||||
|
|
@ -503,13 +525,32 @@ namespace OperationControl.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Caso 2: módulo mandatório não operante
|
||||||
|
if (moduloMandatorioNaoOperante)
|
||||||
|
{
|
||||||
|
var statusTexto = (modulo?.status ?? StatusModulo.Desconectado).ToString();
|
||||||
|
|
||||||
|
eventos.Add(new MotivoTopBarModel
|
||||||
|
{
|
||||||
|
Prioridade = 610,
|
||||||
|
Fonte = "ModuloCritico",
|
||||||
|
Titulo = "MÓDULO MANDATÓRIO NÃO OPERACIONAL",
|
||||||
|
Descricao = $"{nomeModulo}: módulo mandatório em estado '{statusTexto}'",
|
||||||
|
StatusVisual = StatusModulo.Falha,
|
||||||
|
Modulo = modulo?.modulo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var ordemModulos = new Dictionary<T_Code, int>
|
var ordemModulos = new Dictionary<T_Code, int>
|
||||||
{
|
{
|
||||||
{ T_Code.Ipb, 1 },
|
{ T_Code.Ipb, 1 },
|
||||||
{ T_Code.Npc, 2 },
|
{ T_Code.Npc, 2 },
|
||||||
{ T_Code.Bat, 3 },
|
{ T_Code.Can, 3 },
|
||||||
{ T_Code.Atu, 4 },
|
{ T_Code.Bat, 4 },
|
||||||
{ T_Code.Sen, 5 }
|
{ T_Code.Gps, 5 },
|
||||||
|
{ T_Code.Atu, 6 },
|
||||||
|
{ T_Code.Sen, 7 }
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
|
|
@ -590,7 +631,7 @@ namespace OperationControl.ViewModels
|
||||||
if (string.IsNullOrWhiteSpace(linha2))
|
if (string.IsNullOrWhiteSpace(linha2))
|
||||||
linha2 = "Sem detalhes adicionais.";
|
linha2 = "Sem detalhes adicionais.";
|
||||||
|
|
||||||
Variaveis.MostrarLog($"LINHA 1: {linha1}");
|
Models.Variaveis.MostrarLog($"LINHA 1: {linha1}");
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// 8) Atualiza UI
|
// 8) Atualiza UI
|
||||||
|
|
@ -619,7 +660,7 @@ namespace OperationControl.ViewModels
|
||||||
|
|
||||||
public void AtualizarDadosGnss(AgroBase.Models.GPSModel dados)
|
public void AtualizarDadosGnss(AgroBase.Models.GPSModel dados)
|
||||||
{
|
{
|
||||||
var bf = Variaveis.GpsService.BaseFix;
|
var bf = Models.Variaveis.GpsService.BaseFix;
|
||||||
if (bf.CorrecaoEmAndamento)
|
if (bf.CorrecaoEmAndamento)
|
||||||
{
|
{
|
||||||
AtualizarProgressoFixacaoBase(bf.Progresso, bf.ProgressoStr);
|
AtualizarProgressoFixacaoBase(bf.Progresso, bf.ProgressoStr);
|
||||||
|
|
@ -805,7 +846,7 @@ namespace OperationControl.ViewModels
|
||||||
|
|
||||||
// ESQUERDA
|
// ESQUERDA
|
||||||
var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
|
var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
|
||||||
double c = conexao?.saude ?? 0;
|
double saude_rede = conexao?.saude ?? 0;
|
||||||
double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value<double?>() ?? 0.0;
|
double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value<double?>() ?? 0.0;
|
||||||
double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value<double?>() ?? 0.0;
|
double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value<double?>() ?? 0.0;
|
||||||
double bwTot = conexao?.detalhes?["bw_total_mbps"]?.Value<double?>() ?? 0.0;
|
double bwTot = conexao?.detalhes?["bw_total_mbps"]?.Value<double?>() ?? 0.0;
|
||||||
|
|
@ -814,14 +855,13 @@ namespace OperationControl.ViewModels
|
||||||
|
|
||||||
_viewOperacaoLeft?._vm?.AtualizarRover(
|
_viewOperacaoLeft?._vm?.AtualizarRover(
|
||||||
rover?.Descricao ?? "",
|
rover?.Descricao ?? "",
|
||||||
obj.Controle?.PercentualVelocidadeSPKmh ?? 0,
|
obj.Controle,
|
||||||
obj.Controle?.Angulo ?? 0,
|
|
||||||
obj.Controle?.TipoMovimento ?? TipoMovimentoDirecional.Diagnostico,
|
|
||||||
obj.Refrigeracao?.Temperatura ?? 0,
|
obj.Refrigeracao?.Temperatura ?? 0,
|
||||||
obj.Bateria?.PercentualBateria ?? 0,
|
obj.Bateria?.PercentualBateria ?? 0,
|
||||||
obj.Atuador?.PercentualReservatorio ?? 0,
|
obj.Atuador?.PercentualReservatorio ?? 0,
|
||||||
$"{c}%",
|
$"{saude_rede}%",
|
||||||
$"{obj.Gnss?.QualidadeFix ?? TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm",
|
$"{obj.Gnss?.QualidadeFix ?? TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm",
|
||||||
|
AgroBase.Models.GPSUtils.DistanciaEntrePontos(new AgroBase.Models.GPSModel() { Latitude = obj.Gnss?.Latitude ?? 0, Longitude = obj.Gnss?.Longitude ?? 0 }, Models.Variaveis.GpsService.UltimaLeitura),
|
||||||
obj.Operacao?.Iniciada ?? false
|
obj.Operacao?.Iniciada ?? false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue