ajustado calculo de vazao por fluxo, protocolo daly bms, protocolo oid, can service robusto
This commit is contained in:
parent
824f2bad58
commit
92a845aec0
|
|
@ -155,15 +155,13 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
get
|
||||
{
|
||||
double direta = VazaoFluxoMLpSInstantanea;
|
||||
|
||||
if (direta > 0)
|
||||
return direta;
|
||||
|
||||
return VazaoFluxoMLpSInstantaneaCalc;
|
||||
return ObterVazaoAtualConfiavelMLs(DateTime.Now);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public double VazaoMaximaSensor_mLs => VazaoMaximaSensor_LMin * 1000.0 / 60.0;
|
||||
|
||||
public double VazaoMediaMLs { get; set; }
|
||||
public double VolumeVazadoML { get; set; }
|
||||
public double TempoAtuado { get; set; }
|
||||
|
|
@ -226,25 +224,38 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
var sensor = SensorFluxoLinha;
|
||||
|
||||
if (sensor == null || !sensor.Inicializado || sensor.ValoresLeituras == null)
|
||||
if (sensor == null ||
|
||||
!sensor.Inicializado ||
|
||||
sensor.ValoresLeituras == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var leituraFluxo = sensor.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Fluxo);
|
||||
double maximaMLs =
|
||||
VazaoMaximaSensor_LMin *
|
||||
1000.0 /
|
||||
60.0;
|
||||
|
||||
var leituraFluxo =
|
||||
sensor.ValoresLeituras.FirstOrDefault(
|
||||
x => x.funcao == FuncoesPinout.Fluxo
|
||||
);
|
||||
|
||||
/*
|
||||
* A leitura direta é prioritária, inclusive quando vale zero.
|
||||
* Zero recente é uma medição válida, não motivo para usar
|
||||
* pulsos antigos como fallback.
|
||||
* IMPORTANTE:
|
||||
* A leitura direta FuncoesPinout.Fluxo já está em mL/s no modelo.
|
||||
* Não converter novamente de L/min para mL/s.
|
||||
*
|
||||
* Zero recente é válido. Se a linha está sem fluxo, o sensor deve
|
||||
* poder dizer zero sem cair no fallback por pulso antigo.
|
||||
*/
|
||||
if (LeituraSensorRecente(leituraFluxo, agora))
|
||||
{
|
||||
double vazaoLMin;
|
||||
double vazaoMLs;
|
||||
|
||||
try
|
||||
{
|
||||
vazaoLMin =
|
||||
vazaoMLs =
|
||||
Convert.ToDouble(
|
||||
leituraFluxo.atual.valor
|
||||
);
|
||||
|
|
@ -254,25 +265,23 @@ namespace AgroBase.Models.Modules
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (double.IsNaN(vazaoLMin) ||
|
||||
double.IsInfinity(vazaoLMin) ||
|
||||
vazaoLMin < 0)
|
||||
if (double.IsNaN(vazaoMLs) ||
|
||||
double.IsInfinity(vazaoMLs) ||
|
||||
vazaoMLs < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
vazaoLMin = FuncoesMatematicas.Clamp(
|
||||
vazaoLMin,
|
||||
return FuncoesMatematicas.Clamp(
|
||||
vazaoMLs,
|
||||
0.0,
|
||||
VazaoMaximaSensor_LMin
|
||||
maximaMLs
|
||||
);
|
||||
|
||||
return vazaoLMin * 1000.0 / 60.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Somente usa o cálculo por pulsos se os dois valores
|
||||
* necessários também forem recentes.
|
||||
* Fallback por pulso:
|
||||
* aqui sim a fórmula calcula q em L/min e depois converte para mL/s.
|
||||
*/
|
||||
var leituraPulsos =
|
||||
sensor.ValoresLeituras.FirstOrDefault(
|
||||
|
|
@ -300,11 +309,6 @@ namespace AgroBase.Models.Modules
|
|||
return 0;
|
||||
}
|
||||
|
||||
double maximaMLs =
|
||||
VazaoMaximaSensor_LMin *
|
||||
1000.0 /
|
||||
60.0;
|
||||
|
||||
return FuncoesMatematicas.Clamp(
|
||||
calculadaMLs,
|
||||
0.0,
|
||||
|
|
@ -639,13 +643,10 @@ namespace AgroBase.Models.Modules
|
|||
volumeIntervaloML /
|
||||
bicosDoIntervalo.Count;
|
||||
|
||||
foreach (var bico in
|
||||
bicosDoIntervalo)
|
||||
foreach (var bico in bicosDoIntervalo)
|
||||
{
|
||||
bico.TempoComFluxo += dt;
|
||||
|
||||
bico.VolumeVazadoML +=
|
||||
volumePorBicoML;
|
||||
bico.VolumeVazadoML += volumePorBicoML;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -258,7 +258,8 @@ namespace AgroBase.Models
|
|||
{
|
||||
public byte EnderecoTx { get; set; }
|
||||
public byte EnderecoRx { get; set; }
|
||||
public bool Iniciado
|
||||
public bool Iniciado { get; set; }
|
||||
public bool Iniciado_old
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
|
|||
|
|
@ -129,6 +129,61 @@ namespace AgroBase.Services
|
|||
return $"{Id}_{data[0]}_{parametro}";
|
||||
}
|
||||
|
||||
public static string GerarChaveCorrelacao(T_Code dispositivo, uint idTx, uint idRx, byte funcCodeTx, byte funcCodeRx, byte[] dataTx)
|
||||
{
|
||||
dataTx = dataTx ?? new byte[0];
|
||||
|
||||
switch (dispositivo)
|
||||
{
|
||||
case T_Code.Bat:
|
||||
/*
|
||||
* Daly usa DataID no CAN ID estendido.
|
||||
* Então o idRx já diferencia 0x90, 0x91, 0x95...
|
||||
*/
|
||||
return
|
||||
$"{dispositivo}|tx:{IdKey(idTx)}|rx:{IdKey(idRx)}";
|
||||
|
||||
case T_Code.Oid:
|
||||
/*
|
||||
* OID consulta:
|
||||
* TX/RX DATA[0] = 0x0F
|
||||
* DATA[1] = parâmetro.
|
||||
*/
|
||||
return
|
||||
$"{dispositivo}|tx:{IdKey(idTx)}|rx:{IdKey(idRx)}|fc:0x{funcCodeRx:X2}|param:{ByteKey(dataTx, 1)}";
|
||||
|
||||
case T_Code.Mks:
|
||||
return
|
||||
$"{dispositivo}|tx:{IdKey(idTx)}|rx:{IdKey(idRx)}|fc:0x{funcCodeRx:X2}";
|
||||
|
||||
case T_Code.Atu:
|
||||
case T_Code.Sen:
|
||||
/*
|
||||
* Protocolo dos módulos usa posição/funcCode e normalmente
|
||||
* ID_Num em DATA[1].
|
||||
*/
|
||||
return
|
||||
$"{dispositivo}|tx:{IdKey(idTx)}|rx:{IdKey(idRx)}|fc:0x{funcCodeRx:X2}|id_num:{ByteKey(dataTx, 1)}";
|
||||
|
||||
default:
|
||||
return
|
||||
$"{dispositivo}|tx:{IdKey(idTx)}|rx:{IdKey(idRx)}|fc:0x{funcCodeRx:X2}|p1:{ByteKey(dataTx, 1)}";
|
||||
}
|
||||
}
|
||||
|
||||
private static string ByteKey(byte[] data, int index)
|
||||
{
|
||||
if (data == null || data.Length <= index)
|
||||
return "--";
|
||||
|
||||
return "0x" + data[index].ToString("X2");
|
||||
}
|
||||
|
||||
public static string IdKey(uint id)
|
||||
{
|
||||
return $"0x{id:X}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CanMessage
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
return MensagensPendentes
|
||||
.Where(x => x.ComResposta && x.Enviado && !x.Respondido)
|
||||
.GroupBy(x => IdKey(x.IdRx))
|
||||
.GroupBy(x => CanManager.IdKey(x.IdRx))
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
}
|
||||
}
|
||||
|
|
@ -139,6 +139,33 @@ namespace AgroBase.Services
|
|||
private DateTime _inicioJanelaLatencia = DateTime.UtcNow;
|
||||
private double _latenciaMaxJanelaMs = 0;
|
||||
|
||||
private readonly ConcurrentDictionary<string, DateTime> _cooldownPorCanal =
|
||||
new ConcurrentDictionary<string, DateTime>();
|
||||
|
||||
private readonly ConcurrentDictionary<string, DateTime> _ultimaFilaPorChave =
|
||||
new ConcurrentDictionary<string, DateTime>();
|
||||
|
||||
private static readonly TimeSpan COOLDOWN_DEFAULT =
|
||||
TimeSpan.FromMilliseconds(0);
|
||||
|
||||
private static readonly TimeSpan COOLDOWN_MKS =
|
||||
TimeSpan.FromMilliseconds(20);
|
||||
|
||||
private static readonly TimeSpan COOLDOWN_OID =
|
||||
TimeSpan.FromMilliseconds(8);
|
||||
|
||||
private static readonly TimeSpan COOLDOWN_BAT =
|
||||
TimeSpan.FromMilliseconds(80);
|
||||
|
||||
private static readonly TimeSpan COOLDOWN_ATU_SEN =
|
||||
TimeSpan.FromMilliseconds(4);
|
||||
|
||||
private static readonly TimeSpan DEDUPE_JANELA_GET =
|
||||
TimeSpan.FromMilliseconds(250);
|
||||
|
||||
private static readonly TimeSpan DEDUPE_JANELA_SET =
|
||||
TimeSpan.FromMilliseconds(60);
|
||||
|
||||
private static long NowUnixMs()
|
||||
{
|
||||
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
|
|
@ -340,6 +367,16 @@ namespace AgroBase.Services
|
|||
if (dados.Count > 8)
|
||||
throw new InvalidOperationException($"Payload CAN maior que 8 bytes. ID=0x{idTx:X}, Len={dados.Count}");
|
||||
|
||||
string chaveCorrelacao =
|
||||
CanManager.GerarChaveCorrelacao(
|
||||
Dispositivo,
|
||||
idTx,
|
||||
idRx,
|
||||
funcCodeTx,
|
||||
funcCodeRx,
|
||||
dados.ToArray()
|
||||
);
|
||||
|
||||
var novaMensagem = new CanMessage
|
||||
{
|
||||
Momento = DateTime.Now,
|
||||
|
|
@ -350,25 +387,70 @@ namespace AgroBase.Services
|
|||
funcCodeRx = funcCodeRx,
|
||||
DataTx = dados.ToArray(),
|
||||
ComResposta = get,
|
||||
ChaveInterna = CanManager.GerarChaveMensagem(Dispositivo, idRx, dados.ToArray()),
|
||||
ChaveInterna = chaveCorrelacao,
|
||||
ContabilizarTimeoutHealth = contabilizarTimeoutHealth,
|
||||
MensagemDescoberta = mensagemDescoberta
|
||||
};
|
||||
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
bool mensagemNaFila = MensagensPendentes.Any(x =>
|
||||
(!x.Enviado || (x.ComResposta && x.Enviado && !x.Respondido)) &&
|
||||
x.IdTx == idTx &&
|
||||
x.IdRx == idRx &&
|
||||
x.DataTx != null &&
|
||||
x.DataTx.SequenceEqual(dados.ToArray()));
|
||||
DateTime agora =
|
||||
DateTime.Now;
|
||||
|
||||
if (!mensagemNaFila)
|
||||
/*
|
||||
* Para request com resposta, não deixa duas pendências iguais
|
||||
* ficarem esperando a mesma resposta. Isso é vital para OID, porque
|
||||
* todas as consultas respondem com funcCode 0x0F e diferem pelo param.
|
||||
*/
|
||||
bool mesmaPendenciaAberta =
|
||||
MensagensPendentes.Any(x =>
|
||||
x.ComResposta &&
|
||||
!x.Respondido &&
|
||||
x.ChaveInterna == chaveCorrelacao);
|
||||
|
||||
if (get && mesmaPendenciaAberta)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Para comando sem resposta, evita empilhar comandos idênticos
|
||||
* ainda não enviados. Depois de enviado, ele será limpo normalmente.
|
||||
*/
|
||||
bool comandoIdenticoNaoEnviado =
|
||||
MensagensPendentes.Any(x =>
|
||||
!x.ComResposta &&
|
||||
!x.Enviado &&
|
||||
x.IdTx == idTx &&
|
||||
x.DataTx != null &&
|
||||
x.DataTx.SequenceEqual(dados.ToArray()));
|
||||
|
||||
if (!get && comandoIdenticoNaoEnviado)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Dedupe temporal extra, útil para comandos de controle que chegam
|
||||
* repetidos por loop rápido.
|
||||
*/
|
||||
TimeSpan janela =
|
||||
get
|
||||
? DEDUPE_JANELA_GET
|
||||
: DEDUPE_JANELA_SET;
|
||||
|
||||
if (_ultimaFilaPorChave.TryGetValue(
|
||||
chaveCorrelacao,
|
||||
out DateTime ultima) &&
|
||||
agora - ultima < janela)
|
||||
{
|
||||
MensagensPendentes.Add(novaMensagem);
|
||||
MostrarLog($"Mensagem adicionada. ID=0x{idTx:X}, Data={BitConverter.ToString(novaMensagem.DataTx)}", idTx);
|
||||
return;
|
||||
}
|
||||
|
||||
_ultimaFilaPorChave[chaveCorrelacao] = agora;
|
||||
|
||||
MensagensPendentes.Add(novaMensagem);
|
||||
|
||||
MostrarLog(
|
||||
$"Mensagem adicionada. ID=0x{idTx:X}, Data={BitConverter.ToString(novaMensagem.DataTx)}",
|
||||
idTx
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -402,23 +484,62 @@ namespace AgroBase.Services
|
|||
}
|
||||
|
||||
private readonly ConcurrentDictionary<uint, DateTime> _cooldownPorId = new ConcurrentDictionary<uint, DateTime>();
|
||||
private static readonly TimeSpan COOLDOWN_MKS = TimeSpan.FromMilliseconds(20);
|
||||
|
||||
private bool PodeEnviarAgora(CanMessage m)
|
||||
{
|
||||
if (m.Dispositivo != T_Code.Mks)
|
||||
if (m == null)
|
||||
return false;
|
||||
|
||||
TimeSpan cooldown =
|
||||
ObterCooldownDispositivo(m.Dispositivo);
|
||||
|
||||
if (cooldown <= TimeSpan.Zero)
|
||||
return true;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var due = _cooldownPorId.GetOrAdd(m.IdTx, DateTime.MinValue);
|
||||
string chave =
|
||||
$"{m.Dispositivo}|tx:{CanManager.IdKey(m.IdTx)}";
|
||||
|
||||
var now =
|
||||
DateTime.UtcNow;
|
||||
|
||||
var due =
|
||||
_cooldownPorCanal.GetOrAdd(
|
||||
chave,
|
||||
DateTime.MinValue
|
||||
);
|
||||
|
||||
if (now < due)
|
||||
return false;
|
||||
|
||||
_cooldownPorId[m.IdTx] = now + COOLDOWN_MKS;
|
||||
_cooldownPorCanal[chave] =
|
||||
now + cooldown;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static TimeSpan ObterCooldownDispositivo(T_Code dispositivo)
|
||||
{
|
||||
switch (dispositivo)
|
||||
{
|
||||
case T_Code.Mks:
|
||||
return COOLDOWN_MKS;
|
||||
|
||||
case T_Code.Oid:
|
||||
return COOLDOWN_OID;
|
||||
|
||||
case T_Code.Bat:
|
||||
return COOLDOWN_BAT;
|
||||
|
||||
case T_Code.Atu:
|
||||
case T_Code.Sen:
|
||||
return COOLDOWN_ATU_SEN;
|
||||
|
||||
default:
|
||||
return COOLDOWN_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task EnviarCAN()
|
||||
{
|
||||
if (!IsConnected) return;
|
||||
|
|
@ -498,7 +619,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
|
||||
_tcpStream.Write(packet, 0, packet.Length);
|
||||
_tcpStream.Flush();
|
||||
//_tcpStream.Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -686,29 +807,17 @@ namespace AgroBase.Services
|
|||
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
mensagem = MensagensPendentes.FirstOrDefault(x =>
|
||||
{
|
||||
if (!x.Enviado) return false;
|
||||
if (x.Respondido) return false;
|
||||
if (x.Dispositivo != dispositivo) return false;
|
||||
if (x.IdRx != id) return false;
|
||||
if (x.DataTx == null || x.DataTx.Length == 0) return false;
|
||||
|
||||
if (db)
|
||||
return true;
|
||||
|
||||
// MKS: a resposta deve casar pelo function code.
|
||||
// TX: [code, ..., crc]
|
||||
// RX: [code, ..., crc]
|
||||
if (x.Dispositivo == T_Code.Mks)
|
||||
return data.Length > 0 && x.funcCodeRx == data[0];
|
||||
|
||||
// Demais protocolos antigos
|
||||
if (data.Length < 2 || x.DataTx.Length < 2)
|
||||
return true;
|
||||
|
||||
return x.DataTx[1] == data[1];
|
||||
});
|
||||
mensagem =
|
||||
MensagensPendentes
|
||||
.Where(x =>
|
||||
MensagemCorrespondeResposta(
|
||||
x,
|
||||
dispositivo,
|
||||
id,
|
||||
data
|
||||
))
|
||||
.OrderBy(x => x.EnviadoEm)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (mensagem == null)
|
||||
|
|
@ -745,6 +854,112 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
private bool MensagemCorrespondeResposta(CanMessage pendente, T_Code dispositivoResposta, uint idResposta, byte[] dataResposta)
|
||||
{
|
||||
if (pendente == null)
|
||||
return false;
|
||||
|
||||
if (!pendente.Enviado)
|
||||
return false;
|
||||
|
||||
if (pendente.Respondido)
|
||||
return false;
|
||||
|
||||
if (!pendente.ComResposta)
|
||||
return false;
|
||||
|
||||
if (pendente.Dispositivo != dispositivoResposta)
|
||||
return false;
|
||||
|
||||
if (pendente.IdRx != idResposta)
|
||||
return false;
|
||||
|
||||
byte[] dataTx =
|
||||
pendente.DataTx ?? new byte[0];
|
||||
|
||||
byte[] dataRx =
|
||||
dataResposta ?? new byte[0];
|
||||
|
||||
switch (pendente.Dispositivo)
|
||||
{
|
||||
case T_Code.Bat:
|
||||
/*
|
||||
* Daly: DataID vem no CAN ID, então idRx já fechou.
|
||||
*/
|
||||
return true;
|
||||
|
||||
case T_Code.Mks:
|
||||
return
|
||||
dataRx.Length > 0 &&
|
||||
pendente.funcCodeRx == dataRx[0];
|
||||
|
||||
case T_Code.Oid:
|
||||
/*
|
||||
* OID:
|
||||
* consulta retorna DATA[0]=0x0F e DATA[1]=param.
|
||||
*/
|
||||
return
|
||||
dataRx.Length >= 2 &&
|
||||
dataTx.Length >= 2 &&
|
||||
dataRx[0] == pendente.funcCodeRx &&
|
||||
dataRx[1] == dataTx[1];
|
||||
|
||||
case T_Code.Atu:
|
||||
case T_Code.Sen:
|
||||
/*
|
||||
* ATU/SEN:
|
||||
* casa pelo func/posição e, quando existir, pelo ID_Num.
|
||||
*/
|
||||
if (dataRx.Length <= 0)
|
||||
return false;
|
||||
|
||||
if (dataRx[0] != pendente.funcCodeRx)
|
||||
return false;
|
||||
|
||||
if (dataRx.Length >= 2 && dataTx.Length >= 2)
|
||||
return dataRx[1] == dataTx[1];
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
if (dataRx.Length <= 0)
|
||||
return false;
|
||||
|
||||
if (dataRx[0] != pendente.funcCodeRx)
|
||||
return false;
|
||||
|
||||
if (dataRx.Length >= 2 && dataTx.Length >= 2)
|
||||
return dataRx[1] == dataTx[1];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeSpan TimeoutResposta(CanMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
return TimeSpan.FromSeconds(2);
|
||||
|
||||
switch (msg.Dispositivo)
|
||||
{
|
||||
case T_Code.Oid:
|
||||
return TimeSpan.FromMilliseconds(900);
|
||||
|
||||
case T_Code.Bat:
|
||||
return TimeSpan.FromMilliseconds(1500);
|
||||
|
||||
case T_Code.Mks:
|
||||
return TimeSpan.FromMilliseconds(900);
|
||||
|
||||
case T_Code.Atu:
|
||||
case T_Code.Sen:
|
||||
return TimeSpan.FromMilliseconds(1200);
|
||||
|
||||
default:
|
||||
return TimeSpan.FromSeconds(2);
|
||||
}
|
||||
}
|
||||
|
||||
private void LimparMensagensAntigas()
|
||||
{
|
||||
var agora = DateTime.Now;
|
||||
|
|
@ -756,7 +971,8 @@ namespace AgroBase.Services
|
|||
x.ComResposta &&
|
||||
x.Enviado &&
|
||||
!x.Respondido &&
|
||||
x.EnviadoEm < agora.AddSeconds(-2))
|
||||
x.EnviadoEm != DateTime.MinValue &&
|
||||
agora - x.EnviadoEm > TimeoutResposta(x))
|
||||
.ToList();
|
||||
|
||||
foreach (var item in timeouts)
|
||||
|
|
@ -765,12 +981,14 @@ namespace AgroBase.Services
|
|||
MensagensPendentes.Remove(item);
|
||||
}
|
||||
|
||||
var remover = MensagensPendentes
|
||||
.Where(x =>
|
||||
(!x.ComResposta && x.Enviado) ||
|
||||
(x.ComResposta && x.Respondido) ||
|
||||
(x.Momento < agora.AddSeconds(-30)))
|
||||
.ToList();
|
||||
var remover =
|
||||
MensagensPendentes
|
||||
.Where(x =>
|
||||
(!x.ComResposta && x.Enviado) ||
|
||||
(x.ComResposta && x.Respondido) ||
|
||||
(x.Momento < agora.AddSeconds(-30)) ||
|
||||
(!IsConnected && x.Enviado))
|
||||
.ToList();
|
||||
|
||||
foreach (var item in remover)
|
||||
MensagensPendentes.Remove(item);
|
||||
|
|
@ -836,6 +1054,8 @@ namespace AgroBase.Services
|
|||
{
|
||||
try
|
||||
{
|
||||
LimparPendenciasEnviadasPorReconexao();
|
||||
|
||||
FecharConexaoSemLimparHandlers();
|
||||
|
||||
if (TransportMode == CanEthernetTransportMode.Tcp)
|
||||
|
|
@ -863,6 +1083,22 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
private void LimparPendenciasEnviadasPorReconexao()
|
||||
{
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
var remover =
|
||||
MensagensPendentes
|
||||
.Where(x =>
|
||||
x.Enviado ||
|
||||
x.Momento < DateTime.Now.AddSeconds(-2))
|
||||
.ToList();
|
||||
|
||||
foreach (var item in remover)
|
||||
MensagensPendentes.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void FecharConexaoSemLimparHandlers()
|
||||
{
|
||||
lock (_socketLock)
|
||||
|
|
@ -1032,17 +1268,12 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
private static string IdKey(uint id)
|
||||
{
|
||||
return $"0x{id:X}";
|
||||
}
|
||||
|
||||
private static string TimeoutChave(CanMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
return "desconhecido";
|
||||
|
||||
string chave = $"{msg.Dispositivo}|tx:{IdKey(msg.IdTx)}|rx:{IdKey(msg.IdRx)}|fc_rx:0x{msg.funcCodeRx:X2}";
|
||||
string chave = $"{msg.Dispositivo}|tx:{CanManager.IdKey(msg.IdTx)}|rx:{CanManager.IdKey(msg.IdRx)}|fc_rx:0x{msg.funcCodeRx:X2}";
|
||||
|
||||
if (new List<T_Code>() { T_Code.Atu, T_Code.Sen }.Contains(msg.Dispositivo) && msg.DataTx != null && msg.DataTx.Length > 1)
|
||||
{
|
||||
|
|
@ -1054,6 +1285,11 @@ namespace AgroBase.Services
|
|||
chave += $"|fc_tx:0x{msg.DataTx[0]:X2}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(msg.ChaveInterna))
|
||||
{
|
||||
chave += $"|key:{msg.ChaveInterna}";
|
||||
}
|
||||
|
||||
return chave;
|
||||
}
|
||||
|
||||
|
|
@ -1075,7 +1311,7 @@ namespace AgroBase.Services
|
|||
|
||||
TimeoutsRespostaTotal++;
|
||||
|
||||
string idRx = IdKey(msg.IdRx);
|
||||
string idRx = CanManager.IdKey(msg.IdRx);
|
||||
string dispositivo = msg.Dispositivo.ToString();
|
||||
string chave = TimeoutChave(msg);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -144,14 +144,6 @@ namespace AgroBase.Services
|
|||
|
||||
public void IniciarOperacao(double distanciaTotalMetrosAtual, double tempoTotalSegundosAtual)
|
||||
{
|
||||
/*
|
||||
* Antes, se o contador ainda não tivesse recebido uma leitura
|
||||
* válida do reservatório, o início era ignorado.
|
||||
*
|
||||
* Isso fazia VolumeConsumidoOperacao_L ficar zero durante toda
|
||||
* a operação quando a primeira leitura válida chegava depois
|
||||
* do comando de iniciar.
|
||||
*/
|
||||
if (!_inicializado)
|
||||
{
|
||||
OperacaoAtiva = true;
|
||||
|
|
@ -229,19 +221,24 @@ namespace AgroBase.Services
|
|||
|
||||
if (!_inicializado)
|
||||
{
|
||||
Inicializar(distanciaTotalMetrosAtual, tempoTotalSegundosAtual);
|
||||
Inicializar(
|
||||
distanciaTotalMetrosAtual,
|
||||
tempoTotalSegundosAtual
|
||||
);
|
||||
|
||||
if (OperacaoAtiva || _inicioOperacaoPendente)
|
||||
{
|
||||
/*
|
||||
* Primeira leitura válida depois do comando de iniciar.
|
||||
* A operação passa a usar o volume atual como referência
|
||||
* inicial, sem perder o estado de OperacaoAtiva.
|
||||
* Primeira leitura válida depois do início da operação.
|
||||
* Usa o volume atual como baseline da operação para começar
|
||||
* PercentualConsumidoOperacao em zero.
|
||||
*/
|
||||
_volumeInicialOperacao_L = VolumeAtual_L;
|
||||
|
||||
_distanciaTotalAnterior_m = distanciaTotalMetrosAtual;
|
||||
_tempoTotalAnterior_s = tempoTotalSegundosAtual;
|
||||
_distanciaTotalAnterior_m =
|
||||
distanciaTotalMetrosAtual;
|
||||
_tempoTotalAnterior_s =
|
||||
tempoTotalSegundosAtual;
|
||||
|
||||
OperacaoAtiva = true;
|
||||
OperacaoFinalizada = false;
|
||||
|
|
@ -306,15 +303,24 @@ namespace AgroBase.Services
|
|||
|
||||
private double ObterVazaoFluxo_mLs()
|
||||
{
|
||||
var dados = Variaveis.OperacaoEmAndamento.DispAtu?.Dados;
|
||||
var dados =
|
||||
Variaveis
|
||||
.OperacaoEmAndamento
|
||||
.DispAtu?
|
||||
.Dados;
|
||||
|
||||
if (dados == null)
|
||||
return 0;
|
||||
|
||||
double vazao = dados.VazaoFluxoMLpSReferencia;
|
||||
double vazao =
|
||||
dados.VazaoFluxoMLpSReferencia;
|
||||
|
||||
if (vazao < 0)
|
||||
if (double.IsNaN(vazao) ||
|
||||
double.IsInfinity(vazao) ||
|
||||
vazao < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vazao;
|
||||
}
|
||||
|
|
@ -399,11 +405,15 @@ namespace AgroBase.Services
|
|||
?? 0;
|
||||
|
||||
if (qtdAtuacoes > 0)
|
||||
{
|
||||
HerbicidaPorAtuacao_L =
|
||||
VolumeConsumidoOperacao_L /
|
||||
qtdAtuacoes;
|
||||
}
|
||||
else
|
||||
{
|
||||
HerbicidaPorAtuacao_L = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void AtualizarAutonomia(double velocidadeAtual_m_s)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue