agrobot_base/AgroBase/AgroBase/Models/Modules/ModuloIMUModel.cs

455 lines
15 KiB
C#

using AgroBase.Models.Operadores;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using static AgroBase.Models.Enums;
namespace AgroBase.Models.Modules
{
public class ModuloIMUModel
{
public DateTime Momento { get; set; }
// ============================================================
// Estado geral
// ============================================================
public bool valido { get; set; }
/// <summary>
/// normal, single_source, redundante, sem_imu, inicializando...
/// </summary>
public string modo { get; set; } = "";
// ============================================================
// Atitude final do robô
// ============================================================
public double roll { get; set; }
public double pitch { get; set; }
public double yaw { get; set; }
public double roll_filtrado { get; set; }
public double pitch_filtrado { get; set; }
public double yaw_filtrado { get; set; }
/// <summary>
/// Compatibilidade com contrato antigo.
/// Normalmente espelha roll_filtrado.
/// </summary>
public double roll_seg { get; set; }
/// <summary>
/// Compatibilidade com contrato antigo.
/// Normalmente espelha pitch_filtrado.
/// </summary>
public double pitch_seg { get; set; }
// ============================================================
// Tendência angular
// ============================================================
public double roll_rate_dps { get; set; }
public double pitch_rate_dps { get; set; }
public double yaw_rate_dps { get; set; }
public double roll_rate_filtrado_dps { get; set; }
public double pitch_rate_filtrado_dps { get; set; }
// ============================================================
// Risco / segurança operacional
// ============================================================
/// <summary>
/// normal, attention, risk, critical, emergency, unknown.
/// </summary>
public string risk_level { get; set; } = "unknown";
/// <summary>
/// 0 normal, 1 attention, 2 risk, 3 critical, 4 emergency.
/// </summary>
public int risk_level_num { get; set; }
public double risk_score { get; set; }
public List<string> motivos_risco { get; set; } = new List<string>();
/// <summary>
/// normal, reduzir_velocidade_leve, reduzir_velocidade_forte,
/// parada_controlada, emergency_stop.
/// </summary>
public string acao_sugerida { get; set; } = "";
/// <summary>
/// Fator recomendado para movimentação.
/// 1.0 = normal, 0.70 = reduz leve, 0.35 = reduz forte, 0 = parar.
/// </summary>
public double velocidade_factor { get; set; } = 1.0;
public bool bloquear_avanco { get; set; }
public bool stop { get; set; }
public bool emergency_stop { get; set; }
// ============================================================
// Recuperação / intervenção humana
// ============================================================
public bool recovery_required { get; set; }
public bool recovery_mode_allowed { get; set; }
public bool auto_resume_allowed { get; set; }
public bool stable_to_resume { get; set; }
public double stable_to_resume_s { get; set; }
/// <summary>
/// aguardar_operador, operador_pode_liberar_retomada, sem_acao...
/// </summary>
public string recovery_hint { get; set; } = "";
// ============================================================
// Fusão / redundância dos sensores
// ============================================================
public double confidence { get; set; }
public string fonte_principal { get; set; } = "";
public bool single_source { get; set; }
public int sensors_count { get; set; }
public int sensors_valid_count { get; set; }
public int sensors_ignored_count { get; set; }
public bool sensors_agree { get; set; }
public double roll_disagreement_deg { get; set; }
public double pitch_disagreement_deg { get; set; }
public double max_disagreement_deg { get; set; }
public List<ModuloIMUFonteModel> fontes { get; set; } = new List<ModuloIMUFonteModel>();
public List<ModuloIMUFonteIgnoradaModel> fontes_ignoradas { get; set; } = new List<ModuloIMUFonteIgnoradaModel>();
public List<ModuloIMUFonteDivergenteModel> divergentes { get; set; } = new List<ModuloIMUFonteDivergenteModel>();
// ============================================================
// Índices diagnósticos
// ============================================================
public double estabilidade_idx { get; set; }
public double oscilacao_roll_std { get; set; }
public double oscilacao_pitch_std { get; set; }
public double oscilacao_total_std { get; set; }
// ============================================================
// Tempo / saúde de publicação
// ============================================================
/// <summary>
/// Timestamp principal. Normalmente em ms.
/// </summary>
public double timestamp { get; set; }
/// <summary>
/// Timestamp do último pacote/fusão. Normalmente em segundos.
/// </summary>
public double last_packet_ts { get; set; }
/// <summary>
/// Timestamp da última publicação. Normalmente em segundos.
/// </summary>
public double last_publish_ts { get; set; }
public double ultimo_erro_ts { get; set; }
/// <summary>
/// Latência normalmente em segundos no contrato Python.
/// </summary>
public double latencia { get; set; }
/// <summary>
/// Frequência real do loop final.
/// </summary>
public double frequencia { get; set; }
// ============================================================
// Saúde do módulo final
// ============================================================
public ManagerWorkerMessageResponseModulosPendentesModel saude { get; set; } = new ManagerWorkerMessageResponseModulosPendentesModel();
// ============================================================
// Extras
// ============================================================
[JsonExtensionData]
public IDictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>();
// ============================================================
// Helpers para UI / controle
// ============================================================
[JsonIgnore]
public bool Iniciado => valido;
[JsonIgnore]
public bool Operacional => valido && saude != null && (saude.status == StatusModulo.Operante || saude.status == StatusModulo.Alerta);
[JsonIgnore]
public bool DeveParar =>
emergency_stop ||
stop ||
bloquear_avanco ||
risk_level_num >= 3;
[JsonIgnore]
public bool DeveReduzir =>
!DeveParar && risk_level_num >= 1;
[JsonIgnore]
public double RollSeguro =>
roll_filtrado != 0 ? roll_filtrado :
roll_seg != 0 ? roll_seg :
roll;
[JsonIgnore]
public double PitchSeguro =>
pitch_filtrado != 0 ? pitch_filtrado :
pitch_seg != 0 ? pitch_seg :
pitch;
[JsonIgnore]
public double YawSeguro =>
yaw_filtrado != 0 ? yaw_filtrado : yaw;
[JsonIgnore]
public double LatenciaMsFinal
{
get
{
if (latencia <= 0) return 0;
// Se veio em segundos.
if (latencia < 10) return latencia * 1000.0;
// Se algum dia vier direto em ms.
return latencia;
}
}
[JsonIgnore]
public double TimestampSegundos
{
get
{
if (timestamp <= 0) return 0;
// timestamp em ms.
if (timestamp > 100000000000.0)
return timestamp / 1000.0;
return timestamp;
}
}
[JsonIgnore]
public double IdadeSegundos
{
get
{
var ts = TimestampSegundos;
if (ts <= 0) return 999.0;
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
return Math.Max(0, now - ts);
}
}
public ModuloIMUModel Clone()
{
return new ModuloIMUModel
{
valido = valido,
modo = modo,
roll = roll,
pitch = pitch,
yaw = yaw,
roll_filtrado = roll_filtrado,
pitch_filtrado = pitch_filtrado,
yaw_filtrado = yaw_filtrado,
roll_seg = roll_seg,
pitch_seg = pitch_seg,
roll_rate_dps = roll_rate_dps,
pitch_rate_dps = pitch_rate_dps,
yaw_rate_dps = yaw_rate_dps,
roll_rate_filtrado_dps = roll_rate_filtrado_dps,
pitch_rate_filtrado_dps = pitch_rate_filtrado_dps,
risk_level = risk_level,
risk_level_num = risk_level_num,
risk_score = risk_score,
motivos_risco = motivos_risco?.ToList() ?? new List<string>(),
acao_sugerida = acao_sugerida,
velocidade_factor = velocidade_factor,
bloquear_avanco = bloquear_avanco,
stop = stop,
emergency_stop = emergency_stop,
recovery_required = recovery_required,
recovery_mode_allowed = recovery_mode_allowed,
auto_resume_allowed = auto_resume_allowed,
stable_to_resume = stable_to_resume,
stable_to_resume_s = stable_to_resume_s,
recovery_hint = recovery_hint,
confidence = confidence,
fonte_principal = fonte_principal,
single_source = single_source,
sensors_count = sensors_count,
sensors_valid_count = sensors_valid_count,
sensors_ignored_count = sensors_ignored_count,
sensors_agree = sensors_agree,
roll_disagreement_deg = roll_disagreement_deg,
pitch_disagreement_deg = pitch_disagreement_deg,
max_disagreement_deg = max_disagreement_deg,
fontes = fontes?.Select(x => x.Clone()).ToList() ?? new List<ModuloIMUFonteModel>(),
fontes_ignoradas = fontes_ignoradas?.Select(x => x.Clone()).ToList() ?? new List<ModuloIMUFonteIgnoradaModel>(),
divergentes = divergentes?.Select(x => x.Clone()).ToList() ?? new List<ModuloIMUFonteDivergenteModel>(),
estabilidade_idx = estabilidade_idx,
oscilacao_roll_std = oscilacao_roll_std,
oscilacao_pitch_std = oscilacao_pitch_std,
oscilacao_total_std = oscilacao_total_std,
timestamp = timestamp,
last_packet_ts = last_packet_ts,
last_publish_ts = last_publish_ts,
ultimo_erro_ts = ultimo_erro_ts,
latencia = latencia,
frequencia = frequencia,
saude = saude,
Extra = Extra?.ToDictionary(
x => x.Key,
x => x.Value?.DeepClone()
) ?? new Dictionary<string, JToken>()
};
}
}
public class ModuloIMUFonteModel
{
public string mx_id { get; set; } = "";
public string modelo { get; set; } = "";
public double roll { get; set; }
public double pitch { get; set; }
public double yaw { get; set; }
public double roll_rate_dps { get; set; }
public double pitch_rate_dps { get; set; }
public double idade_s { get; set; }
public double confidence { get; set; }
public double peso_base { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>();
public ModuloIMUFonteModel Clone()
{
return new ModuloIMUFonteModel
{
mx_id = mx_id,
modelo = modelo,
roll = roll,
pitch = pitch,
yaw = yaw,
roll_rate_dps = roll_rate_dps,
pitch_rate_dps = pitch_rate_dps,
idade_s = idade_s,
confidence = confidence,
peso_base = peso_base,
Extra = Extra?.ToDictionary(
x => x.Key,
x => x.Value?.DeepClone()
) ?? new Dictionary<string, JToken>()
};
}
}
public class ModuloIMUFonteIgnoradaModel
{
public string mx_id { get; set; } = "";
public string modelo { get; set; } = "";
public double idade_s { get; set; }
public List<string> motivos { get; set; } = new List<string>();
[JsonExtensionData]
public IDictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>();
public ModuloIMUFonteIgnoradaModel Clone()
{
return new ModuloIMUFonteIgnoradaModel
{
mx_id = mx_id,
modelo = modelo,
idade_s = idade_s,
motivos = motivos?.ToList() ?? new List<string>(),
Extra = Extra?.ToDictionary(
x => x.Key,
x => x.Value?.DeepClone()
) ?? new Dictionary<string, JToken>()
};
}
}
public class ModuloIMUFonteDivergenteModel
{
public string mx_id { get; set; } = "";
public double roll { get; set; }
public double pitch { get; set; }
public double confidence { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>();
public ModuloIMUFonteDivergenteModel Clone()
{
return new ModuloIMUFonteDivergenteModel
{
mx_id = mx_id,
roll = roll,
pitch = pitch,
confidence = confidence,
Extra = Extra?.ToDictionary(
x => x.Key,
x => x.Value?.DeepClone()
) ?? new Dictionary<string, JToken>()
};
}
}
}