From 1e2622cfb319b1df7ec2ffa2131d6340b37891c4 Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Thu, 17 Jul 2025 14:53:42 -0300 Subject: [PATCH] Retorno do CAN Serial --- AgroBase/AgroBase/AgroBase.csproj | 1 + AgroBase/AgroBase/Forms/frmMKS.cs | 8 +- .../AgroBase/Models/Modules/AtuadorModel.cs | 6 +- .../Models/Modules/SensoriamentoModel.cs | 12 +- AgroBase/AgroBase/Services/CANService.cs | 94 ++-- .../AgroBase/Services/DispositivosService.cs | 2 +- .../AgroBase/Services/MKS057DCanService.cs | 12 +- AgroBase/AgroBase/Services/OIDCanService.cs | 12 +- .../AgroBase/Services/SerialCanService.cs | 468 ++++++++++++++++++ AgroBase/AgroBase/Services/SerialService.cs | 28 +- 10 files changed, 580 insertions(+), 63 deletions(-) create mode 100644 AgroBase/AgroBase/Services/SerialCanService.cs diff --git a/AgroBase/AgroBase/AgroBase.csproj b/AgroBase/AgroBase/AgroBase.csproj index 170a72d3c..06f4115da 100644 --- a/AgroBase/AgroBase/AgroBase.csproj +++ b/AgroBase/AgroBase/AgroBase.csproj @@ -589,6 +589,7 @@ + diff --git a/AgroBase/AgroBase/Forms/frmMKS.cs b/AgroBase/AgroBase/Forms/frmMKS.cs index 01fe84291..7fcf0d045 100644 --- a/AgroBase/AgroBase/Forms/frmMKS.cs +++ b/AgroBase/AgroBase/Forms/frmMKS.cs @@ -2375,14 +2375,14 @@ namespace AgroBase.Forms private bool MKSIniciado() { - CanService.InicializarCanal(); + CanManager.CanService.Inicializar(); byte addr = FuncoesGlobais.StringToHexByte(cmbEndereco.Text); - if (CanService.IsConnected && MKS057DCanService.DadosLeitura.Any(x => x.EnderecoTx == addr && !x.Iniciado)) + if (CanManager.CanService.IsConnected && MKS057DCanService.DadosLeitura.Any(x => x.EnderecoTx == addr && !x.Iniciado)) { MKS057DCanService.RequisitarDado(addr, addr, MKS057DCanService.MksHandler.MksFuncCode.Versao); } - return CanService.IsConnected; + return CanManager.CanService.IsConnected; } private void btnMovimentoCAN_Click(object sender, EventArgs e) @@ -2413,7 +2413,7 @@ namespace AgroBase.Forms private async void btnRequisitarDados_Click(object sender, EventArgs e) { byte addr = FuncoesGlobais.StringToHexByte(cmbEndereco.Text); - CanService.RegistrarHandler(addr, MKS057DCanService._MksHandler); + CanManager.CanService.RegistrarHandler(addr, MKS057DCanService._MksHandler); await tmrReq_Tick(); } diff --git a/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs b/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs index a7689cf24..cea10e9de 100644 --- a/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs +++ b/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs @@ -501,11 +501,11 @@ namespace AgroBase.Models.Modules public async Task VerificaDispositivoConectado() { - if (!CanService.IsConnected) return false; + if (!CanManager.CanService.IsConnected) return false; if (Variaveis.OperacaoEmAndamento.DispAtu == null) return false; - CanService.RegistrarHandler(_EnderecoCAN_Rx, _AtuHandler); + CanManager.CanService.RegistrarHandler(_EnderecoCAN_Rx, _AtuHandler); RequisitarDadosModulo(Variaveis.ID_Num_sMOD, CanMessagePosicaoDados.Status, CanMessagePosicaoDados.Status, true); @@ -529,7 +529,7 @@ namespace AgroBase.Models.Modules SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel() { Dispositivo = DadosLeitura.Dispositivo, - Endereco = CanService._portName, + Endereco = CanManager.CanService._portName, Versao = DadosLeitura.Versao.ToString(), Mod_ID = Modulo_ID }); diff --git a/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs b/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs index a97d27755..7e5043c88 100644 --- a/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs +++ b/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs @@ -1748,11 +1748,11 @@ namespace AgroBase.Models.Modules public async Task VerificaDispositivoConectado() { - if (!CanService.IsConnected) return false; + if (!CanManager.CanService.IsConnected) return false; if (Variaveis.OperacaoEmAndamento.DispSen == null) return false; - CanService.RegistrarHandler(_EnderecoCAN_Rx, _SenHandler); + CanManager.CanService.RegistrarHandler(_EnderecoCAN_Rx, _SenHandler); RequisitarDadosModulo(Variaveis.ID_Num_sMOD, CanMessagePosicaoDados.Status, CanMessagePosicaoDados.Status, true); @@ -1775,7 +1775,7 @@ namespace AgroBase.Models.Modules { SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel() { - Endereco = CanService._portName, + Endereco = CanManager.CanService._portName, Dispositivo = DadosLeitura.Dispositivo, Versao = DadosLeitura.Versao.ToString(), Mod_ID = Modulo_ID, @@ -2801,7 +2801,7 @@ namespace AgroBase.Models.Modules var Dados = Variaveis.OperacaoEmAndamento.DispSen.Dados; - Console.WriteLine($"[SEN] Componente={componente.ToString()}, ID_Num={ID_Num}, Posicao={posicao.ToString()}, Data={FuncoesGlobais.ConverterComandoBytesParaTexto(data)}"); + //Console.WriteLine($"[SEN] Componente={componente.ToString()}, ID_Num={ID_Num}, Posicao={posicao.ToString()}, Data={FuncoesGlobais.ConverterComandoBytesParaTexto(data)}"); switch (componente) { @@ -2815,7 +2815,7 @@ namespace AgroBase.Models.Modules bool conectado = data[3] == 1; int versao = data[4]; - Console.WriteLine($"[SEN] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}"); + //Console.WriteLine($"[SEN] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}"); Dados.DadosLeitura.Conectado = conectado; Dados.DadosLeitura.Dispositivo = tipoModulo; @@ -2951,7 +2951,7 @@ namespace AgroBase.Models.Modules { double latenciaLoop = ConverterByteParaDouble(data, 2) * 100; Dados.DadosLeitura.LatenciaLoop = Convert.ToInt32(latenciaLoop); - Console.WriteLine($"[SEN] Ciclo concluido. Latencia de loop: {latenciaLoop} ms"); + //Console.WriteLine($"[SEN] Ciclo concluido. Latencia de loop: {latenciaLoop} ms"); break; } } diff --git a/AgroBase/AgroBase/Services/CANService.cs b/AgroBase/AgroBase/Services/CANService.cs index 15d99c356..967481494 100644 --- a/AgroBase/AgroBase/Services/CANService.cs +++ b/AgroBase/AgroBase/Services/CANService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO.Ports; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -10,6 +11,30 @@ using static WaveshareCANFDService; namespace AgroBase.Services { + public class CanManager + { + public static bool UseCanSerial = true; + + private static CanServiceWaveshare CanWaveshare = new CanServiceWaveshare(); + private static CanServiceSerial CanSerial = new CanServiceSerial(); + + public static ICanService CanService + { + get + { + if (UseCanSerial) + { + return CanSerial; + } + else + { + return CanWaveshare; + } + } + } + + } + public class CanMessage { public DateTime Momento { get; set; } @@ -122,30 +147,41 @@ namespace AgroBase.Services void ProcessarMensagem(CanMessage mensagem); } - public class CanService : IDisposable + public interface ICanService : IDisposable { - public static WaveshareCANFDService Connector = new WaveshareCANFDService(0); - public static string _portName = "CAN-FD"; - public static bool IsConnected { get; private set; } - public static bool Iniciado + void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler); + bool Inicializar(SerialPort Porta = null); + void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true); + void Fechar(); + string _portName { get; set; } + bool Iniciado { get; } + bool IsConnected { get; } + } + + public class CanServiceWaveshare : ICanService + { + public WaveshareCANFDService Connector = new WaveshareCANFDService(0); + public string _portName { get; set; } = "CAN-FD"; + public bool IsConnected { get; private set; } + public bool Iniciado { get { return SerialService.DispositivosCan.Any(d => SerialService.DispositivosMapeados.Any(x => x.Status == StatusModulo.Operante && x.Dispositivo == d)); } } - private static AsyncTaskTimerModel tmrEnviarCAN; - private static AsyncTaskTimerModel tmrOuvirCAN; - private static AsyncTaskTimerModel tmrProcessarCAN; - private static StringBuilder SerialCANBuffer = new StringBuilder(); - private static ConcurrentQueue<(byte, byte[])> FramesRecebidos = new ConcurrentQueue<(byte, byte[])>(); - private static readonly ConcurrentDictionary _roteadores = new ConcurrentDictionary(); - private static byte? _ultimoIdEnviado = null; - private static List MensagensPendentes = new List(); - private static readonly object _LockMensagens = new object(); - private static bool DebugMode = false; + private AsyncTaskTimerModel tmrEnviarCAN; + private AsyncTaskTimerModel tmrOuvirCAN; + private AsyncTaskTimerModel tmrProcessarCAN; + private StringBuilder SerialCANBuffer = new StringBuilder(); + private ConcurrentQueue<(byte, byte[])> FramesRecebidos = new ConcurrentQueue<(byte, byte[])>(); + private readonly ConcurrentDictionary _roteadores = new ConcurrentDictionary(); + private byte? _ultimoIdEnviado = null; + private List MensagensPendentes = new List(); + private readonly object _LockMensagens = new object(); + private bool DebugMode = false; - private static void MostrarLog(string message, byte id) + private void MostrarLog(string message, byte id) { if (DebugMode && new List() { 0x01 }.Contains(id)) { @@ -153,13 +189,13 @@ namespace AgroBase.Services } } - public static void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler) + public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler) { _roteadores[endereco_rx] = handler; MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx); } - public static bool InicializarCanal() + public bool Inicializar(SerialPort Porta = null) { bool canIniciado = Connector.IsOpen; @@ -202,7 +238,7 @@ namespace AgroBase.Services return canIniciado; } - private static async Task ProcessarCAN() + private async Task ProcessarCAN() { int mensagensParaProcessar = Math.Min(FramesRecebidos.Count, 10); @@ -221,7 +257,7 @@ namespace AgroBase.Services LimparMensagensAntigas(); } - private static void ProcessarFrameCAN((byte, byte[]) frame) + private void ProcessarFrameCAN((byte, byte[]) frame) { byte id = frame.Item1; byte[] data = frame.Item2; @@ -268,7 +304,7 @@ namespace AgroBase.Services } } - public static void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true) + public void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true) { if (!Connector.IsOpen) return; @@ -313,7 +349,7 @@ namespace AgroBase.Services } } - private static CanMessage ObterProximaMensagem() + private CanMessage ObterProximaMensagem() { List mensagensPendentes; @@ -365,7 +401,7 @@ namespace AgroBase.Services return proximaMsg; } - private static async Task EnviarCAN() + private async Task EnviarCAN() { if (!Connector.IsOpen) return; @@ -396,7 +432,7 @@ namespace AgroBase.Services } } - private static string CriarFrame(CanMessage msg) + private string CriarFrame(CanMessage msg) { string frame = $"t{msg.IdTx:X3}{msg.DataTx.Length}"; foreach (byte b in msg.DataTx) @@ -407,7 +443,7 @@ namespace AgroBase.Services return frame; } - private static bool EnviarComando(string comando, CanMessage msg = null) + private bool EnviarComando(string comando, CanMessage msg = null) { if (Connector.IsOpen) { @@ -427,7 +463,7 @@ namespace AgroBase.Services return false; } - private static void LimparMensagensAntigas() + private void LimparMensagensAntigas() { var agora = DateTime.Now; @@ -448,7 +484,7 @@ namespace AgroBase.Services } } - public static void Fechar() + public void Fechar() { tmrOuvirCAN?.Dispose(); tmrEnviarCAN?.Dispose(); @@ -473,7 +509,7 @@ namespace AgroBase.Services Fechar(); } - private static byte CalcularChecksum(byte id, byte[] data) + private byte CalcularChecksum(byte id, byte[] data) { int soma = id; foreach (var b in data) @@ -481,7 +517,7 @@ namespace AgroBase.Services return (byte)(soma & 0xFF); } - private static string GerarChaveMensagem(T_Code Dispositivo, byte Id, byte[] data) + private string GerarChaveMensagem(T_Code Dispositivo, byte Id, byte[] data) { byte parametro = 0x00; diff --git a/AgroBase/AgroBase/Services/DispositivosService.cs b/AgroBase/AgroBase/Services/DispositivosService.cs index c4455716a..631b2fa20 100644 --- a/AgroBase/AgroBase/Services/DispositivosService.cs +++ b/AgroBase/AgroBase/Services/DispositivosService.cs @@ -74,7 +74,7 @@ namespace AgroBase.Comum { payload.AddRange(dados); } - CanService.AdicionarMensagemNaFila(Dispositivo, Dados._EnderecoCAN_Tx, Dados._EnderecoCAN_Rx, (byte)PosicaoTx, (byte)PosicaoRx, payload.ToArray(), get); + CanManager.CanService.AdicionarMensagemNaFila(Dispositivo, Dados._EnderecoCAN_Tx, Dados._EnderecoCAN_Rx, (byte)PosicaoTx, (byte)PosicaoRx, payload.ToArray(), get); } public Control InstanciarDispositivo(int index) diff --git a/AgroBase/AgroBase/Services/MKS057DCanService.cs b/AgroBase/AgroBase/Services/MKS057DCanService.cs index 07b27e169..9514df222 100644 --- a/AgroBase/AgroBase/Services/MKS057DCanService.cs +++ b/AgroBase/AgroBase/Services/MKS057DCanService.cs @@ -19,7 +19,7 @@ namespace AgroBase.Services { get { - return CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Dir) && DadosLeitura.Any(x => x.Iniciado); + return CanManager.CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Dir) && DadosLeitura.Any(x => x.Iniciado); } } public static bool Referenciando @@ -40,7 +40,7 @@ namespace AgroBase.Services public static async Task VerificaDispositivoConectado() { - if (!CanService.IsConnected) return false; + if (!CanManager.CanService.IsConnected) return false; if (Variaveis.OperacaoEmAndamento.DispMvd == null) return false; @@ -48,7 +48,7 @@ namespace AgroBase.Services foreach (var Modulo in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => !x.DirMotor.Inicializado)) { - CanService.RegistrarHandler(Modulo.DirMotor._EnderecoCAN_Rx, _MksHandler); + CanManager.CanService.RegistrarHandler(Modulo.DirMotor._EnderecoCAN_Rx, _MksHandler); RequisitarDado(Modulo.DirMotor._EnderecoCAN_Tx, Modulo.DirMotor._EnderecoCAN_Rx, MksFuncCode.Versao); @@ -67,7 +67,7 @@ namespace AgroBase.Services DefinirDispositivo(Mod_IDs); } - return Iniciado; + return Mod_IDs.Any(); } private static void DefinirDispositivo(List Mod_IDs) @@ -84,7 +84,7 @@ namespace AgroBase.Services SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel() { Dispositivo = T_Code.Dir, - Endereco = CanService._portName, + Endereco = CanManager.CanService._portName, Versao = (Leitura.FV ?? "").ToString(), Mod_ID = Mod_ID, }); @@ -120,7 +120,7 @@ namespace AgroBase.Services private static void EnviarComando(T_Code Dispositivo, byte idTx, byte idRx, byte funcCode, byte[] payload = null, bool get = true) { - CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get); + CanManager.CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get); } public static void SetModoOperacao(byte idTx, byte idRx, byte modo) diff --git a/AgroBase/AgroBase/Services/OIDCanService.cs b/AgroBase/AgroBase/Services/OIDCanService.cs index 946f8a295..2d2182690 100644 --- a/AgroBase/AgroBase/Services/OIDCanService.cs +++ b/AgroBase/AgroBase/Services/OIDCanService.cs @@ -20,7 +20,7 @@ namespace AgroBase.Services { get { - return CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Mov) && DadosLeitura.Any(x => x.Iniciado); + return CanManager.CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Mov) && DadosLeitura.Any(x => x.Iniciado); } } public static int TaxaAmostragem @@ -33,7 +33,7 @@ namespace AgroBase.Services public static async Task VerificaDispositivoConectado() { - if (!CanService.IsConnected) return false; + if (!CanManager.CanService.IsConnected) return false; if (Variaveis.OperacaoEmAndamento.DispMvd == null) return false; @@ -41,7 +41,7 @@ namespace AgroBase.Services foreach (var Modulo in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => !x.MovMotor.Inicializado)) { - CanService.RegistrarHandler(Modulo.MovMotor._EnderecoCAN_Rx, _OidHandler); + CanManager.CanService.RegistrarHandler(Modulo.MovMotor._EnderecoCAN_Rx, _OidHandler); RequisitarDado(Modulo.MovMotor._EnderecoCAN_Tx, Modulo.MovMotor._EnderecoCAN_Rx, OidParametros.CodErro); @@ -60,7 +60,7 @@ namespace AgroBase.Services DefinirDispositivo(Mod_IDs); } - return Iniciado; + return Mod_IDs.Any(); } private static void DefinirDispositivo(List Mod_IDs) @@ -89,7 +89,7 @@ namespace AgroBase.Services SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel() { Dispositivo = T_Code.Mov, - Endereco = CanService._portName, + Endereco = CanManager.CanService._portName, Versao = "1.0", Mod_ID = Mod_ID, }); @@ -103,7 +103,7 @@ namespace AgroBase.Services private static void EnviarComando(T_Code Dispositivo, byte idTx, byte idRx, byte funcCode, byte[] payload = null, bool get = true) { - CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get); + CanManager.CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get); } public static void RequisitarDado(byte idTx, byte idRx, OidParametros dado) diff --git a/AgroBase/AgroBase/Services/SerialCanService.cs b/AgroBase/AgroBase/Services/SerialCanService.cs new file mode 100644 index 000000000..d75bd0de5 --- /dev/null +++ b/AgroBase/AgroBase/Services/SerialCanService.cs @@ -0,0 +1,468 @@ +using AgroBase.Models; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO.Ports; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using static AgroBase.Models.Enums; + +namespace AgroBase.Services +{ + public class CanServiceSerial : ICanService + { + public SerialPort _PortaCAN; + public string _portName { get; set; } + private int _baudRate; + private object _lock = new object(); + public bool IsConnected { get; private set; } + public bool Iniciado + { + get + { + return SerialService.DispositivosCan.Any(d => SerialService.DispositivosMapeados.Any(x => x.Status != StatusModulo.Desconectado && x.Dispositivo == d)); + } + } + private AsyncTaskTimerModel tmrEnviarCAN; + private AsyncTaskTimerModel tmrOuvirCAN; + private AsyncTaskTimerModel tmrProcessarCAN; + private ConcurrentQueue FramesRecebidos = new ConcurrentQueue(); + private StringBuilder SerialCANBuffer = new StringBuilder(); + private readonly ConcurrentDictionary _roteadores = new ConcurrentDictionary(); + private byte? _ultimoIdEnviado = null; + + private List Mensagens = new List(); + private readonly object _LockMensagens = new object(); + + private bool DebugMode = false; + private void MostrarLog(string message, byte id) + { + if (DebugMode && new List() { 0x01 }.Contains(id)) + { + Console.WriteLine($"[CAN] {message}"); + } + } + + public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler) + { + _roteadores[endereco_rx] = handler; + MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx); + } + + public bool Inicializar(SerialPort Porta = null) + { + if (Porta == null && (_PortaCAN?.IsOpen ?? false)) + { + Porta = _PortaCAN; + } + + if (!IsConnected) + { + Porta.Close(); + } + + if (_PortaCAN == null || !IsConnected) + { + lock (_lock) + { + _portName = Porta.PortName; + _baudRate = 115200; + + _PortaCAN = new SerialPort(_portName, _baudRate, Parity.None, 8, StopBits.One); + _PortaCAN.Open(); + + EnviarComando("S6\r"); // 500Kbps + Thread.Sleep(100); + EnviarComando("O\r"); // Open CAN + Thread.Sleep(100); + + tmrOuvirCAN?.Dispose(); + tmrOuvirCAN = new AsyncTaskTimerModel("tmrOuvirCAN", OuvirCAN, 5); + tmrOuvirCAN.Start(); + + tmrEnviarCAN?.Dispose(); + tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 5); + tmrEnviarCAN.Start(); + + tmrProcessarCAN?.Dispose(); + tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 10); + tmrProcessarCAN.Start(); + + IsConnected = true; + } + } + + if (_PortaCAN != null && !_PortaCAN.IsOpen) + { + _PortaCAN.Open(); + } + + return IsConnected; + } + + private async Task ProcessarCAN() + { + int mensagensParaProcessar = Math.Min(FramesRecebidos.Count, 10); + + for (int i = 0; i < mensagensParaProcessar; i++) + { + if (FramesRecebidos.TryDequeue(out string frame)) + { + ProcessarFrameCAN(frame); + } + else + { + // A fila ficou vazia antes de atingir o limite + break; + } + } + LimparMensagensAntigas(); + } + + private async Task OuvirCAN() + { + if (_PortaCAN.BytesToRead > 0) + { + byte[] tempBuffer = await SerialService.LerDadosDaPortaSerialAsync(_PortaCAN, _PortaCAN.BytesToRead); + + foreach (byte b in tempBuffer) + { + char c = (char)b; + + if (c == '\r') + { + var frame = SerialCANBuffer.ToString(); + SerialCANBuffer.Clear(); + + if (!string.IsNullOrWhiteSpace(frame)) + { + FramesRecebidos.Enqueue(frame); + } + } + else + { + SerialCANBuffer.Append(c); + } + } + } + } + + private void ProcessarFrameCAN(string frame) + { + // Ex: t00183301020304 + if (!frame.StartsWith("t")) return; + + try + { + if (frame.Length < 5) return; + + int id = Convert.ToInt32(frame.Substring(1, 3), 16); + int len = int.Parse(frame.Substring(4, 1)); + + byte[] data = new byte[len]; + for (int i = 0; i < len; i++) + { + data[i] = Convert.ToByte(frame.Substring(5 + i * 2, 2), 16); + } + + MostrarLog($"Processando frame: {frame}", (byte)id); + + if (_roteadores.TryGetValue((byte)id, out ICanMessageHandler handler)) + { + + } + + T_Code dispositivo = handler?.Dispositivo ?? T_Code.Vzo; + byte fCodeRx = data[0]; + byte parametroRx = data[1]; + string chave = GerarChaveMensagem(dispositivo, (byte)id, data); + + CanMessage mensagem = null; + lock (_LockMensagens) + { + mensagem = Mensagens.FirstOrDefault(x => x.Enviado && !x.Respondido && x.Dispositivo == x.Dispositivo && x.IdRx == id && x.DataTx[1] == data[1]); + } + if (mensagem == null) + { + mensagem = new CanMessage { Dispositivo = dispositivo, IdTx = (byte)id, IdRx = (byte)id, funcCodeRx = fCodeRx, DataRx = data, ChaveInterna = chave }; + } + mensagem.DataRx = data; + mensagem.Respondido = true; + mensagem.RespondidoEm = DateTime.Now; + + if ((handler?.Dispositivo ?? T_Code.Vzo) != T_Code.Vzo) + { + handler.ProcessarMensagem(mensagem); + } + else + { + MostrarLog($"Roteador não registrado! ID_Num não tratado: 0x{id:X2}, fCodeRx: 0x{fCodeRx:X2}, frame: {frame}", (byte)id); + } + + + + + /*byte[] data = new byte[len]; + for (int i = 0; i < len; i++) + { + data[i] = Convert.ToByte(frame.Substring(5 + i * 2, 2), 16); + } + + byte funcCodeRx = data[0]; + var mensagem = new CanMessage(); + + lock (Mensagens) + { + mensagem = Mensagens.Where(x => x.IdTx == id).OrderBy(x => x.EnviadoEm).FirstOrDefault(x => x.Enviado && !x.Respondido && x.funcCodeRx == funcCodeRx); + } + + if (mensagem == null) + { + mensagem = new CanMessage { IdTx = (byte)id, DataRx = data }; + } + + mensagem.DataRx = data; + mensagem.Respondido = true; + mensagem.RespondidoEm = DateTime.Now; + + if (_roteadores.TryGetValue(mensagem.IdTx, out var handler)) + { + handler.ProcessarMensagem(mensagem); + } + else + { + Console.WriteLine($"[CAN] ID_Num não tratado: 0x{funcCodeRx:X2}"); + }*/ + } + catch (Exception ex) + { + Console.WriteLine($"[CAN] Erro ao processar frame: {ex.Message}"); + } + } + + public void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true) + { + if (_PortaCAN?.IsOpen != true) return; + + var dados = new List() { funcCodeTx }; + + if (payload != null) + dados.AddRange(payload); + + if (Dispositivo == T_Code.Mks) + { + byte crc = CalcularChecksum(idTx, dados.ToArray()); + dados.Add(crc); + } + + var novaMensagem = new CanMessage + { + Momento = DateTime.Now, + Dispositivo = Dispositivo, + IdTx = idTx, + IdRx = idRx, + funcCodeTx = funcCodeTx, + funcCodeRx = funcCodeRx, + DataTx = dados.ToArray(), + ComResposta = get, + ChaveInterna = GerarChaveMensagem(Dispositivo, idRx, dados.ToArray()) + }; + + bool mensagemNaFila = false; + + lock (_LockMensagens) + { + mensagemNaFila = Mensagens.Any(x => (!x.Enviado || (x.ComResposta && x.Enviado && !x.Respondido)) && x.IdTx == idTx && x.IdRx == idRx && x.DataTx.SequenceEqual(dados.ToArray())); + if (!mensagemNaFila) + { + Mensagens.Add(novaMensagem); + MostrarLog($"Mensagem adicionada na fila. ID: {novaMensagem.IdTx}, Data: {FuncoesGlobais.ConverterComandoBytesParaTexto(novaMensagem.DataTx)}", idTx); + } + else + { + MostrarLog($"Mensagem já pendente e idêntica. ID: {novaMensagem.IdTx}, Data: {FuncoesGlobais.ConverterComandoBytesParaTexto(novaMensagem.DataTx)}", idTx); + } + } + } + + private CanMessage ObterProximaMensagem() + { + List mensagensPendentes; + + lock (_LockMensagens) + { + mensagensPendentes = Mensagens + .Where(x => !x.Enviado) + .ToList(); + } + + if (!mensagensPendentes.Any()) + return null; + + // Primeiro tenta pegar mensagens com Get == false + var mensagensPrioritarias = mensagensPendentes + .Where(x => !x.ComResposta) + .ToList(); + + // Se não houver mensagens prioritárias, considera todas + var mensagensParaProcessar = mensagensPrioritarias.Any() + ? mensagensPrioritarias + : mensagensPendentes; + + // Agrupa por IdTx e ordena os grupos + var idsDisponiveis = mensagensParaProcessar + .Select(x => x.IdTx) + .Distinct() + .OrderBy(x => x) + .ToList(); + + byte proximoId; + + if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value)) + { + proximoId = idsDisponiveis.First(); + } + else + { + var index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value); + proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.First(); + } + + // Seleciona a mensagem mais antiga com esse IdTx + var proximaMsg = mensagensParaProcessar + .Where(x => x.IdTx == proximoId) + .OrderBy(x => x.Momento) + .FirstOrDefault(); + + return proximaMsg; + } + + private async Task EnviarCAN() + { + if (_PortaCAN?.IsOpen != true) return; + + CanMessage msg = ObterProximaMensagem(); + + if (msg != null) + { + string frame = CriarFrame(msg); + + if (_ultimoIdEnviado.HasValue && msg.IdTx == _ultimoIdEnviado.Value) + { + await Task.Delay(5); + } + + if (msg.Dispositivo == T_Code.Mks) + { + await Task.Delay(10); + } + + if (!EnviarComando(frame)) + { + MostrarLog("Erro ao enviar frame", msg.IdTx); + } + + msg.Enviado = true; + msg.EnviadoEm = DateTime.Now; + _ultimoIdEnviado = msg.IdTx; + } + } + + private string CriarFrame(CanMessage msg) + { + string frame = $"t{msg.IdTx:X3}{msg.DataTx.Length}"; + foreach (byte b in msg.DataTx) + frame += b.ToString("X2"); + + frame += "\r"; + + return frame; + } + + private bool EnviarComando(string comando) + { + if (_PortaCAN?.IsOpen == true) + { + _PortaCAN.Write(comando); + //Console.WriteLine($"[CAN] Frame enviado: {comando}"); + return true; + } + return false; + } + + private void LimparMensagensAntigas() + { + var agora = DateTime.Now; + + lock (_LockMensagens) + { + List mensagens = Mensagens + .Where(x => + (!x.ComResposta && x.Enviado) || + (x.ComResposta && x.Respondido) || + (x.ComResposta && x.Enviado && !x.Respondido && x.EnviadoEm < agora.AddSeconds(-1)) || + (x.Momento < agora.AddSeconds(-30)) + ) + .ToList(); + foreach (var item in mensagens) + { + Mensagens.Remove(item); + } + } + } + + public void Fechar() + { + tmrOuvirCAN?.Dispose(); + tmrEnviarCAN?.Dispose(); + tmrProcessarCAN?.Dispose(); + + lock (_LockMensagens) + { + Mensagens.Clear(); + } + + SerialCANBuffer.Clear(); + FramesRecebidos = new ConcurrentQueue(); + _roteadores.Clear(); + _ultimoIdEnviado = null; + + if (_PortaCAN?.IsOpen == true) + { + EnviarComando("C\r"); + _PortaCAN.Close(); + } + + IsConnected = false; + } + + public void Dispose() + { + Fechar(); + } + + private byte CalcularChecksum(byte id, byte[] data) + { + int soma = id; + foreach (var b in data) + soma += b; + return (byte)(soma & 0xFF); + } + + private string GerarChaveMensagem(T_Code Dispositivo, byte Id, byte[] data) + { + byte parametro = 0x00; + + // Para dispositivos que não sejam MKS, usar DataTx[1] se existir + if (Dispositivo != T_Code.Mks && (data?.Length ?? 0) > 1) + { + parametro = data[1]; + } + + return $"{Id}_{data[0]}_{parametro}"; + } + + } +} \ No newline at end of file diff --git a/AgroBase/AgroBase/Services/SerialService.cs b/AgroBase/AgroBase/Services/SerialService.cs index 3472b5838..2d2dfae74 100644 --- a/AgroBase/AgroBase/Services/SerialService.cs +++ b/AgroBase/AgroBase/Services/SerialService.cs @@ -222,6 +222,18 @@ namespace AgroBase.Services continue; } } + + if (CanManager.UseCanSerial && !CanManager.CanService.Iniciado) + { + AtualizarConsole($"{PortaCom} - Procurando dispositivos no barramento CAN"); + bool dispositivoCan = await ProcurarDispositivosCAN(_Porta); + if (dispositivoCan) + { + _Porta.Close(); + AtualizarConsole("Barramento CAN encontrado"); + continue; + } + } } if (Variaveis.IsAgroMonitor) return; @@ -372,17 +384,17 @@ namespace AgroBase.Services } } - private static async Task ProcurarDispositivosCAN() + private static async Task ProcurarDispositivosCAN(SerialPort Porta = null) { - if (!CanService.InicializarCanal()) + if (!CanManager.CanService.Inicializar(Porta)) { - AtualizarConsole($"Adaptador {CanService._portName} não conectado!"); + AtualizarConsole($"Adaptador {CanManager.CanService._portName} não conectado!"); return false; } if (true && Variaveis.OperacaoEmAndamento.DispSen != null && !Variaveis.OperacaoEmAndamento.DispSen.Dados.Conectado) { - AtualizarConsole($"{CanService._portName} - Procurando dispositivo SEN"); + AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo SEN"); bool DispSen = await Variaveis.OperacaoEmAndamento.DispSen.Dados.VerificaDispositivoConectado(); if (DispSen) { @@ -392,7 +404,7 @@ namespace AgroBase.Services if (true && Variaveis.OperacaoEmAndamento.DispAtu != null && !Variaveis.OperacaoEmAndamento.DispAtu.Dados.ConexaoAtiva) { - AtualizarConsole($"{CanService._portName} - Procurando dispositivo ATU"); + AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo ATU"); bool DispAtu = await Variaveis.OperacaoEmAndamento.DispAtu.Dados.VerificaDispositivoConectado(); if (DispAtu) { @@ -402,7 +414,7 @@ namespace AgroBase.Services if (true && !MKS057DCanService.Referenciando && (!MKS057DCanService.Iniciado || MKS057DCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4))) { - AtualizarConsole($"{CanService._portName} - Procurando dispositivos MKS"); + AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivos MKS"); bool DispMks = await MKS057DCanService.VerificaDispositivoConectado(); if (DispMks) { @@ -412,7 +424,7 @@ namespace AgroBase.Services if (true && (!OIDCanService.Iniciado || OIDCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4))) { - AtualizarConsole($"{CanService._portName} - Procurando dispositivo OID"); + AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo OID"); bool DispOid = await OIDCanService.VerificaDispositivoConectado(); if (DispOid) { @@ -420,7 +432,7 @@ namespace AgroBase.Services } } - return CanService.Iniciado; + return CanManager.CanService.Iniciado; } private static void ProcurarDispositivosUSB()