ajustes no imu, filtro temperatura motor, alarmes npc, offset temperatura sensoriamento, metodo que abre curva ao final do corredor
This commit is contained in:
parent
6242d8b3dc
commit
742aac3f5d
|
|
@ -232,7 +232,8 @@ namespace AgroBase.Models.Modules
|
|||
maximo = 80,
|
||||
minimo = 5,
|
||||
nominal = 25,
|
||||
unidade_medida = "ºC"
|
||||
unidade_medida = "ºC",
|
||||
offset = -39.9,
|
||||
},
|
||||
new SensorValoresLeituraModel()
|
||||
{
|
||||
|
|
@ -288,7 +289,8 @@ namespace AgroBase.Models.Modules
|
|||
maximo = 80,
|
||||
minimo = 5,
|
||||
nominal = 25,
|
||||
unidade_medida = "ºC"
|
||||
unidade_medida = "ºC",
|
||||
offset = -0.5,
|
||||
},
|
||||
new SensorValoresLeituraModel()
|
||||
{
|
||||
|
|
@ -344,7 +346,8 @@ namespace AgroBase.Models.Modules
|
|||
maximo = 80,
|
||||
minimo = 5,
|
||||
nominal = 25,
|
||||
unidade_medida = "ºC"
|
||||
unidade_medida = "ºC",
|
||||
offset = -15.9,
|
||||
},
|
||||
new SensorValoresLeituraModel()
|
||||
{
|
||||
|
|
@ -400,7 +403,8 @@ namespace AgroBase.Models.Modules
|
|||
maximo = 80,
|
||||
minimo = 5,
|
||||
nominal = 25,
|
||||
unidade_medida = "ºC"
|
||||
unidade_medida = "ºC",
|
||||
offset = -3.6,
|
||||
},
|
||||
new SensorValoresLeituraModel()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace AgroBase.Models
|
|||
public AutonomiaCorredorModel AutonomiaCorredor { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private GPSModel GPSPosicaoAtual => Variaveis.OperacaoEmAndamento.Sensoriamento?.Gps?.Clone();
|
||||
private GPSModel GPSPosicaoAtual => GPSService.UltimaLeitura?.Clone();
|
||||
|
||||
[JsonProperty]
|
||||
public double TempoEntreLeituras { get; private set; }
|
||||
|
|
@ -2155,8 +2155,6 @@ namespace AgroBase.Models
|
|||
int idxEsq = 0;
|
||||
int idxDir = 0;
|
||||
|
||||
double anguloAcrescentar = direcaoAtual == DirecaoCarroRua.Ida ? AnguloAberturaCurva : -AnguloAberturaCurva;
|
||||
|
||||
for (int idx = 0; idx < Corredores.Count(); idx++)
|
||||
{
|
||||
double _FatorLarguraCorredor = CorredoresLarguras[idx] / LarguraCorredorPadrao;
|
||||
|
|
@ -2282,15 +2280,29 @@ namespace AgroBase.Models
|
|||
_trajetoriaFixa.Add(PontoTrajetoria);
|
||||
}
|
||||
|
||||
double anguloProjetar2 = GPSUtils.CalcularOrientacao(CorredorAtual.Skip(CorredorAtual.Count() - 2).First(), CorredorAtual.Last());
|
||||
double percentualAcrescimoPontoAberturaCurva = 0.8;
|
||||
double distancia_projetar = !ultimoCorredor ? (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva) : (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva);
|
||||
|
||||
GPSModel ultimoPontoCorredorAtual = CorredorAtual.Last();
|
||||
double anguloProjetar2 = GPSUtils.CalcularOrientacao(CorredorAtual.Skip(CorredorAtual.Count() - 2).First(), ultimoPontoCorredorAtual);
|
||||
GPSModel UltimoPonto = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2);
|
||||
|
||||
// Realiza um acréscimo no angulo à projetar, permitindo que o equipamento faça uma curva mais aberta entre corredores
|
||||
if (!ultimoCorredor && !_FatorLarguraElevado)
|
||||
{
|
||||
anguloProjetar2 += anguloAcrescentar;
|
||||
anguloAcrescentar *= -1;
|
||||
GPSModel up_pos = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2 + AnguloAberturaCurva);
|
||||
GPSModel up_neg = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2 - AnguloAberturaCurva);
|
||||
|
||||
GPSModel pp_proximoCorredor = direcaoAtual == DirecaoCarroRua.Volta ? Corredores[idx + 1].FirstOrDefault() : Corredores[idx + 1].LastOrDefault();
|
||||
|
||||
double d_pos = GPSUtils.DistanciaEntrePontos(pp_proximoCorredor, up_pos);
|
||||
double d_neg = GPSUtils.DistanciaEntrePontos(pp_proximoCorredor, up_neg);
|
||||
|
||||
if (d_pos > d_neg)
|
||||
UltimoPonto = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2 + AnguloAberturaCurva);
|
||||
else
|
||||
UltimoPonto = GPSUtils.ProjetarPontoDeslocado(ultimoPontoCorredorAtual, distancia_projetar, anguloProjetar2 - AnguloAberturaCurva);
|
||||
}
|
||||
double percentualAcrescimoPontoAberturaCurva = 0.8;
|
||||
GPSModel UltimoPonto = GPSUtils.ProjetarPontoDeslocado(CorredorAtual.Last(), !ultimoCorredor ? (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva) : (DistanciaProjecaoRua * percentualAcrescimoPontoAberturaCurva), anguloProjetar2);
|
||||
|
||||
PontoTrajetoriaModel PontoFinal = new PontoTrajetoriaModel(TipoPontoRua.LigacaoSaida)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,8 +86,13 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
self.calibrado = False
|
||||
self.calibrando = False
|
||||
self.calib_inicio_ts = 0.0
|
||||
self.calib_primeira_amostra_quat_ts = None
|
||||
self.calib_min_amostras_quat = max(30, int(self.freq * 0.50))
|
||||
self.calib_max_std_deg = 1.50
|
||||
self.calib_quats_xyzw = []
|
||||
self.q_ref_inv = None
|
||||
self.rotation_accuracy_rad = None
|
||||
self.rotation_accuracy_max_rad = math.radians(20.0)
|
||||
|
||||
# Estado angular
|
||||
self.roll_deg = 0.0
|
||||
|
|
@ -176,7 +181,9 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
|
||||
self.calibrado = False
|
||||
self.calibrando = True
|
||||
self.calib_inicio_ts = time.perf_counter()
|
||||
# A janela útil começa somente quando chegar a primeira amostra válida.
|
||||
self.calib_inicio_ts = 0.0
|
||||
self.calib_primeira_amostra_quat_ts = None
|
||||
|
||||
self.calib_quats_xyzw.clear()
|
||||
self._calib_roll_hist.clear()
|
||||
|
|
@ -580,7 +587,12 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
if not self.calibrando:
|
||||
return
|
||||
|
||||
self.calib_quats_xyzw.append(q_xyzw)
|
||||
agora = time.perf_counter()
|
||||
if self.calib_primeira_amostra_quat_ts is None:
|
||||
self.calib_primeira_amostra_quat_ts = agora
|
||||
self.calib_inicio_ts = agora
|
||||
|
||||
self.calib_quats_xyzw.append(np.asarray(q_xyzw, dtype=np.float64))
|
||||
|
||||
try:
|
||||
roll_abs, pitch_abs, _ = R.from_quat(q_xyzw).as_euler("xyz", degrees=True)
|
||||
|
|
@ -589,9 +601,28 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
elapsed = time.perf_counter() - self.calib_inicio_ts
|
||||
elapsed = agora - self.calib_primeira_amostra_quat_ts
|
||||
|
||||
if elapsed < self.calibracao_segundos:
|
||||
if (
|
||||
elapsed < self.calibracao_segundos
|
||||
or len(self.calib_quats_xyzw) < self.calib_min_amostras_quat
|
||||
):
|
||||
return
|
||||
|
||||
roll_std = float(np.std(self._calib_roll_hist)) if self._calib_roll_hist else 999.0
|
||||
pitch_std = float(np.std(self._calib_pitch_hist)) if self._calib_pitch_hist else 999.0
|
||||
|
||||
# Não aceita como zero uma janela em que o rover/câmera se moveu.
|
||||
if roll_std > self.calib_max_std_deg or pitch_std > self.calib_max_std_deg:
|
||||
self.mostrar_log(
|
||||
f"Calibração rejeitada por movimento | std_roll={roll_std:.3f}° | "
|
||||
f"std_pitch={pitch_std:.3f}°. Reiniciando janela."
|
||||
)
|
||||
self.calib_quats_xyzw.clear()
|
||||
self._calib_roll_hist.clear()
|
||||
self._calib_pitch_hist.clear()
|
||||
self.calib_primeira_amostra_quat_ts = None
|
||||
self.calib_inicio_ts = 0.0
|
||||
return
|
||||
|
||||
q_ref = self._media_quaternion_xyzw(self.calib_quats_xyzw)
|
||||
|
|
@ -607,9 +638,6 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
self.calibrando = False
|
||||
self.calibrado = True
|
||||
|
||||
roll_std = float(np.std(self._calib_roll_hist)) if self._calib_roll_hist else 0.0
|
||||
pitch_std = float(np.std(self._calib_pitch_hist)) if self._calib_pitch_hist else 0.0
|
||||
|
||||
self.mostrar_log(
|
||||
f"Calibração concluída | amostras={len(self.calib_quats_xyzw)} | "
|
||||
f"std_roll={roll_std:.3f}° | std_pitch={pitch_std:.3f}°"
|
||||
|
|
@ -762,7 +790,6 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
if q_xyzw is None:
|
||||
return False
|
||||
|
||||
agora = time.perf_counter()
|
||||
agora_wall = time.time()
|
||||
|
||||
self.last_packet_ts = agora_wall
|
||||
|
|
@ -770,6 +797,8 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
|
||||
self._processar_calibracao(q_xyzw)
|
||||
|
||||
self.rotation_accuracy_rad = accuracy_rad
|
||||
|
||||
roll, pitch, yaw = self._quat_relativo_para_euler(q_xyzw)
|
||||
|
||||
self.roll_deg = roll
|
||||
|
|
@ -825,6 +854,11 @@ class IMUCamera(ModuloDiagnosticoBase):
|
|||
|
||||
# Metadados do rotation vector.
|
||||
"rotation_accuracy_rad": accuracy_rad,
|
||||
"rotation_accuracy_ok": bool(
|
||||
accuracy_rad is not None
|
||||
and math.isfinite(float(accuracy_rad))
|
||||
and 0.0 <= float(accuracy_rad) <= self.rotation_accuracy_max_rad
|
||||
),
|
||||
|
||||
# Saúde operacional básica.
|
||||
"timestamp": agora_wall * 1000.0,
|
||||
|
|
|
|||
|
|
@ -808,17 +808,25 @@ class CameraMultispectral:
|
|||
)
|
||||
performance["device_metrics"] = metrics
|
||||
|
||||
temp = float(metrics.get("temperature_average_c", 0.0) or 0.0)
|
||||
temp_raw = metrics.get("temperature_average_c")
|
||||
if temp_raw is None:
|
||||
performance["temperatura"] = None
|
||||
motivos.append("Temperatura interna indisponível")
|
||||
saude -= 10
|
||||
else:
|
||||
temp = float(temp_raw)
|
||||
performance["temperatura"] = temp
|
||||
|
||||
if temp >= 80:
|
||||
if temp >= 100:
|
||||
motivos.append(f"Temperatura de emergência: {temp:.1f} °C")
|
||||
saude = min(saude, 10)
|
||||
elif temp >= 95:
|
||||
motivos.append(f"Temperatura crítica: {temp:.1f} °C")
|
||||
saude -= 30
|
||||
elif temp >= 70:
|
||||
saude = min(saude, 35)
|
||||
elif temp >= 85:
|
||||
motivos.append(f"Temperatura elevada: {temp:.1f} °C")
|
||||
saude -= 15
|
||||
elif temp >= 60:
|
||||
motivos.append(f"Temperatura acima do ideal: {temp:.1f} °C")
|
||||
elif temp >= 75:
|
||||
motivos.append(f"Aquecimento preventivo: {temp:.1f} °C")
|
||||
saude -= 5
|
||||
|
||||
ddr = float(metrics.get("ddr_used_bytes", 0) or 0) / 1024.0 / 1024.0
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class CameraOak:
|
|||
nome_sensor=f"{self.modelo}_imu",
|
||||
modo=self.imu_modo,
|
||||
gyro_unidade="rad_s",
|
||||
referencial_robo="r,p,y",
|
||||
referencial_robo="y,r,p",
|
||||
)
|
||||
|
||||
if self.modelo_ia_seg is not None:
|
||||
|
|
@ -634,14 +634,23 @@ class CameraOak:
|
|||
"velocidade": speed
|
||||
}
|
||||
# 🔸 Temperatura (> 60 °C começa a penalizar)
|
||||
if temp >= 80:
|
||||
if temp is None:
|
||||
performance["temperatura"] = None
|
||||
motivos.append("Temperatura interna indisponível")
|
||||
saude -= 10
|
||||
else:
|
||||
temp = float(temp)
|
||||
if temp >= 100:
|
||||
motivos.append(f"Temperatura de emergência: {temp:.1f} °C")
|
||||
saude = min(saude, 10)
|
||||
elif temp >= 95:
|
||||
motivos.append(f"Temperatura crítica: {temp:.1f} °C")
|
||||
saude -= 30
|
||||
elif temp >= 70:
|
||||
saude = min(saude, 35)
|
||||
elif temp >= 85:
|
||||
motivos.append(f"Temperatura elevada: {temp:.1f} °C")
|
||||
saude -= 15
|
||||
elif temp >= 60:
|
||||
motivos.append(f"Temperatura acima do ideal: {temp:.1f} °C")
|
||||
elif temp >= 75:
|
||||
motivos.append(f"Aquecimento preventivo: {temp:.1f} °C")
|
||||
saude -= 5
|
||||
|
||||
# 🔸 Memória DDR (> 300 MB começa a penalizar)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
timeout_imu_s=0.40,
|
||||
timeout_camera_s=2.0,
|
||||
max_disagreement_deg=4.0,
|
||||
rotation_accuracy_max_rad=0.35,
|
||||
|
||||
# Limites principais vindos do equipamento.
|
||||
# No rover:
|
||||
|
|
@ -68,6 +69,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
self.timeout_imu_s = float(timeout_imu_s)
|
||||
self.timeout_camera_s = float(timeout_camera_s)
|
||||
self.max_disagreement_deg = float(max_disagreement_deg)
|
||||
self.rotation_accuracy_max_rad = float(rotation_accuracy_max_rad)
|
||||
|
||||
# ============================================================
|
||||
# Limites angulares do equipamento
|
||||
|
|
@ -429,14 +431,13 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
# timestamp pode vir em ms.
|
||||
ts_s = ts_ms / 1000.0 if ts_ms > 100000000000.0 else ts_ms
|
||||
|
||||
candidatos_ts = [
|
||||
ts_s if ts_s > 0 else 0.0,
|
||||
last_packet_ts if last_packet_ts > 0 else 0.0,
|
||||
last_publish_ts if last_publish_ts > 0 else 0.0,
|
||||
]
|
||||
|
||||
ts_ref = max(candidatos_ts)
|
||||
idade_s = (agora - ts_ref) if ts_ref > 0 else 999.0
|
||||
# Todos os campos acima devem estar no relógio de parede (Unix epoch).
|
||||
# Não usa max() cegamente: um timestamp monotônico misturado ao epoch
|
||||
# pode esconder uma fonte velha ou produzir idade negativa.
|
||||
ts_ref = ts_s if ts_s > 0 else last_packet_ts
|
||||
if ts_ref <= 0:
|
||||
ts_ref = last_publish_ts
|
||||
idade_s = max(0.0, agora - ts_ref) if ts_ref > 0 else 999.0
|
||||
|
||||
roll = self._safe_float(imu, "roll", 0.0)
|
||||
pitch = self._safe_float(imu, "pitch", 0.0)
|
||||
|
|
@ -450,6 +451,9 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
pitch_rate = self._safe_float(imu, "pitch_rate_dps", 0.0)
|
||||
yaw_rate = self._safe_float(imu, "yaw_rate_dps", 0.0)
|
||||
|
||||
modo = str(imu.get("modo", "") or "").lower()
|
||||
accuracy_rad = self._safe_float(imu, "rotation_accuracy_rad", float("nan"))
|
||||
|
||||
imu_saude = None
|
||||
try:
|
||||
saude_obj = imu.get("saude")
|
||||
|
|
@ -491,6 +495,29 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
confiavel = False
|
||||
motivos.append(f"peso_base_baixo")
|
||||
|
||||
valores = (roll_filtrado, pitch_filtrado, yaw_filtrado,
|
||||
roll_rate, pitch_rate, yaw_rate)
|
||||
if not all(math.isfinite(v) for v in valores):
|
||||
confiavel = False
|
||||
motivos.append("imu_valor_nao_finito")
|
||||
|
||||
accuracy_score = 1.0
|
||||
if modo in ("rotation_vector", "game_rotation_vector"):
|
||||
if not math.isfinite(accuracy_rad) or accuracy_rad < 0.0:
|
||||
confiavel = False
|
||||
accuracy_score = 0.0
|
||||
motivos.append("rotation_accuracy_indisponivel")
|
||||
elif accuracy_rad > self.rotation_accuracy_max_rad:
|
||||
confiavel = False
|
||||
accuracy_score = 0.0
|
||||
motivos.append(f"rotation_accuracy_ruim_{accuracy_rad:.3f}rad")
|
||||
else:
|
||||
accuracy_score = self.clamp(
|
||||
1.0 - accuracy_rad / self.rotation_accuracy_max_rad,
|
||||
0.25,
|
||||
1.0,
|
||||
)
|
||||
|
||||
freshness = 1.0 - self.norm_to_100(idade_s, 0.0, self.timeout_imu_s) / 100.0
|
||||
freshness = self.clamp(freshness, 0.0, 1.0)
|
||||
|
||||
|
|
@ -501,7 +528,10 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
if imu_saude is not None:
|
||||
saude_score = self.clamp(imu_saude / 100.0, 0.0, 1.0)
|
||||
|
||||
confidence = peso_base * freshness * calib_score * valido_score * saude_score
|
||||
confidence = (
|
||||
peso_base * freshness * calib_score * valido_score
|
||||
* saude_score * accuracy_score
|
||||
)
|
||||
|
||||
if not confiavel:
|
||||
confidence = 0.0
|
||||
|
|
@ -525,9 +555,11 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
"confidence": float(confidence),
|
||||
"peso_base": float(peso_base),
|
||||
|
||||
"roll": float(roll),
|
||||
"pitch": float(pitch),
|
||||
"yaw": float(yaw),
|
||||
# O módulo final consome uma atitude coerente: os três ângulos
|
||||
# filtrados pertencem à mesma fonte e ao mesmo instante publicado.
|
||||
"roll": float(roll_filtrado),
|
||||
"pitch": float(pitch_filtrado),
|
||||
"yaw": float(yaw_filtrado),
|
||||
|
||||
"roll_filtrado": float(roll_filtrado),
|
||||
"pitch_filtrado": float(pitch_filtrado),
|
||||
|
|
@ -536,6 +568,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
"roll_rate_dps": float(roll_rate),
|
||||
"pitch_rate_dps": float(pitch_rate),
|
||||
"yaw_rate_dps": float(yaw_rate),
|
||||
"rotation_accuracy_rad": accuracy_rad if math.isfinite(accuracy_rad) else None,
|
||||
|
||||
"timestamp": ts_ms,
|
||||
"last_packet_ts": last_packet_ts,
|
||||
|
|
@ -790,11 +823,18 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
"divergentes": [],
|
||||
}
|
||||
|
||||
def maior_distancia_circular(valores):
|
||||
maior = 0.0
|
||||
for i, a in enumerate(valores):
|
||||
for b in valores[i + 1:]:
|
||||
maior = max(maior, abs(self._angle_delta_deg(a, b)))
|
||||
return float(maior)
|
||||
|
||||
rolls = [float(f["roll"]) for f in fontes]
|
||||
pitchs = [float(f["pitch"]) for f in fontes]
|
||||
|
||||
roll_dis = float(max(rolls) - min(rolls))
|
||||
pitch_dis = float(max(pitchs) - min(pitchs))
|
||||
roll_dis = maior_distancia_circular(rolls)
|
||||
pitch_dis = maior_distancia_circular(pitchs)
|
||||
max_dis = max(roll_dis, pitch_dis)
|
||||
|
||||
sensores_agree = max_dis <= self.max_disagreement_deg
|
||||
|
|
@ -817,17 +857,6 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
"divergentes": divergentes,
|
||||
}
|
||||
|
||||
def _valor_mais_conservador(self, fontes, key):
|
||||
fonte = max(
|
||||
fontes,
|
||||
key=lambda f: abs(float(f.get(key, 0.0))),
|
||||
)
|
||||
|
||||
return (
|
||||
float(fonte.get(key, 0.0)),
|
||||
fonte["mx_id"],
|
||||
)
|
||||
|
||||
def _fundir_fontes(self, fontes, sensores_agree=True):
|
||||
if not fontes:
|
||||
return None
|
||||
|
|
@ -879,41 +908,26 @@ class CameraIMU(ModuloDiagnosticoBase):
|
|||
"estrategia_fusao": "weighted_agreement",
|
||||
}
|
||||
|
||||
roll, fonte_roll = self._valor_mais_conservador(
|
||||
fontes,
|
||||
"roll",
|
||||
)
|
||||
pitch, fonte_pitch = self._valor_mais_conservador(
|
||||
fontes,
|
||||
"pitch",
|
||||
)
|
||||
roll_rate, fonte_roll_rate = self._valor_mais_conservador(
|
||||
fontes,
|
||||
"roll_rate_dps",
|
||||
)
|
||||
pitch_rate, fonte_pitch_rate = self._valor_mais_conservador(
|
||||
fontes,
|
||||
"pitch_rate_dps",
|
||||
)
|
||||
|
||||
# Em divergência, não cria uma orientação artificial misturando eixos.
|
||||
# Isola as fontes secundárias e usa o pacote completo da mais confiável.
|
||||
return {
|
||||
"roll": roll,
|
||||
"pitch": pitch,
|
||||
"roll": fonte_principal["roll"],
|
||||
"pitch": fonte_principal["pitch"],
|
||||
"yaw": fonte_principal["yaw"],
|
||||
"roll_rate_dps": roll_rate,
|
||||
"pitch_rate_dps": pitch_rate,
|
||||
"roll_rate_dps": fonte_principal["roll_rate_dps"],
|
||||
"pitch_rate_dps": fonte_principal["pitch_rate_dps"],
|
||||
"yaw_rate_dps": fonte_principal["yaw_rate_dps"],
|
||||
"confidence": min(
|
||||
float(fonte_principal["confidence"]),
|
||||
1.0,
|
||||
),
|
||||
"fonte_principal": fonte_principal["mx_id"],
|
||||
"fonte_roll": fonte_roll,
|
||||
"fonte_pitch": fonte_pitch,
|
||||
"fonte_roll_rate": fonte_roll_rate,
|
||||
"fonte_pitch_rate": fonte_pitch_rate,
|
||||
"fonte_roll": fonte_principal["mx_id"],
|
||||
"fonte_pitch": fonte_principal["mx_id"],
|
||||
"fonte_roll_rate": fonte_principal["mx_id"],
|
||||
"fonte_pitch_rate": fonte_principal["mx_id"],
|
||||
"single_source": False,
|
||||
"estrategia_fusao": "conservative_disagreement",
|
||||
"estrategia_fusao": "primary_source_on_disagreement",
|
||||
}
|
||||
|
||||
def _atualizar_filtro_fusao(self, fusao):
|
||||
|
|
|
|||
|
|
@ -47,6 +47,33 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
PENALIDADE_FREQ_MAX_PADRAO = 4
|
||||
PENALIDADE_FREQ_MAX_CRITICA = 8
|
||||
|
||||
# Temperatura MOV
|
||||
TEMP_ALERTA_C = 70.0
|
||||
TEMP_FALHA_C = 85.0
|
||||
TEMP_EMERGENCIA_C = 100.0
|
||||
|
||||
TEMP_RECUPERA_ALERTA_C = 67.0
|
||||
TEMP_RECUPERA_FALHA_C = 80.0
|
||||
TEMP_RECUPERA_EMERGENCIA_C = 90.0
|
||||
|
||||
TEMP_ALERTA_INTERMEDIARIO_S = 3.0
|
||||
TEMP_ALERTA_CONFIRMADO_S = 10.0
|
||||
|
||||
TEMP_FALHA_INTERMEDIARIO_S = 2.0
|
||||
TEMP_FALHA_CONFIRMADO_S = 5.0
|
||||
|
||||
TEMP_EMERGENCIA_CONFIRMADO_S = 1.0
|
||||
TEMP_RECUPERACAO_S = 10.0
|
||||
|
||||
# Elétricos
|
||||
TENSAO_FALHA_PERSISTENCIA_S = 2.0
|
||||
CORRENTE_MOTOR_FALHA_PERSISTENCIA_S = 1.0
|
||||
CORRENTE_BARRAMENTO_FALHA_PERSISTENCIA_S = 1.0
|
||||
|
||||
# Freio/comando
|
||||
FREIO_RPM_FALHA_PERSISTENCIA_S = 1.0
|
||||
ALARME_COMANDO_PERSISTENCIA_S = 2.0
|
||||
|
||||
def __init__(self):
|
||||
self.t_code = T_Code.Mov
|
||||
self.nome = "Movimentacao"
|
||||
|
|
@ -89,6 +116,8 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
19: ("Sinal Z não encontrado", 25, False),
|
||||
}
|
||||
|
||||
self._estados_persistencia = {}
|
||||
|
||||
def atualizar_saude(self):
|
||||
try:
|
||||
dados_mov = ContextoGlobalRedis.get_modulo(self.t_code) or {}
|
||||
|
|
@ -543,7 +572,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
|
||||
# Faixas absolutas. Ajustar depois com logs reais.
|
||||
if tensao > 0:
|
||||
if tensao < 24 or tensao > 50:
|
||||
if tensao < 25 or tensao > 45:
|
||||
motivos.append(f"Tensão MOV em faixa perigosa: {tensao:.1f} V")
|
||||
condicoes.append({
|
||||
"label": "Tensão MOV",
|
||||
|
|
@ -554,7 +583,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
})
|
||||
score -= 70
|
||||
risco_eletrico_termico = True
|
||||
elif tensao < 28 or tensao > 45:
|
||||
elif tensao < 30 or tensao > 43:
|
||||
motivos.append(f"Tensão MOV fora da faixa esperada: {tensao:.1f} V")
|
||||
condicoes.append({
|
||||
"label": "Tensão MOV",
|
||||
|
|
@ -579,7 +608,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
})
|
||||
score -= 75
|
||||
risco_eletrico_termico = True
|
||||
elif abs(corrente_motor) > 45:
|
||||
elif abs(corrente_motor) > 50:
|
||||
motivos.append(f"Corrente do motor elevada: {corrente_motor:.1f} A")
|
||||
condicoes.append({
|
||||
"label": "Corrente motor",
|
||||
|
|
@ -615,36 +644,37 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
})
|
||||
score -= 20
|
||||
|
||||
temp_max = max(temperatura_motor, temperatura_driver)
|
||||
# Temperatura do driver
|
||||
_penalidade_d, _motivos_d, _condicoes_d, _risco_d = (
|
||||
self._avaliar_temperatura(
|
||||
endereco=endereco,
|
||||
temperatura=temperatura_driver,
|
||||
regra_desc="driver",
|
||||
)
|
||||
)
|
||||
motivos.extend(_motivos_d)
|
||||
condicoes.extend(_condicoes_d)
|
||||
|
||||
# Temperatura do motor
|
||||
_penalidade_m, _motivos_m, _condicoes_m, _risco_m = (
|
||||
self._avaliar_temperatura(
|
||||
endereco=endereco,
|
||||
temperatura=temperatura_motor,
|
||||
regra_desc="motor",
|
||||
)
|
||||
)
|
||||
motivos.extend(_motivos_m)
|
||||
condicoes.extend(_condicoes_m)
|
||||
|
||||
# Aplica somente a maior penalidade térmica.
|
||||
score -= max(_penalidade_d, _penalidade_m)
|
||||
|
||||
risco_eletrico_termico = (
|
||||
risco_eletrico_termico
|
||||
or _risco_d
|
||||
or _risco_m
|
||||
)
|
||||
|
||||
if temp_max > 85:
|
||||
motivos.append(f"Temperatura MOV perigosa: {temp_max:.1f} °C")
|
||||
condicoes.append({
|
||||
"label": "Temperatura MOV",
|
||||
"valor": temp_max,
|
||||
"severidade": 95,
|
||||
"descricao": f"Temperatura em faixa perigosa ({temp_max:.1f} °C)",
|
||||
"acoes": [
|
||||
"Reduzir carga dos motores.",
|
||||
"Verificar ventilação do compartimento.",
|
||||
"Pausar operação se persistir.",
|
||||
],
|
||||
})
|
||||
score -= 80
|
||||
risco_eletrico_termico = True
|
||||
elif temp_max > 70:
|
||||
motivos.append(f"Temperatura MOV elevada: {temp_max:.1f} °C")
|
||||
condicoes.append({
|
||||
"label": "Temperatura MOV",
|
||||
"valor": temp_max,
|
||||
"severidade": 60,
|
||||
"descricao": f"Temperatura elevada ({temp_max:.1f} °C)",
|
||||
"acoes": [
|
||||
"Reduzir carga dos motores.",
|
||||
"Verificar ventilação do compartimento dos drivers.",
|
||||
],
|
||||
})
|
||||
score -= 25
|
||||
|
||||
# Padrões dinâmicos: úteis para diagnóstico, mas não travam operação.
|
||||
self._avaliar_padroes_dinamicos(
|
||||
|
|
@ -752,6 +782,399 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
"parametros": parametros_debug,
|
||||
}
|
||||
|
||||
def _temperatura_valida(self, temperatura):
|
||||
try:
|
||||
valor = float(temperatura)
|
||||
return 0.0 < valor < 200.0
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
|
||||
def _avaliar_temperatura(
|
||||
self,
|
||||
endereco,
|
||||
temperatura,
|
||||
regra_desc,
|
||||
):
|
||||
motivos = []
|
||||
condicoes = []
|
||||
penalidade = 0
|
||||
risco_eletrico_termico = False
|
||||
|
||||
nome_origem = (
|
||||
"driver de movimento"
|
||||
if regra_desc == "driver"
|
||||
else "motor de tração"
|
||||
)
|
||||
|
||||
label = f"Temperatura do {nome_origem}"
|
||||
|
||||
if not self._temperatura_valida(temperatura):
|
||||
motivos.append(
|
||||
f"Temperatura do {nome_origem} inválida: "
|
||||
f"{temperatura} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 50,
|
||||
"descricao": (
|
||||
"Leitura de temperatura inválida"
|
||||
),
|
||||
"acoes": [
|
||||
"Verificar sensor de temperatura.",
|
||||
],
|
||||
})
|
||||
return (
|
||||
penalidade,
|
||||
motivos,
|
||||
condicoes,
|
||||
risco_eletrico_termico,
|
||||
)
|
||||
|
||||
estado_temp_emergencia = self._estado_limite_alto(
|
||||
driver=endereco,
|
||||
regra=f"temperatura_emergencia_{regra_desc}",
|
||||
valor=temperatura,
|
||||
limite_entrada=self.TEMP_EMERGENCIA_C,
|
||||
limite_saida=self.TEMP_RECUPERA_EMERGENCIA_C,
|
||||
tempo_intermediario_s=0.0,
|
||||
tempo_confirmacao_s=self.TEMP_EMERGENCIA_CONFIRMADO_S,
|
||||
tempo_recuperacao_s=self.TEMP_RECUPERACAO_S,
|
||||
)
|
||||
|
||||
estado_temp_falha = self._estado_limite_alto(
|
||||
driver=endereco,
|
||||
regra=f"temperatura_falha_{regra_desc}",
|
||||
valor=temperatura,
|
||||
limite_entrada=self.TEMP_FALHA_C,
|
||||
limite_saida=self.TEMP_RECUPERA_FALHA_C,
|
||||
tempo_intermediario_s=self.TEMP_FALHA_INTERMEDIARIO_S,
|
||||
tempo_confirmacao_s=self.TEMP_FALHA_CONFIRMADO_S,
|
||||
tempo_recuperacao_s=self.TEMP_RECUPERACAO_S,
|
||||
)
|
||||
|
||||
estado_temp_alerta = self._estado_limite_alto(
|
||||
driver=endereco,
|
||||
regra=f"temperatura_alerta_{regra_desc}",
|
||||
valor=temperatura,
|
||||
limite_entrada=self.TEMP_ALERTA_C,
|
||||
limite_saida=self.TEMP_RECUPERA_ALERTA_C,
|
||||
tempo_intermediario_s=self.TEMP_ALERTA_INTERMEDIARIO_S,
|
||||
tempo_confirmacao_s=self.TEMP_ALERTA_CONFIRMADO_S,
|
||||
tempo_recuperacao_s=self.TEMP_RECUPERACAO_S,
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# EMERGÊNCIA CONFIRMADA
|
||||
# ============================================================
|
||||
|
||||
if estado_temp_emergencia["nivel"] == "confirmado":
|
||||
motivos.append(
|
||||
f"Temperatura do {nome_origem} em nível de emergência "
|
||||
f"confirmado: {temperatura:.1f} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 98,
|
||||
"descricao": (
|
||||
"Temperatura de emergência confirmada após "
|
||||
f"{estado_temp_emergencia['duracao_s']:.1f} s"
|
||||
),
|
||||
"acoes": [
|
||||
"Interromper imediatamente o esforço dos motores.",
|
||||
"Verificar ventilação, motor, driver e sensor térmico.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 90
|
||||
risco_eletrico_termico = True
|
||||
|
||||
# ============================================================
|
||||
# RECUPERANDO DE EMERGÊNCIA
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_emergencia["nivel"] == "recuperando":
|
||||
motivos.append(
|
||||
f"Temperatura do {nome_origem} normalizando após emergência: "
|
||||
f"{temperatura:.1f} °C. Aguardando estabilidade para liberação."
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 98,
|
||||
"descricao": (
|
||||
"Temperatura caiu abaixo do limite de recuperação da "
|
||||
"emergência, mas ainda aguarda estabilidade por "
|
||||
f"{self.TEMP_RECUPERACAO_S:.1f} s "
|
||||
f"({estado_temp_emergencia['duracao_s']:.1f} s decorridos)"
|
||||
),
|
||||
"acoes": [
|
||||
"Manter o equipamento sem esforço.",
|
||||
"Aguardar a conclusão da recuperação térmica.",
|
||||
"Verificar se a temperatura continua diminuindo.",
|
||||
],
|
||||
})
|
||||
|
||||
# Mantém exatamente a proteção da emergência enquanto recupera.
|
||||
penalidade = 90
|
||||
risco_eletrico_termico = True
|
||||
|
||||
# ============================================================
|
||||
# EMERGÊNCIA DETECTADA, AINDA NÃO CONFIRMADA
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_emergencia["ativa"]:
|
||||
motivos.append(
|
||||
f"Temperatura de emergência do {nome_origem} detectada: "
|
||||
f"{temperatura:.1f} °C "
|
||||
f"({estado_temp_emergencia['duracao_s']:.1f} s)"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 89,
|
||||
"descricao": (
|
||||
"Temperatura em nível de emergência, aguardando "
|
||||
"confirmação da leitura"
|
||||
),
|
||||
"acoes": [
|
||||
"Acompanhar imediatamente as próximas leituras.",
|
||||
"Preparar a interrupção do esforço dos motores.",
|
||||
],
|
||||
})
|
||||
|
||||
# Ainda não bloqueia, pois não passou pela confirmação.
|
||||
penalidade = 45
|
||||
|
||||
# ============================================================
|
||||
# FALHA TÉRMICA CONFIRMADA
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_falha["nivel"] == "confirmado":
|
||||
motivos.append(
|
||||
f"Temperatura perigosa do {nome_origem} confirmada: "
|
||||
f"{temperatura:.1f} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 95,
|
||||
"descricao": (
|
||||
"Temperatura perigosa persistiu por "
|
||||
f"{estado_temp_falha['duracao_s']:.1f} s"
|
||||
),
|
||||
"acoes": [
|
||||
"Interromper ou reduzir o esforço dos motores.",
|
||||
"Verificar a ventilação do compartimento.",
|
||||
"Inspecionar motor, driver e sensor térmico.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 80
|
||||
risco_eletrico_termico = True
|
||||
|
||||
# ============================================================
|
||||
# RECUPERANDO DE FALHA TÉRMICA
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_falha["nivel"] == "recuperando":
|
||||
motivos.append(
|
||||
f"Temperatura do {nome_origem} normalizando após condição "
|
||||
f"perigosa: {temperatura:.1f} °C. "
|
||||
"Aguardando estabilidade para liberação."
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 95,
|
||||
"descricao": (
|
||||
"Temperatura caiu abaixo do limite de recuperação da "
|
||||
"falha, mas ainda aguarda estabilidade por "
|
||||
f"{self.TEMP_RECUPERACAO_S:.1f} s "
|
||||
f"({estado_temp_falha['duracao_s']:.1f} s decorridos)"
|
||||
),
|
||||
"acoes": [
|
||||
"Manter a carga reduzida.",
|
||||
"Aguardar a conclusão da recuperação térmica.",
|
||||
"Verificar se a temperatura permanece estável.",
|
||||
],
|
||||
})
|
||||
|
||||
# Continua bloqueante até terminar a recuperação.
|
||||
penalidade = 80
|
||||
risco_eletrico_termico = True
|
||||
|
||||
# ============================================================
|
||||
# FALHA TÉRMICA PERSISTINDO
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_falha["nivel"] == "persistente":
|
||||
motivos.append(
|
||||
f"Temperatura perigosa do {nome_origem} persistindo: "
|
||||
f"{temperatura:.1f} °C "
|
||||
f"({estado_temp_falha['duracao_s']:.1f} s)"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 85,
|
||||
"descricao": (
|
||||
"Temperatura perigosa persistindo, aguardando "
|
||||
"confirmação de segurança"
|
||||
),
|
||||
"acoes": [
|
||||
"Acompanhar as próximas leituras.",
|
||||
"Reduzir a carga se a temperatura continuar aumentando.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 35
|
||||
|
||||
# ============================================================
|
||||
# PRIMEIRA DETECÇÃO DE FALHA TÉRMICA
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_falha["nivel"] == "detectado":
|
||||
motivos.append(
|
||||
f"Pico de temperatura perigosa do {nome_origem} detectado: "
|
||||
f"{temperatura:.1f} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 70,
|
||||
"descricao": (
|
||||
"Temperatura perigosa detectada, ainda sem persistência"
|
||||
),
|
||||
"acoes": [
|
||||
"Acompanhar a confirmação da leitura térmica.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 10
|
||||
|
||||
# ============================================================
|
||||
# ALERTA TÉRMICO CONFIRMADO
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_alerta["nivel"] == "confirmado":
|
||||
motivos.append(
|
||||
f"Temperatura elevada do {nome_origem} confirmada: "
|
||||
f"{temperatura:.1f} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 60,
|
||||
"descricao": (
|
||||
"Temperatura elevada persistiu por "
|
||||
f"{estado_temp_alerta['duracao_s']:.1f} s"
|
||||
),
|
||||
"acoes": [
|
||||
"Reduzir a carga dos motores.",
|
||||
"Verificar a ventilação.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 25
|
||||
|
||||
# ============================================================
|
||||
# RECUPERANDO DE ALERTA TÉRMICO
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_alerta["nivel"] == "recuperando":
|
||||
motivos.append(
|
||||
f"Temperatura do {nome_origem} normalizando após alerta: "
|
||||
f"{temperatura:.1f} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 60,
|
||||
"descricao": (
|
||||
"Temperatura caiu abaixo do limite de recuperação do "
|
||||
"alerta, aguardando estabilidade por "
|
||||
f"{self.TEMP_RECUPERACAO_S:.1f} s "
|
||||
f"({estado_temp_alerta['duracao_s']:.1f} s decorridos)"
|
||||
),
|
||||
"acoes": [
|
||||
"Acompanhar a estabilização da temperatura.",
|
||||
"Manter a carga moderada durante a recuperação.",
|
||||
],
|
||||
})
|
||||
|
||||
# Mantém a penalidade do alerta até concluir a recuperação,
|
||||
# mas não caracteriza risco elétrico/térmico bloqueante.
|
||||
penalidade = 25
|
||||
|
||||
# ============================================================
|
||||
# ALERTA PERSISTINDO
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_alerta["nivel"] == "persistente":
|
||||
motivos.append(
|
||||
f"Aquecimento do {nome_origem} persistindo: "
|
||||
f"{temperatura:.1f} °C "
|
||||
f"({estado_temp_alerta['duracao_s']:.1f} s)"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 50,
|
||||
"descricao": (
|
||||
"Temperatura elevada persistindo, aguardando confirmação"
|
||||
),
|
||||
"acoes": [
|
||||
"Acompanhar a evolução da temperatura.",
|
||||
"Verificar a ventilação se continuar aumentando.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 15
|
||||
|
||||
# ============================================================
|
||||
# PRIMEIRA DETECÇÃO DE ALERTA
|
||||
# ============================================================
|
||||
|
||||
elif estado_temp_alerta["nivel"] == "detectado":
|
||||
motivos.append(
|
||||
f"Aquecimento do {nome_origem} detectado: "
|
||||
f"{temperatura:.1f} °C"
|
||||
)
|
||||
|
||||
condicoes.append({
|
||||
"label": label,
|
||||
"valor": temperatura,
|
||||
"severidade": 40,
|
||||
"descricao": (
|
||||
"Temperatura acima da faixa ideal, aguardando persistência"
|
||||
),
|
||||
"acoes": [
|
||||
"Acompanhar a evolução da temperatura.",
|
||||
],
|
||||
})
|
||||
|
||||
penalidade = 5
|
||||
|
||||
return (
|
||||
penalidade,
|
||||
motivos,
|
||||
condicoes,
|
||||
risco_eletrico_termico,
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# Frequência / contrato
|
||||
# ============================================================
|
||||
|
|
@ -835,9 +1258,6 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
|
||||
@staticmethod
|
||||
def _penalizar_frequencia(freq, base, minimo, max_penalidade):
|
||||
try:
|
||||
return float(penalizar_frequencia(freq, base, minimo, max_penalidade) or 0)
|
||||
except Exception:
|
||||
if base <= 0:
|
||||
return 0.0
|
||||
if freq >= minimo:
|
||||
|
|
@ -903,6 +1323,117 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
def _estado_persistencia(
|
||||
self,
|
||||
driver,
|
||||
regra,
|
||||
ativa,
|
||||
tempo_intermediario_s,
|
||||
tempo_confirmacao_s,
|
||||
tempo_recuperacao_s=0.0,
|
||||
):
|
||||
agora = time.monotonic()
|
||||
chave = (str(driver), str(regra))
|
||||
|
||||
estado = self._estados_persistencia.setdefault(chave, {
|
||||
"ativa_desde": None,
|
||||
"normal_desde": None,
|
||||
"confirmada": False,
|
||||
})
|
||||
|
||||
if ativa:
|
||||
estado["normal_desde"] = None
|
||||
|
||||
if estado["ativa_desde"] is None:
|
||||
estado["ativa_desde"] = agora
|
||||
|
||||
duracao_s = max(0.0, agora - estado["ativa_desde"])
|
||||
|
||||
if duracao_s >= tempo_confirmacao_s:
|
||||
estado["confirmada"] = True
|
||||
|
||||
if estado["confirmada"]:
|
||||
nivel = "confirmado"
|
||||
elif duracao_s >= tempo_intermediario_s:
|
||||
nivel = "persistente"
|
||||
else:
|
||||
nivel = "detectado"
|
||||
|
||||
return {
|
||||
"ativa": True,
|
||||
"nivel": nivel,
|
||||
"duracao_s": duracao_s,
|
||||
"confirmada": estado["confirmada"],
|
||||
}
|
||||
|
||||
estado["ativa_desde"] = None
|
||||
|
||||
if not estado["confirmada"]:
|
||||
estado["normal_desde"] = None
|
||||
return {
|
||||
"ativa": False,
|
||||
"nivel": "normal",
|
||||
"duracao_s": 0.0,
|
||||
"confirmada": False,
|
||||
}
|
||||
|
||||
if estado["normal_desde"] is None:
|
||||
estado["normal_desde"] = agora
|
||||
|
||||
normal_ha_s = max(0.0, agora - estado["normal_desde"])
|
||||
|
||||
if normal_ha_s >= tempo_recuperacao_s:
|
||||
estado["confirmada"] = False
|
||||
estado["normal_desde"] = None
|
||||
|
||||
return {
|
||||
"ativa": False,
|
||||
"nivel": "normal",
|
||||
"duracao_s": 0.0,
|
||||
"confirmada": False,
|
||||
}
|
||||
|
||||
return {
|
||||
"ativa": True,
|
||||
"nivel": "recuperando",
|
||||
"duracao_s": normal_ha_s,
|
||||
"confirmada": True,
|
||||
}
|
||||
|
||||
def _estado_limite_alto(
|
||||
self,
|
||||
driver,
|
||||
regra,
|
||||
valor,
|
||||
limite_entrada,
|
||||
limite_saida,
|
||||
tempo_intermediario_s,
|
||||
tempo_confirmacao_s,
|
||||
tempo_recuperacao_s,
|
||||
):
|
||||
chave = (str(driver), str(regra))
|
||||
persistencia = self._estados_persistencia.get(chave, {})
|
||||
confirmada = bool(persistencia.get("confirmada", False))
|
||||
|
||||
# Histerese:
|
||||
# ainda não confirmou -> entra no limite_entrada
|
||||
# já confirmou -> permanece até cair abaixo do limite_saida
|
||||
ativa = (
|
||||
valor >= limite_saida
|
||||
if confirmada
|
||||
else valor >= limite_entrada
|
||||
)
|
||||
|
||||
return self._estado_persistencia(
|
||||
driver=driver,
|
||||
regra=regra,
|
||||
ativa=ativa,
|
||||
tempo_intermediario_s=tempo_intermediario_s,
|
||||
tempo_confirmacao_s=tempo_confirmacao_s,
|
||||
tempo_recuperacao_s=tempo_recuperacao_s,
|
||||
)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Status/publicação
|
||||
# ============================================================
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
# Processo C#
|
||||
"process_private_mem_warn_mb": 6000.0,
|
||||
"process_private_mem_fail_mb": 10000.0,
|
||||
"process_threads_warn": 120,
|
||||
"process_threads_warn": 160,
|
||||
"process_threads_fail": 220,
|
||||
|
||||
# ThreadPool
|
||||
|
|
@ -615,19 +615,6 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
],
|
||||
))
|
||||
|
||||
if st_gpu_mem_load != StatusModulo.OPERANTE:
|
||||
gpu_cond.append(self._cond(
|
||||
"Carga da Memória do GPU",
|
||||
raw["gpu_memory_load_percent"],
|
||||
80,
|
||||
"Controlador/memória da GPU sob carga elevada.",
|
||||
[
|
||||
"Verificar tamanho dos tensores",
|
||||
"Reduzir cópias CPU/GPU",
|
||||
"Avaliar TensorRT/ONNX",
|
||||
],
|
||||
))
|
||||
|
||||
if gpu_power is not None and gpu_power >= C["gpu_power_warn_w"]:
|
||||
gpu_cond.append(self._cond(
|
||||
"Potência do GPU",
|
||||
|
|
@ -654,6 +641,23 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
else StatusModulo.OPERANTE
|
||||
)
|
||||
|
||||
if st_gpu_memory_block != StatusModulo.OPERANTE:
|
||||
gpu_memory_value = max(
|
||||
x for x in [gpu_mem_load, gpu_fb_load]
|
||||
if x is not None
|
||||
) if gpu_mem_load is not None or gpu_fb_load is not None else None
|
||||
gpu_cond.append(self._cond(
|
||||
"Carga da Memória do GPU",
|
||||
gpu_memory_value,
|
||||
80,
|
||||
"Controlador/memória da GPU sob carga elevada.",
|
||||
[
|
||||
"Verificar tamanho dos tensores",
|
||||
"Reduzir cópias CPU/GPU",
|
||||
"Avaliar TensorRT/ONNX",
|
||||
],
|
||||
))
|
||||
|
||||
saude_individual.append(self._item(
|
||||
"gpu_temp",
|
||||
"Temperatura do GPU",
|
||||
|
|
@ -692,7 +696,7 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
"Carga de Memória/Framebuffer GPU",
|
||||
st_gpu_memory_block,
|
||||
sc_gpu_memory_block,
|
||||
self._motivo("gpu_memory_load", st_gpu_memory_block, min(
|
||||
self._motivo("gpu_memory_load", st_gpu_memory_block, max(
|
||||
x for x in [gpu_mem_load, gpu_fb_load] if x is not None
|
||||
) if gpu_mem_load is not None or gpu_fb_load is not None else None, "%"),
|
||||
[c for c in gpu_cond if c["label"] == "Carga da Memória do GPU"],
|
||||
|
|
@ -877,29 +881,78 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
)
|
||||
|
||||
sc_process = min(sc_proc_mem, sc_proc_threads, sc_tp)
|
||||
st_process = (
|
||||
StatusModulo.FALHA
|
||||
if StatusModulo.FALHA in [st_proc_mem, st_proc_threads, st_tp]
|
||||
else StatusModulo.ALERTA
|
||||
if StatusModulo.ALERTA in [st_proc_mem, st_proc_threads, st_tp]
|
||||
else StatusModulo.OPERANTE
|
||||
)
|
||||
status_processos = [st_proc_mem, st_proc_threads, st_tp]
|
||||
|
||||
if StatusModulo.FALHA in status_processos:
|
||||
st_process = StatusModulo.FALHA
|
||||
elif StatusModulo.ALERTA in status_processos:
|
||||
st_process = StatusModulo.ALERTA
|
||||
elif StatusModulo.DESCONECTADO in status_processos:
|
||||
st_process = StatusModulo.DESCONECTADO
|
||||
else:
|
||||
st_process = StatusModulo.OPERANTE
|
||||
|
||||
process_cond = []
|
||||
if st_process != StatusModulo.OPERANTE:
|
||||
process_motivos = []
|
||||
|
||||
if st_proc_mem != StatusModulo.OPERANTE:
|
||||
process_motivos += self._motivo(
|
||||
"process_memory",
|
||||
st_proc_mem,
|
||||
proc_private,
|
||||
"MB",
|
||||
)
|
||||
process_cond.append(self._cond(
|
||||
"Processo C#",
|
||||
"Memória do Processo",
|
||||
raw["process_private_memory_mb"],
|
||||
100 if st_process == StatusModulo.FALHA else 70,
|
||||
"Processo principal com uso elevado de memória, threads ou ThreadPool.",
|
||||
100 if st_proc_mem == StatusModulo.FALHA else 70,
|
||||
"Uso elevado de memória privada pelo processo principal.",
|
||||
[
|
||||
"Verificar vazamento de memória",
|
||||
"Revisar criação de threads",
|
||||
"Verificar tasks longas no C#",
|
||||
"Verificar crescimento contínuo da memória",
|
||||
"Verificar retenção de imagens e buffers",
|
||||
"Verificar objetos gráficos não descartados",
|
||||
],
|
||||
))
|
||||
|
||||
motivos += self._motivo("process", st_process, proc_private, "MB")
|
||||
if st_proc_threads != StatusModulo.OPERANTE:
|
||||
process_motivos += self._motivo(
|
||||
"process_threads",
|
||||
st_proc_threads,
|
||||
proc_threads,
|
||||
"",
|
||||
)
|
||||
process_cond.append(self._cond(
|
||||
"Threads do Processo",
|
||||
raw["process_thread_count"],
|
||||
100 if st_proc_threads == StatusModulo.FALHA else 70,
|
||||
"Quantidade elevada de threads no processo principal.",
|
||||
[
|
||||
"Verificar criação recorrente de threads e timers",
|
||||
"Verificar ciclos de reconexão de dispositivos",
|
||||
"Verificar threads que não estão sendo finalizadas",
|
||||
],
|
||||
))
|
||||
|
||||
if st_tp != StatusModulo.OPERANTE:
|
||||
process_motivos += self._motivo(
|
||||
"threadpool_worker",
|
||||
st_tp,
|
||||
tp_worker,
|
||||
"%",
|
||||
)
|
||||
process_cond.append(self._cond(
|
||||
"Uso do ThreadPool",
|
||||
raw["threadpool_used_worker_percent"],
|
||||
100 if st_tp == StatusModulo.FALHA else 70,
|
||||
"Ocupação elevada das threads worker do ThreadPool.",
|
||||
[
|
||||
"Verificar tarefas bloqueantes",
|
||||
"Verificar operações síncronas demoradas",
|
||||
"Verificar tasks de longa duração",
|
||||
],
|
||||
))
|
||||
|
||||
motivos += process_motivos
|
||||
condicoes += process_cond
|
||||
|
||||
saude_individual.append(self._item(
|
||||
|
|
@ -907,10 +960,10 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
"Processo Principal",
|
||||
st_process,
|
||||
sc_process,
|
||||
self._motivo("process", st_process, proc_private, "MB"),
|
||||
process_motivos,
|
||||
process_cond,
|
||||
valor=raw["process_private_memory_mb"],
|
||||
unidade="MB",
|
||||
valor=None,
|
||||
unidade="",
|
||||
))
|
||||
|
||||
# ======================================================
|
||||
|
|
@ -979,17 +1032,21 @@ class ModuloPC(ModuloDiagnosticoBase):
|
|||
st_ram == StatusModulo.FALHA,
|
||||
st_vram == StatusModulo.FALHA,
|
||||
st_disk == StatusModulo.FALHA,
|
||||
st_process == StatusModulo.FALHA,
|
||||
])
|
||||
|
||||
any_alert = any(
|
||||
item.get("status") == StatusModulo.ALERTA.value
|
||||
any_problem = any(
|
||||
item.get("status") in (
|
||||
StatusModulo.ALERTA.value,
|
||||
StatusModulo.FALHA.value,
|
||||
)
|
||||
for item in saude_individual
|
||||
)
|
||||
|
||||
if hard_fail:
|
||||
status = StatusModulo.FALHA
|
||||
score_total = min(score_total, 35.0)
|
||||
elif score_total < 80.0 or any_alert:
|
||||
elif score_total < 80.0 or any_problem:
|
||||
status = StatusModulo.ALERTA
|
||||
else:
|
||||
status = StatusModulo.OPERANTE
|
||||
|
|
|
|||
Loading…
Reference in New Issue