455 lines
15 KiB
C#
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>()
|
|
};
|
|
}
|
|
}
|
|
|
|
}
|