novas regras do mov e nao permitir parar operacao se o modulo nao for mandatorio nas regras taticas
This commit is contained in:
parent
fce03f5044
commit
bfed826136
|
|
@ -30,7 +30,7 @@ def main():
|
||||||
T_Code.Sen: ModuloSensoriamento(),
|
T_Code.Sen: ModuloSensoriamento(),
|
||||||
T_Code.Atu: ModuloAtuador(),
|
T_Code.Atu: ModuloAtuador(),
|
||||||
#T_Code.Lra: ModuloLoRa(),
|
#T_Code.Lra: ModuloLoRa(),
|
||||||
T_Code.Imu: CameraIMU(modulos_imu=[T_Code.Snr, T_Code.Cam], max_disagreement_deg=4.0, max_skew_imu_s=0.12),
|
T_Code.Imu: CameraIMU(modulos_imu=[T_Code.Snr]), # CameraIMU(modulos_imu=[T_Code.Snr, T_Code.Cam], max_disagreement_deg=4.0, max_skew_imu_s=0.12),
|
||||||
T_Code.Npc: ModuloPC(),
|
T_Code.Npc: ModuloPC(),
|
||||||
T_Code.Lvx: ModuloLivox(),
|
T_Code.Lvx: ModuloLivox(),
|
||||||
T_Code.Ipb: ModuloIPBribge(),
|
T_Code.Ipb: ModuloIPBribge(),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import time
|
import time
|
||||||
|
import math
|
||||||
|
from collections import deque
|
||||||
from shared.enums import T_Code, StatusModulo
|
from shared.enums import T_Code, StatusModulo
|
||||||
from health_worker.modulos.base import ModuloDiagnosticoBase
|
from health_worker.modulos.base import ModuloDiagnosticoBase
|
||||||
from health_worker.aprendizado.padrao_dinamico import PadraoDinamico
|
from health_worker.aprendizado.padrao_dinamico import PadraoDinamico
|
||||||
|
|
@ -75,7 +77,6 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
TEMP_AMOSTRAS_MIN_FALHA = 3
|
TEMP_AMOSTRAS_MIN_FALHA = 3
|
||||||
TEMP_AMOSTRAS_MIN_EMERGENCIA = 3
|
TEMP_AMOSTRAS_MIN_EMERGENCIA = 3
|
||||||
TEMP_AMOSTRA_MAX_IDADE_S = 3.0
|
TEMP_AMOSTRA_MAX_IDADE_S = 3.0
|
||||||
TEMP_SALTO_SUSPEITO_C = 35.0
|
|
||||||
|
|
||||||
# Elétricos
|
# Elétricos
|
||||||
TENSAO_FALHA_PERSISTENCIA_S = 2.0
|
TENSAO_FALHA_PERSISTENCIA_S = 2.0
|
||||||
|
|
@ -94,6 +95,21 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
CORRENTE_MOTOR_EXTREMA_A = 90.0
|
CORRENTE_MOTOR_EXTREMA_A = 90.0
|
||||||
CORRENTE_BARRAMENTO_EXTREMA_A = 55.0
|
CORRENTE_BARRAMENTO_EXTREMA_A = 55.0
|
||||||
|
|
||||||
|
TEMP_MIN_FISICA_C = 0.0
|
||||||
|
TEMP_MAX_FISICA_C = 150.0
|
||||||
|
|
||||||
|
# Uma elevação maior que isso entre amostras próximas entra em quarentena.
|
||||||
|
TEMP_SALTO_SUSPEITO_C = 12.0
|
||||||
|
|
||||||
|
# Velocidade térmica aceitável. O piso de 3 °C tolera ruído e quantização.
|
||||||
|
TEMP_VARIACAO_BASE_C = 3.0
|
||||||
|
TEMP_SUBIDA_MAX_C_S = 2.0
|
||||||
|
TEMP_DESCIDA_MAX_C_S = 4.0
|
||||||
|
|
||||||
|
# Para aceitar um novo patamar após salto.
|
||||||
|
TEMP_CONFIRMAR_PATAMAR_AMOSTRAS = 3
|
||||||
|
TEMP_CONFIRMAR_PATAMAR_TOL_C = 5.0
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.t_code = T_Code.Mov
|
self.t_code = T_Code.Mov
|
||||||
self.nome = "Movimentacao"
|
self.nome = "Movimentacao"
|
||||||
|
|
@ -140,6 +156,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
self._estados_persistencia = {}
|
self._estados_persistencia = {}
|
||||||
self._historico_amostras = {}
|
self._historico_amostras = {}
|
||||||
|
self._estados_temperatura = {}
|
||||||
|
|
||||||
def atualizar_saude(self):
|
def atualizar_saude(self):
|
||||||
try:
|
try:
|
||||||
|
|
@ -979,8 +996,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
def _temperatura_valida(self, temperatura):
|
def _temperatura_valida(self, temperatura):
|
||||||
try:
|
try:
|
||||||
valor = float(temperatura)
|
valor = float(temperatura)
|
||||||
return 0.0 < valor < 200.0
|
|
||||||
except (TypeError, ValueError):
|
return (
|
||||||
|
math.isfinite(valor)
|
||||||
|
and self.TEMP_MIN_FISICA_C < valor < self.TEMP_MAX_FISICA_C
|
||||||
|
)
|
||||||
|
except (TypeError, ValueError, OverflowError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _avaliar_temperatura(
|
def _avaliar_temperatura(
|
||||||
|
|
@ -1005,11 +1026,11 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
label = f"Temperatura do {nome_origem}"
|
label = f"Temperatura do {nome_origem}"
|
||||||
|
|
||||||
amostra = self._registrar_amostra(endereco, campo, temperatura, modulo)
|
amostra = self._registrar_amostra(endereco, campo, temperatura, modulo)
|
||||||
|
normalizada = self._normalizar_temperatura(endereco=endereco, campo=campo, temperatura=temperatura, amostra=amostra)
|
||||||
|
|
||||||
if not self._temperatura_valida(temperatura):
|
if normalizada["status"] == "invalida":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura do {nome_origem} inválida: "
|
f"Temperatura do {nome_origem} inválida: {temperatura}"
|
||||||
f"{temperatura} °C"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
|
|
@ -1017,49 +1038,64 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
"valor": temperatura,
|
"valor": temperatura,
|
||||||
"severidade": 50,
|
"severidade": 50,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Leitura de temperatura inválida"
|
"Amostra térmica inválida; ignorada pela proteção "
|
||||||
|
"de sobretemperatura"
|
||||||
),
|
),
|
||||||
"acoes": [
|
"acoes": [
|
||||||
"Verificar sensor de temperatura.",
|
"Verificar sensor, conversão, aterramento e cabeamento.",
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return (
|
|
||||||
penalidade,
|
self._resetar_candidatos_temperatura(endereco, regra_desc)
|
||||||
motivos,
|
|
||||||
condicoes,
|
return self._resultado_temperatura_inconclusiva(endereco, regra_desc, motivos, condicoes)
|
||||||
risco_eletrico_termico,
|
|
||||||
|
if normalizada["status"] == "desatualizada":
|
||||||
|
motivos.append(
|
||||||
|
f"Temperatura do {nome_origem} desatualizada; "
|
||||||
|
"sobretemperatura não confirmada"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not amostra["fresca"]:
|
return self._resultado_temperatura_inconclusiva(endereco, regra_desc, motivos, condicoes)
|
||||||
|
|
||||||
|
if normalizada["status"] == "suspeita":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura do {nome_origem} sem amostra fresca "
|
f"Amostra térmica suspeita ignorada no {nome_origem}: "
|
||||||
f"({amostra['idade_s']:.1f} s)"
|
f"{float(temperatura):.1f} °C; "
|
||||||
|
f"referência validada={normalizada['valor_validado']:.1f} °C; "
|
||||||
|
f"delta={normalizada['delta_c']:+.1f} °C; "
|
||||||
|
f"confirmações="
|
||||||
|
f"{normalizada['amostras_candidatas']}/"
|
||||||
|
f"{self.TEMP_CONFIRMAR_PATAMAR_AMOSTRAS}"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura,
|
||||||
"severidade": 55,
|
"severidade": 35,
|
||||||
"descricao": "Leitura térmica está congelada ou desatualizada; não confirma sobretemperatura",
|
"descricao": (
|
||||||
"acoes": ["Verificar publicação e timestamp do sensor térmico."],
|
"Salto térmico fisicamente implausível em quarentena; "
|
||||||
|
"não classificado como temperatura elevada"
|
||||||
|
),
|
||||||
|
"acoes": [
|
||||||
|
"Aguardar as próximas amostras.",
|
||||||
|
"Verificar sensor e aterramento se o ruído persistir.",
|
||||||
|
],
|
||||||
})
|
})
|
||||||
return (0, motivos, condicoes, False)
|
|
||||||
|
|
||||||
if amostra["salto_suspeito"]:
|
self._resetar_candidatos_temperatura(endereco, regra_desc)
|
||||||
motivos.append(
|
|
||||||
f"Salto térmico suspeito no {nome_origem}: {amostra['delta_c']:.1f} °C"
|
return self._resultado_temperatura_inconclusiva(endereco, regra_desc, motivos, condicoes)
|
||||||
)
|
|
||||||
condicoes.append({
|
if normalizada["status"] == "repetida":
|
||||||
"label": label,
|
return self._resultado_temperatura_inconclusiva(endereco, regra_desc, motivos, condicoes)
|
||||||
"valor": temperatura,
|
|
||||||
"severidade": 70,
|
temperatura_avaliada = normalizada["valor_validado"]
|
||||||
"descricao": "Variação térmica instantânea acima do fisicamente plausível; aguardando confirmação",
|
|
||||||
"acoes": ["Verificar sensor, divisor, aterramento e próximas amostras."],
|
|
||||||
})
|
|
||||||
|
|
||||||
estado_temp_emergencia = self._estado_limite_alto(
|
estado_temp_emergencia = self._estado_limite_alto(
|
||||||
driver=endereco,
|
driver=endereco,
|
||||||
regra=f"temperatura_emergencia_{regra_desc}",
|
regra=f"temperatura_emergencia_{regra_desc}",
|
||||||
valor=temperatura,
|
valor=temperatura_avaliada,
|
||||||
limite_entrada=self.TEMP_EMERGENCIA_C,
|
limite_entrada=self.TEMP_EMERGENCIA_C,
|
||||||
limite_saida=self.TEMP_RECUPERA_EMERGENCIA_C,
|
limite_saida=self.TEMP_RECUPERA_EMERGENCIA_C,
|
||||||
tempo_intermediario_s=0.0,
|
tempo_intermediario_s=0.0,
|
||||||
|
|
@ -1072,7 +1108,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
estado_temp_falha = self._estado_limite_alto(
|
estado_temp_falha = self._estado_limite_alto(
|
||||||
driver=endereco,
|
driver=endereco,
|
||||||
regra=f"temperatura_falha_{regra_desc}",
|
regra=f"temperatura_falha_{regra_desc}",
|
||||||
valor=temperatura,
|
valor=temperatura_avaliada,
|
||||||
limite_entrada=self.TEMP_FALHA_C,
|
limite_entrada=self.TEMP_FALHA_C,
|
||||||
limite_saida=self.TEMP_RECUPERA_FALHA_C,
|
limite_saida=self.TEMP_RECUPERA_FALHA_C,
|
||||||
tempo_intermediario_s=self.TEMP_FALHA_INTERMEDIARIO_S,
|
tempo_intermediario_s=self.TEMP_FALHA_INTERMEDIARIO_S,
|
||||||
|
|
@ -1085,7 +1121,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
estado_temp_alerta = self._estado_limite_alto(
|
estado_temp_alerta = self._estado_limite_alto(
|
||||||
driver=endereco,
|
driver=endereco,
|
||||||
regra=f"temperatura_alerta_{regra_desc}",
|
regra=f"temperatura_alerta_{regra_desc}",
|
||||||
valor=temperatura,
|
valor=temperatura_avaliada,
|
||||||
limite_entrada=self.TEMP_ALERTA_C,
|
limite_entrada=self.TEMP_ALERTA_C,
|
||||||
limite_saida=self.TEMP_RECUPERA_ALERTA_C,
|
limite_saida=self.TEMP_RECUPERA_ALERTA_C,
|
||||||
tempo_intermediario_s=self.TEMP_ALERTA_INTERMEDIARIO_S,
|
tempo_intermediario_s=self.TEMP_ALERTA_INTERMEDIARIO_S,
|
||||||
|
|
@ -1102,12 +1138,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
if estado_temp_emergencia["nivel"] == "confirmado":
|
if estado_temp_emergencia["nivel"] == "confirmado":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura do {nome_origem} em nível de emergência "
|
f"Temperatura do {nome_origem} em nível de emergência "
|
||||||
f"confirmado: {temperatura:.1f} °C"
|
f"confirmado: {temperatura_avaliada:.1f} °C"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 98,
|
"severidade": 98,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura de emergência confirmada após "
|
"Temperatura de emergência confirmada após "
|
||||||
|
|
@ -1129,12 +1165,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_emergencia["nivel"] == "recuperando":
|
elif estado_temp_emergencia["nivel"] == "recuperando":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura do {nome_origem} normalizando após emergência: "
|
f"Temperatura do {nome_origem} normalizando após emergência: "
|
||||||
f"{temperatura:.1f} °C. Aguardando estabilidade para liberação."
|
f"{temperatura_avaliada:.1f} °C. Aguardando estabilidade para liberação."
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 98,
|
"severidade": 98,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura caiu abaixo do limite de recuperação da "
|
"Temperatura caiu abaixo do limite de recuperação da "
|
||||||
|
|
@ -1160,13 +1196,13 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_emergencia["ativa"]:
|
elif estado_temp_emergencia["ativa"]:
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura de emergência do {nome_origem} detectada: "
|
f"Temperatura de emergência do {nome_origem} detectada: "
|
||||||
f"{temperatura:.1f} °C "
|
f"{temperatura_avaliada:.1f} °C "
|
||||||
f"({estado_temp_emergencia['duracao_s']:.1f} s)"
|
f"({estado_temp_emergencia['duracao_s']:.1f} s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 89,
|
"severidade": 89,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura em nível de emergência, aguardando "
|
"Temperatura em nível de emergência, aguardando "
|
||||||
|
|
@ -1188,12 +1224,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_falha["nivel"] == "confirmado":
|
elif estado_temp_falha["nivel"] == "confirmado":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura perigosa do {nome_origem} confirmada: "
|
f"Temperatura perigosa do {nome_origem} confirmada: "
|
||||||
f"{temperatura:.1f} °C"
|
f"{temperatura_avaliada:.1f} °C"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 95,
|
"severidade": 95,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura perigosa persistiu por "
|
"Temperatura perigosa persistiu por "
|
||||||
|
|
@ -1216,13 +1252,13 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_falha["nivel"] == "recuperando":
|
elif estado_temp_falha["nivel"] == "recuperando":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura do {nome_origem} normalizando após condição "
|
f"Temperatura do {nome_origem} normalizando após condição "
|
||||||
f"perigosa: {temperatura:.1f} °C. "
|
f"perigosa: {temperatura_avaliada:.1f} °C. "
|
||||||
"Aguardando estabilidade para liberação."
|
"Aguardando estabilidade para liberação."
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 95,
|
"severidade": 95,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura caiu abaixo do limite de recuperação da "
|
"Temperatura caiu abaixo do limite de recuperação da "
|
||||||
|
|
@ -1248,13 +1284,13 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_falha["nivel"] == "persistente":
|
elif estado_temp_falha["nivel"] == "persistente":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura perigosa do {nome_origem} persistindo: "
|
f"Temperatura perigosa do {nome_origem} persistindo: "
|
||||||
f"{temperatura:.1f} °C "
|
f"{temperatura_avaliada:.1f} °C "
|
||||||
f"({estado_temp_falha['duracao_s']:.1f} s)"
|
f"({estado_temp_falha['duracao_s']:.1f} s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 85,
|
"severidade": 85,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura perigosa persistindo, aguardando "
|
"Temperatura perigosa persistindo, aguardando "
|
||||||
|
|
@ -1275,12 +1311,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_falha["nivel"] == "detectado":
|
elif estado_temp_falha["nivel"] == "detectado":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Pico de temperatura perigosa do {nome_origem} detectado: "
|
f"Pico de temperatura perigosa do {nome_origem} detectado: "
|
||||||
f"{temperatura:.1f} °C"
|
f"{temperatura_avaliada:.1f} °C"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 70,
|
"severidade": 70,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura perigosa detectada, ainda sem persistência"
|
"Temperatura perigosa detectada, ainda sem persistência"
|
||||||
|
|
@ -1299,12 +1335,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_alerta["nivel"] == "confirmado":
|
elif estado_temp_alerta["nivel"] == "confirmado":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura elevada do {nome_origem} confirmada: "
|
f"Temperatura elevada do {nome_origem} confirmada: "
|
||||||
f"{temperatura:.1f} °C"
|
f"{temperatura_avaliada:.1f} °C"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 60,
|
"severidade": 60,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura elevada persistiu por "
|
"Temperatura elevada persistiu por "
|
||||||
|
|
@ -1325,12 +1361,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_alerta["nivel"] == "recuperando":
|
elif estado_temp_alerta["nivel"] == "recuperando":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Temperatura do {nome_origem} normalizando após alerta: "
|
f"Temperatura do {nome_origem} normalizando após alerta: "
|
||||||
f"{temperatura:.1f} °C"
|
f"{temperatura_avaliada:.1f} °C"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 60,
|
"severidade": 60,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura caiu abaixo do limite de recuperação do "
|
"Temperatura caiu abaixo do limite de recuperação do "
|
||||||
|
|
@ -1355,13 +1391,13 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_alerta["nivel"] == "persistente":
|
elif estado_temp_alerta["nivel"] == "persistente":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Aquecimento do {nome_origem} persistindo: "
|
f"Aquecimento do {nome_origem} persistindo: "
|
||||||
f"{temperatura:.1f} °C "
|
f"{temperatura_avaliada:.1f} °C "
|
||||||
f"({estado_temp_alerta['duracao_s']:.1f} s)"
|
f"({estado_temp_alerta['duracao_s']:.1f} s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 50,
|
"severidade": 50,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura elevada persistindo, aguardando confirmação"
|
"Temperatura elevada persistindo, aguardando confirmação"
|
||||||
|
|
@ -1381,12 +1417,12 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
elif estado_temp_alerta["nivel"] == "detectado":
|
elif estado_temp_alerta["nivel"] == "detectado":
|
||||||
motivos.append(
|
motivos.append(
|
||||||
f"Aquecimento do {nome_origem} detectado: "
|
f"Aquecimento do {nome_origem} detectado: "
|
||||||
f"{temperatura:.1f} °C"
|
f"{temperatura_avaliada:.1f} °C"
|
||||||
)
|
)
|
||||||
|
|
||||||
condicoes.append({
|
condicoes.append({
|
||||||
"label": label,
|
"label": label,
|
||||||
"valor": temperatura,
|
"valor": temperatura_avaliada,
|
||||||
"severidade": 40,
|
"severidade": 40,
|
||||||
"descricao": (
|
"descricao": (
|
||||||
"Temperatura acima da faixa ideal, aguardando persistência"
|
"Temperatura acima da faixa ideal, aguardando persistência"
|
||||||
|
|
@ -1909,6 +1945,183 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
if str(k).strip()
|
if str(k).strip()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _normalizar_temperatura(
|
||||||
|
self,
|
||||||
|
endereco,
|
||||||
|
campo,
|
||||||
|
temperatura,
|
||||||
|
amostra,
|
||||||
|
):
|
||||||
|
chave = (str(endereco), str(campo))
|
||||||
|
|
||||||
|
estado = self._estados_temperatura.setdefault(chave, {
|
||||||
|
"valor_validado": None,
|
||||||
|
"mono_validado": None,
|
||||||
|
"candidatas": deque(
|
||||||
|
maxlen=self.TEMP_CONFIRMAR_PATAMAR_AMOSTRAS
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
if not self._temperatura_valida(temperatura):
|
||||||
|
estado["candidatas"].clear()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "invalida",
|
||||||
|
"valor_validado": estado["valor_validado"],
|
||||||
|
"motivo": "valor fora da faixa física ou não finito",
|
||||||
|
}
|
||||||
|
|
||||||
|
valor = float(temperatura)
|
||||||
|
|
||||||
|
if not amostra["fresca"]:
|
||||||
|
return {
|
||||||
|
"status": "desatualizada",
|
||||||
|
"valor_validado": estado["valor_validado"],
|
||||||
|
"motivo": "amostra térmica sem frescor",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Polling repetido do mesmo snapshot não confirma tendência.
|
||||||
|
if not amostra["nova"]:
|
||||||
|
return {
|
||||||
|
"status": "repetida",
|
||||||
|
"valor_validado": estado["valor_validado"],
|
||||||
|
"motivo": "snapshot já avaliado",
|
||||||
|
}
|
||||||
|
|
||||||
|
agora = time.monotonic()
|
||||||
|
anterior = estado["valor_validado"]
|
||||||
|
mono_anterior = estado["mono_validado"]
|
||||||
|
|
||||||
|
# Primeira amostra fisicamente válida vira referência.
|
||||||
|
if anterior is None or mono_anterior is None:
|
||||||
|
estado["valor_validado"] = valor
|
||||||
|
estado["mono_validado"] = agora
|
||||||
|
estado["candidatas"].clear()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "validada",
|
||||||
|
"valor_validado": valor,
|
||||||
|
"motivo": "primeira referência térmica válida",
|
||||||
|
}
|
||||||
|
|
||||||
|
dt = max(0.001, agora - mono_anterior)
|
||||||
|
delta = valor - anterior
|
||||||
|
|
||||||
|
limite_delta = (
|
||||||
|
self.TEMP_VARIACAO_BASE_C
|
||||||
|
+ (
|
||||||
|
self.TEMP_SUBIDA_MAX_C_S
|
||||||
|
if delta >= 0
|
||||||
|
else self.TEMP_DESCIDA_MAX_C_S
|
||||||
|
) * dt
|
||||||
|
)
|
||||||
|
|
||||||
|
if abs(delta) <= limite_delta:
|
||||||
|
estado["valor_validado"] = valor
|
||||||
|
estado["mono_validado"] = agora
|
||||||
|
estado["candidatas"].clear()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "validada",
|
||||||
|
"valor_validado": valor,
|
||||||
|
"delta_c": delta,
|
||||||
|
"dt_s": dt,
|
||||||
|
"motivo": "variação térmica plausível",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Salto implausível: guarda o possível novo patamar,
|
||||||
|
# mas ainda não o entrega à proteção térmica.
|
||||||
|
estado["candidatas"].append({
|
||||||
|
"valor": valor,
|
||||||
|
"mono": agora,
|
||||||
|
})
|
||||||
|
|
||||||
|
candidatas = list(estado["candidatas"])
|
||||||
|
|
||||||
|
patamar_coerente = (
|
||||||
|
len(candidatas) >= self.TEMP_CONFIRMAR_PATAMAR_AMOSTRAS
|
||||||
|
and (
|
||||||
|
max(x["valor"] for x in candidatas)
|
||||||
|
- min(x["valor"] for x in candidatas)
|
||||||
|
) <= self.TEMP_CONFIRMAR_PATAMAR_TOL_C
|
||||||
|
)
|
||||||
|
|
||||||
|
if patamar_coerente:
|
||||||
|
valor_confirmado = sum(
|
||||||
|
x["valor"] for x in candidatas
|
||||||
|
) / len(candidatas)
|
||||||
|
|
||||||
|
estado["valor_validado"] = valor_confirmado
|
||||||
|
estado["mono_validado"] = agora
|
||||||
|
estado["candidatas"].clear()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "patamar_confirmado",
|
||||||
|
"valor_validado": valor_confirmado,
|
||||||
|
"delta_c": valor_confirmado - anterior,
|
||||||
|
"dt_s": dt,
|
||||||
|
"motivo": (
|
||||||
|
"novo patamar confirmado por "
|
||||||
|
f"{self.TEMP_CONFIRMAR_PATAMAR_AMOSTRAS} amostras"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "suspeita",
|
||||||
|
"valor_validado": anterior,
|
||||||
|
"delta_c": delta,
|
||||||
|
"dt_s": dt,
|
||||||
|
"amostras_candidatas": len(candidatas),
|
||||||
|
"motivo": "salto térmico em quarentena",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _resetar_candidatos_temperatura(self, endereco, regra_desc):
|
||||||
|
for nivel in ("alerta", "falha", "emergencia"):
|
||||||
|
chave = (
|
||||||
|
str(endereco),
|
||||||
|
f"temperatura_{nivel}_{regra_desc}",
|
||||||
|
)
|
||||||
|
|
||||||
|
estado = self._estados_persistencia.get(chave)
|
||||||
|
|
||||||
|
if estado and not estado.get("confirmada", False):
|
||||||
|
estado["ativa_desde"] = None
|
||||||
|
estado["normal_desde"] = None
|
||||||
|
estado["amostras_ativas"] = 0
|
||||||
|
|
||||||
|
def _resultado_temperatura_inconclusiva(
|
||||||
|
self,
|
||||||
|
endereco,
|
||||||
|
regra_desc,
|
||||||
|
motivos,
|
||||||
|
condicoes,
|
||||||
|
):
|
||||||
|
def obter_estado(nivel):
|
||||||
|
chave = (
|
||||||
|
str(endereco),
|
||||||
|
f"temperatura_{nivel}_{regra_desc}",
|
||||||
|
)
|
||||||
|
return self._estados_persistencia.get(chave, {})
|
||||||
|
|
||||||
|
emergencia = obter_estado("emergencia")
|
||||||
|
falha = obter_estado("falha")
|
||||||
|
alerta = obter_estado("alerta")
|
||||||
|
|
||||||
|
nivel_emergencia = emergencia.get("nivel")
|
||||||
|
nivel_falha = falha.get("nivel")
|
||||||
|
nivel_alerta = alerta.get("nivel")
|
||||||
|
|
||||||
|
if nivel_emergencia in ("confirmado", "recuperando"):
|
||||||
|
return (90, motivos, condicoes, True)
|
||||||
|
|
||||||
|
if nivel_falha in ("confirmado", "recuperando"):
|
||||||
|
return (80, motivos, condicoes, True)
|
||||||
|
|
||||||
|
if nivel_alerta in ("confirmado", "recuperando"):
|
||||||
|
return (25, motivos, condicoes, False)
|
||||||
|
|
||||||
|
return (0, motivos, condicoes, False)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _bool(valor, default=False):
|
def _bool(valor, default=False):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,15 @@ class RegrasTaticas:
|
||||||
freio_necessario = freio_necessario or freio
|
freio_necessario = freio_necessario or freio
|
||||||
|
|
||||||
novos_motivos, freio = self._avaliar_oak_parada_por_bloqueio(
|
novos_motivos, freio = self._avaliar_oak_parada_por_bloqueio(
|
||||||
controle=controle
|
controle=controle,
|
||||||
|
operacao=operacao
|
||||||
)
|
)
|
||||||
motivos.extend(novos_motivos)
|
motivos.extend(novos_motivos)
|
||||||
freio_necessario = freio_necessario or freio
|
freio_necessario = freio_necessario or freio
|
||||||
|
|
||||||
novos_motivos, freio = self._avaliar_imu_parada_por_inclinacao(
|
novos_motivos, freio = self._avaliar_imu_parada_por_inclinacao(
|
||||||
controle=controle
|
controle=controle,
|
||||||
|
operacao=operacao
|
||||||
)
|
)
|
||||||
motivos.extend(novos_motivos)
|
motivos.extend(novos_motivos)
|
||||||
freio_necessario = freio_necessario or freio
|
freio_necessario = freio_necessario or freio
|
||||||
|
|
@ -124,7 +126,7 @@ class RegrasTaticas:
|
||||||
|
|
||||||
return motivos, freio_necessario
|
return motivos, freio_necessario
|
||||||
|
|
||||||
def _avaliar_oak_parada_por_bloqueio(self, controle: dict):
|
def _avaliar_oak_parada_por_bloqueio(self, controle: dict, operacao: dict):
|
||||||
"""
|
"""
|
||||||
Bloqueia somente se a função de parada por bloqueio estiver ativa.
|
Bloqueia somente se a função de parada por bloqueio estiver ativa.
|
||||||
|
|
||||||
|
|
@ -132,6 +134,12 @@ class RegrasTaticas:
|
||||||
Esses são auxílios, não garantias absolutas de segurança.
|
Esses são auxílios, não garantias absolutas de segurança.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
sonar_mandatorio = self._modulo_mandatorio(operacao, T_Code.Snr)
|
||||||
|
if not sonar_mandatorio:
|
||||||
|
self._resetar_persistencia("oak_indisponivel")
|
||||||
|
self._resetar_persistencia("oak_obstaculo")
|
||||||
|
return [], False
|
||||||
|
|
||||||
oak_parada_por_bloqueio = self._bool(
|
oak_parada_por_bloqueio = self._bool(
|
||||||
controle.get("oak_parada_por_bloqueio", False)
|
controle.get("oak_parada_por_bloqueio", False)
|
||||||
)
|
)
|
||||||
|
|
@ -234,12 +242,10 @@ class RegrasTaticas:
|
||||||
|
|
||||||
return [], False
|
return [], False
|
||||||
|
|
||||||
def _avaliar_imu_parada_por_inclinacao(self, controle: dict):
|
def _avaliar_imu_parada_por_inclinacao(self, controle: dict, operacao: dict):
|
||||||
imu_parada_por_inclinacao = self._bool(
|
protecao_ativa = self._bool(controle.get("imu_parada_por_inclinacao", False))
|
||||||
controle.get("imu_parada_por_inclinacao", False)
|
imu_mandatorio = self._modulo_mandatorio(operacao, T_Code.Imu)
|
||||||
)
|
if not protecao_ativa or not imu_mandatorio:
|
||||||
|
|
||||||
if not imu_parada_por_inclinacao:
|
|
||||||
self._resetar_persistencia("imu_indisponivel")
|
self._resetar_persistencia("imu_indisponivel")
|
||||||
self._resetar_persistencia("imu_perigo")
|
self._resetar_persistencia("imu_perigo")
|
||||||
return [], False
|
return [], False
|
||||||
|
|
@ -537,6 +543,30 @@ class RegrasTaticas:
|
||||||
|
|
||||||
return idade_s <= max_idade_s, idade_ms
|
return idade_s <= max_idade_s, idade_ms
|
||||||
|
|
||||||
|
def _modulo_mandatorio(self, operacao: dict, t_code: T_Code) -> bool:
|
||||||
|
# Se o campo ainda não chegou ao Python, preserva a proteção atual.
|
||||||
|
if "modulos_mandatorios" not in operacao:
|
||||||
|
return True
|
||||||
|
|
||||||
|
valores = operacao.get("modulos_mandatorios", []) or []
|
||||||
|
|
||||||
|
normalizados = set()
|
||||||
|
|
||||||
|
for item in valores:
|
||||||
|
try:
|
||||||
|
if isinstance(item, dict):
|
||||||
|
item = item.get(
|
||||||
|
"modulo",
|
||||||
|
item.get("codigo", item.get("t_code"))
|
||||||
|
)
|
||||||
|
|
||||||
|
item = getattr(item, "value", item)
|
||||||
|
normalizados.add(int(item))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
return int(t_code.value) in normalizados
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Helpers genéricos seguros
|
# Helpers genéricos seguros
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ WEED_DEFAULT_CONFIG = {
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Gate global: só libera bicos quando existe alvo suficiente no frame.
|
# Gate global: só libera bicos quando existe alvo suficiente no frame.
|
||||||
"usar_radar_global_gate": True,
|
"usar_radar_global_gate": True,
|
||||||
"inverter_ordem_bicos": True,
|
"inverter_ordem_bicos": False,
|
||||||
"inverter_sentido_vertical_bicos": True,
|
"inverter_sentido_vertical_bicos": True,
|
||||||
|
|
||||||
# Histerese global da fração de alvo no frame completo.
|
# Histerese global da fração de alvo no frame completo.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue