Implementado CoolerControl

This commit is contained in:
Diego Freitas 2025-12-11 09:29:08 -03:00
parent e14e6c343d
commit 2fddd47b02
13 changed files with 225 additions and 18 deletions

View File

@ -574,6 +574,7 @@
<Compile Include="Services\CameraService.cs" />
<Compile Include="Services\CANService.cs" />
<Compile Include="Services\CANServiceWaveshare.cs" />
<Compile Include="Services\CoolerControlService.cs" />
<Compile Include="Services\CoulombCounterService.cs" />
<Compile Include="Services\DispositivosService.cs" />
<Compile Include="Forms\frmPrincipal.cs">

View File

@ -224,7 +224,9 @@ namespace AgroBase.Models.Modules
_EnderecoCAN_Rx = 0x12,
_TaxaAmostragem = 1000,
RequisitarDados = true,
// 71 ~ 74
BicosPulverizadores = new List<AtuadorBicoModel>(),
// 51 ~ 51
BombasPressurizadoras = new List<AtuadorBombaModel>()
{
new AtuadorBombaModel()
@ -277,6 +279,7 @@ namespace AgroBase.Models.Modules
}
}
},
// 61 ~ 66
Sensores = new List<SensorModel>()
{
new SensorModel()
@ -418,7 +421,6 @@ namespace AgroBase.Models.Modules
}
}
},
new SensorModel()
{
ID = "MAGESQ",
@ -584,9 +586,6 @@ namespace AgroBase.Models.Modules
},
}
},
},
};
for (int i = 0; i < Variaveis.OperacaoEmAndamento.Parametros.QtdBicos; i++)

View File

@ -431,7 +431,8 @@ namespace AgroBase.Models.Modules
PressaoSP,
CodAlarme,
LeituraBruta,
LeituraAngulo
LeituraAngulo,
Cooler
}
public static class PinoutUtils

View File

@ -73,6 +73,7 @@ namespace AgroBase.Models.Modules
power = LoRaBaseService.CodigosPowers[22],
};
public CoulombCounterService ContadorCarga = new CoulombCounterService(VariaveisEquipamento.CorrenteMaximaBateria);
public CoolerControlService CoolerControl = new CoolerControlService();
public AsyncTaskTimerModel tmrRegistrador;
private int _TempoReconexaoComponentes = 5;
private int _VezesLeituraReconexao = 0;
@ -87,6 +88,7 @@ namespace AgroBase.Models.Modules
RequisitarDados = true,
CameraCaminho = new OAKCameraModel(),
CamerasSolo = new List<OAKCameraModel>(),
// 21 ~ 23
Sinaleiros = new List<SensorSinaleiroModel>()
{
new SensorSinaleiroModel()
@ -191,6 +193,7 @@ namespace AgroBase.Models.Modules
},
Sensores = new List<SensorModel>()
{
// sTMP 1 ~ 5
new SensorModel()
{
ID = "TMVET",
@ -411,6 +414,7 @@ namespace AgroBase.Models.Modules
}
}
},
// sCOR 11 ~ 17
new SensorModel()
{
ID = "AB3V",
@ -1122,6 +1126,7 @@ namespace AgroBase.Models.Modules
}
}
},
// Torre 31 ~ 37
new SensorModel()
{
ID = "LUZABT",
@ -1517,7 +1522,7 @@ namespace AgroBase.Models.Modules
}
}
},
// sMAG 91 ~ 94
new SensorModel()
{
ID = "MAGET",
@ -1740,6 +1745,7 @@ namespace AgroBase.Models.Modules
},
},
// 41 ~ 44
Servos = new List<SensorServoModel>()
{
new SensorServoModel()
@ -1867,7 +1873,68 @@ namespace AgroBase.Models.Modules
}
},
},
Reles = new List<SensorReleModel>(),
// 81 ~ 82
Reles = new List<SensorReleModel>()
{
new SensorReleModel()
{
ID = "RLCIN",
ID_Num = 81,
Descricao = "Rele Cooler Entrada",
Componente = S_Code.sRLE,
Controlar = true,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.Cooler,
},
AtuacaoNivelAlto = true,
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.EstadoLeitura,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
}
},
new SensorReleModel()
{
ID = "RLCOUT",
ID_Num = 82,
Descricao = "Rele Cooler Saida",
Componente = S_Code.sRLE,
Controlar = true,
Mandatorio = false,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.Cooler,
},
AtuacaoNivelAlto = true,
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.EstadoLeitura,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
}
},
},
loRaService = new LoRaEspService()
{
ID = "LoRa",
@ -2256,7 +2323,7 @@ namespace AgroBase.Models.Modules
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sLED, ledMN.ID, ledMN.Comportamento);
}
foreach (var rele in Reles.Where(x => x.Controlar && x.Inicializado && !x.ControleExterno))
foreach (var rele in Reles.Where(x => x.Controlar && x.Inicializado))
{
int leituraEstado = (int?)(rele.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.EstadoLeitura)?.atual?.valor) ?? 0;
if (leituraEstado != (int)rele.StatusControle)
@ -2484,7 +2551,6 @@ namespace AgroBase.Models.Modules
public string Descricao { get; set; }
public S_Code Componente { get; set; }
public List<FuncoesPinout> Funcoes { get; set; }
public bool ControleExterno { get; set; }
public bool Controlar { get; set; }
public bool Mandatorio { get; set; }
public bool Liberado

View File

@ -2330,6 +2330,8 @@ namespace AgroBase.Models
var _Bateria = _DispSen?.Dados?.ContadorCarga;
var _Reservatorio = _DispAtu?.Dados?.ContadorCarga;
double tmp_max = Math.Max(Variaveis.OperacaoEmAndamento.DadosPerformance?.GPU_temp ?? 0, Variaveis.OperacaoEmAndamento.DadosPerformance?.CPU_temp ?? 0);
_DispSen?.Dados?.CoolerControl?.Atualizar(tmp_max);
_DispMvd?.Dados?.AtualizarDadosFreio();
_Bateria?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0);
_Reservatorio?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0, Variaveis.OperacaoEmAndamento.TempoDecorridoSegs);
@ -2565,9 +2567,11 @@ namespace AgroBase.Models
public OperacaoSensoriamentoLogModel Clone(bool comSonar = false)
{
var vw = OperadorVisual?.Clone();
vw.CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
if (vw != null)
vw.CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
var ww = OperadorErvas?.Clone();
ww.CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
if (ww != null)
ww.CameraFrames = new Dictionary<CameraFrameType, OAKCameraFrameModel>();
return new OperacaoSensoriamentoLogModel()
{

View File

@ -0,0 +1,135 @@
using AgroBase.Models;
using AgroBase.Models.Modules;
using System;
using System.Linq;
namespace AgroBase.Services
{
public class CoolerControlService
{
// Config
public double TempOn { get; set; } = 30.0; // Liga ventilação básica
public double TempTurbo { get; set; } = 35.0; // Liga entrada+saída
public double TempCritical { get; set; } = 45.0; // Acima disso, alerta
public double Histerese { get; set; } = 1.5; // Para evitar chattering
public TimeSpan MinOnTime { get; set; } = TimeSpan.FromSeconds(30);
public TimeSpan MinOffTime { get; set; } = TimeSpan.FromSeconds(30);
// Filtro de temperatura (EMA)
public double AlphaFiltro { get; set; } = 0.2;
// Estado interno
public CoolerMode ModoAtual { get; private set; } = CoolerMode.Off;
public Enums.Estado CoolerEntradaEstado => ModoAtual == CoolerMode.IntakeAndExhaust ? Enums.Estado.Ligado : Enums.Estado.Desligado;
public Enums.Estado CoolerSaidaEstado => ModoAtual == CoolerMode.ExhaustOnly || ModoAtual == CoolerMode.IntakeAndExhaust ? Enums.Estado.Ligado : Enums.Estado.Desligado;
public double? TemperaturaFiltrada { get; private set; }
public DateTime UltimaMudancaModo { get; private set; } = DateTime.MinValue;
public bool AlertaTemperaturaCritica { get; private set; }
private SensorReleModel ReleCoolerEntrada
{
get
{
var sRLE = Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Reles?.FirstOrDefault(x => x.ID == "RLCIN" && x.Inicializado && x.Controlar);
return sRLE;
}
}
private SensorReleModel ReleCoolerSaida
{
get
{
var sRLE = Variaveis.OperacaoEmAndamento.DispSen?.Dados?.Reles?.FirstOrDefault(x => x.ID == "RLCOUT" && x.Inicializado && x.Controlar);
return sRLE;
}
}
public CoolerControlService()
{
}
public void Reset()
{
ModoAtual = CoolerMode.Off;
TemperaturaFiltrada = null;
UltimaMudancaModo = DateTime.MinValue;
AlertaTemperaturaCritica = false;
}
/// <summary>
/// Chamar a cada tick de atualização com a temperatura interna atual
/// </summary>
public void Atualizar(double temperaturaAtual_C)
{
DateTime agora = DateTime.Now;
// 1) Filtrar temperatura (EMA)
if (TemperaturaFiltrada == null)
{
TemperaturaFiltrada = temperaturaAtual_C;
}
else
{
TemperaturaFiltrada = AlphaFiltro * temperaturaAtual_C + (1 - AlphaFiltro) * TemperaturaFiltrada.Value;
}
var temp = TemperaturaFiltrada.Value;
// 2) Verificar limite crítico (para health_worker)
AlertaTemperaturaCritica = temp >= TempCritical;
// 3) Respeitar permanência mínima de estado
var tempoNoModoAtual = agora - UltimaMudancaModo;
bool podeMudar =
(ModoAtual == CoolerMode.Off && tempoNoModoAtual >= MinOffTime) ||
(ModoAtual != CoolerMode.Off && tempoNoModoAtual >= MinOnTime);
if (!podeMudar)
return;
// 4) Lógica com histerese
CoolerMode novoModo = ModoAtual;
switch (ModoAtual)
{
case CoolerMode.Off:
if (temp >= TempOn + Histerese)
novoModo = CoolerMode.ExhaustOnly;
break;
case CoolerMode.ExhaustOnly:
if (temp <= TempOn - Histerese)
novoModo = CoolerMode.Off;
else if (temp >= TempTurbo + Histerese)
novoModo = CoolerMode.IntakeAndExhaust;
break;
case CoolerMode.IntakeAndExhaust:
if (temp <= TempTurbo - Histerese)
novoModo = CoolerMode.ExhaustOnly;
break;
}
if (novoModo != ModoAtual)
{
ModoAtual = novoModo;
UltimaMudancaModo = agora;
if (ReleCoolerEntrada != null)
GeneralJoystick.EnviarComandoSensoriamento(ReleCoolerEntrada.Componente, ReleCoolerEntrada.ID, CoolerEntradaEstado);
if (ReleCoolerSaida != null)
GeneralJoystick.EnviarComandoSensoriamento(ReleCoolerSaida.Componente, ReleCoolerSaida.ID, CoolerSaidaEstado);
}
}
public enum CoolerMode
{
Off,
ExhaustOnly,
IntakeAndExhaust
}
}
}

View File

@ -8,7 +8,7 @@ namespace AgroBase.Services
{
public class HerbicideCounterService
{
public HerbicideCounterService(double capacidadeLitros, double densidadeProduto, double l_m_base = 0.1188) // 3km/h ~ 3x 35 ml/s
public HerbicideCounterService(double capacidadeLitros, double densidadeProduto, double l_m_base = 0.1188) // 3km/h ~ 3x 33 ml/s
{
CapacidadeLitros = capacidadeLitros;
VolumeAtual_L = 0;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -558,11 +558,11 @@ namespace OperationControl.Windows
break;
case AgroBase.Models.Enums.T_Code.Bat:
iniciado = rover.DadosLeitura?.Bateria?.Iniciado ?? false;
mod_label = "Bateria";
//mod_label = "Bateria";
break;
case AgroBase.Models.Enums.T_Code.Trj:
iniciado = rover.DadosLeitura?.Trajetoria?.QtdPontos > 0;
mod_label = "Trajetória";
//mod_label = "Trajetória";
break;
}
}

View File

@ -248,6 +248,7 @@ void ProcessarCmd(std::vector<uint8_t> data) {
switch (componente) {
case sBIC: {
Rele::ComandarRele(listaBicos, data);
ComponenteCAN::ProcessarComponentesCAN(listaBicos, {}, false, id, false, false, CanMessagePosicaoDados::Dados1, EnviarDadosCAN);
break;
}
case sBMB: {