agrobot_base/AgroBase/AgroBase/Services/LoRaEspService.cs

179 lines
7.1 KiB
C#

using AgroBase.Models;
using AgroBase.Models.Modules;
using static AgroBase.Models.Enums;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Threading.Tasks;
using System.Diagnostics;
namespace AgroBase.Services
{
public class LoRaEspService
{
public string ID { get; set; }
public int ID_Num { get; set; }
public S_Code Componente { get; set; }
public bool Conectado { get; set; }
public bool Configurado { get; set; }
public bool Iniciado
{
get
{
return Conectado && Configurado;
}
}
public double FrequenciaEnvioDadosBase { get; set; } = 0.2;
public double TempoEnvioDadosBase
{
get
{
return 1.0 / FrequenciaEnvioDadosBase * 1000.0;
}
}
public double UltimoRxDados { get; set; } = 0;
public DateTime _ultimoRxDados { get; set; } = DateTime.MinValue;
public double UltimoTxDados { get; set; } = 0;
public DateTime _ultimoTxDados { get; set; } = DateTime.MinValue;
public LoRaParametrosModel ParametrosSet { get; set; } = new LoRaParametrosModel();
public LoRaParametrosModel ParametrosGet { get; set; } = new LoRaParametrosModel();
public List<FuncoesPinout> Funcoes { get; set; }
public List<LoRaMensagemModel> MensagensRecebidasAvulsas { get; set; } = new List<LoRaMensagemModel>();
public List<(int, CanMessagePosicaoDados, byte[])> ProtocoloConfiguracao(PinoutDataModel Pinout, bool conectar)
{
List<byte> config1 = new List<byte>();
var _pinout = Pinout.Clone();
_pinout.Pinos = _pinout.Pinos.Where(x => x.ComponenteID == ID).ToList();
if (_pinout.Pinos.Count > 0)
{
byte[] pinos = PinoutModel.PinoProtocolo(_pinout, Funcoes, false);
config1.AddRange(pinos);
}
List<byte> config2 = new List<byte>
{
Variaveis.LoraBaseParametros?.address ?? 0x01,
ParametrosSet.address,
ParametrosSet.baudAndAirRate,
ParametrosSet.packetSizeAndPower,
ParametrosSet.channel,
ParametrosSet.tranModeAndWorCycle
};
//Console.WriteLine($"Parametros de configuracao LoRa: {FuncoesGlobais.ConverterComandoBytesParaTexto(config2.ToArray())}");
return new List<(int, CanMessagePosicaoDados, byte[])>()
{
(ID_Num, CanMessagePosicaoDados.Config1, config1.ToArray()),
(ID_Num, CanMessagePosicaoDados.Config2, config2.ToArray()),
};
}
public async Task<(bool, LoRaParametrosModel)> RequisitarParametrosModulo()
{
DateTime inicio = DateTime.Now;
bool sucesso = false;
Variaveis.OperacaoEmAndamento.DispSen?.Dados?.RequisitarDadosModulo(Variaveis.ID_Num_sLRA, CanMessagePosicaoDados.Command1, CanMessagePosicaoDados.Dados1, true);
while (!sucesso && inicio.AddMilliseconds(2000) > DateTime.Now)
{
await Task.Delay(10);
sucesso = ParametrosGet.UltimaLeitura < inicio;
}
return (sucesso, ParametrosGet);
}
public async Task<(bool, LoRaParametrosModel)> RedefinirParametros(LoRaParametrosModel parametros)
{
ParametrosSet = parametros.Clone();
Configurado = false;
DateTime inicio = DateTime.Now;
Variaveis.OperacaoEmAndamento.DispSen?.EnviarProtocolosConfiguracao(true);
bool configurado() => Configurado;
await FuncoesGlobais.AguardarCondicaoAsync(configurado, 2000, 100);
return (Configurado, ParametrosGet);
}
public bool EnviarDadosLoRaViaCAN(byte[] payload)
{
if (payload == null) return false;
if (payload.Length > 8) return false;
CanMessagePosicaoDados posicao = ((CanMessagePosicaoDados)payload[0]);
byte idNum = payload[1];
Variaveis.OperacaoEmAndamento.DispSen?.AdicionarMensagemFila(idNum, posicao, posicao, payload.Skip(2).ToArray(), false);
UltimoTxDados = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
_ultimoTxDados = DateTime.UtcNow;
return true;
}
public void EnviarDadosParaBase()
{
if (!Iniciado) return;
var now = DateTime.UtcNow; // evite DST
// --- parâmetros (ajuste) ---
int minIntervaloTxMs = (int)TempoEnvioDadosBase; // mínimo entre TXs do rover
int beaconPeriodoMs = VariaveisEquipamento.TempoEntrePingsConexao; // p.ex. 10000
int janelaMinMs = 1000; // só responda >= 1 s após o beacon
int guardaProximoMs = 2000; // e não responda nos últimos 2 s antes do próximo beacon
// --- idades ---
double ageTxMs = (now - _ultimoTxDados).TotalMilliseconds;
double ageRxMs = (now - _ultimoRxDados).TotalMilliseconds;
// 1) respeite intervalo mínimo entre TX
if (ageTxMs < minIntervaloTxMs) return;
// 2) valide janela após o último beacon
if (ageRxMs < janelaMinMs) return; // cedo demais
if (ageRxMs > beaconPeriodoMs - guardaProximoMs) return; // já muito perto do próximo beacon
// (opcional) se o último beacon já “caducou” (perdeu pings), não envie
if (ageRxMs > beaconPeriodoMs + 2000) return;
// --- ENVIO (ideal: agregue em 1-2 frames) ---
var s = Variaveis.OperacaoEmAndamento.Parametros.DadosLeitura;
foreach (var dado in LoRaSerializer.SerializeGPS(s.Gps))
Variaveis.LoraService?.EnviarDadosLoRaViaCAN(dado);
foreach (var dado in LoRaSerializer.SerializeOperacao(s))
Variaveis.LoraService?.EnviarDadosLoRaViaCAN(dado);
foreach (var dado in LoRaSerializer.SerializeSensores(s))
Variaveis.LoraService?.EnviarDadosLoRaViaCAN(dado);
Variaveis.LoraService?.EnviarDadosLoRaViaCAN(LoRaSerializer.SerializeControleAtual(s.Controle));
}
public enum DadosLoRaParse
{
DadosLivre = (int)CanMessagePosicaoDados.Command2,
GpsQualidade = (int)CanMessagePosicaoDados.Command3,
GpsLatitude = (int)CanMessagePosicaoDados.Command4,
GpsLongitude = (int)CanMessagePosicaoDados.Command5,
GpsGerais = (int)CanMessagePosicaoDados.Command6,
ComandoControle = (int)CanMessagePosicaoDados.Command7,
DadosControle = (int)CanMessagePosicaoDados.Command8,
DadosOperacao = (int)CanMessagePosicaoDados.Command9,
DadosSensores = (int)CanMessagePosicaoDados.Command10,
DadosPulverizador = (int)CanMessagePosicaoDados.Command11,
}
public enum LoRaCaracteresEspeciais
{
BeginMsg = 0xAA,
BreakLine = 0xBB,
EndMsg = 0x55
}
}
}