Commit fedb0e36 authored by Jose Hugo Torres's avatar Jose Hugo Torres
Browse files

Ampliacion logs

Mas logs para conexión al POSBC
parent b7dc25ca
...@@ -89,6 +89,11 @@ Carpetas de código asociado con GK Smart POS: ...@@ -89,6 +89,11 @@ Carpetas de código asociado con GK Smart POS:
- `ComandosGkPruebas` y `ComandosGk` contiene los comandos para ambos tipos de ambientes de Gk. - `ComandosGkPruebas` y `ComandosGk` contiene los comandos para ambos tipos de ambientes de Gk.
- `POSGk` contiene código especializado para GK Smart POS. - `POSGk` contiene código especializado para GK Smart POS.
## Integración con POSBC
La clase ClienteServidorPOSBC maneja la conexión con el servidor POSBC.
## Pendientes ## Pendientes
1. Pasar usuario/clave al archivo de config. 1. Pasar usuario/clave al archivo de config.
......
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using Serilog;
namespace GatewaySCO namespace GatewaySCO
{ {
...@@ -9,6 +11,131 @@ namespace GatewaySCO ...@@ -9,6 +11,131 @@ namespace GatewaySCO
/// </summary> /// </summary>
public class Util public class Util
{ {
/// <summary>
/// Concatena dos arreglos de bytes y retorna arreglo con el resultado.
/// </summary>
public static byte[] ConcatenaArreglosBytes(byte[] array1, byte[] array2)
{
byte[] result = new byte[array1.Length + array2.Length];
Buffer.BlockCopy(array1, 0, result, 0, array1.Length);
Buffer.BlockCopy(array2, 0, result, array1.Length, array2.Length);
return result;
}
/// <summary>
/// Convierte el string del parámetro en un arreglo de bytes, anteponiendo
/// al inicio del arreglo 4 bytes con la longitud del resto del arreglo.
/// </summary>
public static byte[] ConvertirEnBytes(string mensaje)
{
// Codifica longitud del mensaje en un entero sin signo en los 4 primeros bytes.
uint longitud = Convert.ToUInt32(mensaje.Length);
byte[] bytesConLongMensaje = BitConverter.GetBytes(longitud);
// Bytes mas significativos deben ir primero, usa 'big-endian'.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesConLongMensaje);
// Codifica en bytes texto del mensaje.
byte[] bytesConMensaje = Encoding.UTF8.GetBytes(mensaje);
// Copia los 2 arreglos de bytes en un arreglo unificado.
byte[] bytes = new byte[bytesConLongMensaje.Length + bytesConMensaje.Length];
Buffer.BlockCopy(bytesConLongMensaje, 0, bytes, 0, bytesConLongMensaje.Length);
Buffer.BlockCopy(bytesConMensaje, 0, bytes, bytesConLongMensaje.Length, bytesConMensaje.Length);
return bytes;
}
/// <summary>
/// Longitud del mensaje, retorna entero representado en 4 bytes, sin signo.
/// </summary>
public static uint LongitudMensaje(byte[] bytesMensaje)
{
// Extrae longitud. Primeros 4 bytes del arreglo.
byte[] bytesLongitudMensaje = new byte[4];
Buffer.BlockCopy(bytesMensaje, 0, bytesLongitudMensaje, 0, 4);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesLongitudMensaje);
return BitConverter.ToUInt32(bytesLongitudMensaje, 0);
}
/// <summary>
/// Extrae componente de encabezado del mensaje.
/// Retorna cadena vacía si no encuentra un mensaje adecuadamente formado.
/// </summary>
public static string ExtraeEncabezado(string mensaje)
{
// Extrae encabezado. String con patron soeps~{texto>}
int inicioXML = mensaje.IndexOf('<');
if (inicioXML == -1 || inicioXML == 0)
{
return "";
}
string parteEncabezado = mensaje[..inicioXML];
return parteEncabezado;
}
/// <summary>
/// Extrae componente de datos (cuerpo) del mensaje.
/// Asume que la cadena solo contiene un mensaje.
/// Retorna cadena vacía si no encuentra un mensaje adecuadamente formado.
/// </summary>
public static string ExtraeCuerpo(string mensaje)
{
// Identifica el inicio del documento XML.
int inicioXML = mensaje.IndexOf('<');
if (inicioXML == -1 || inicioXML == 0)
{
return "";
}
string parteCuerpo = mensaje[inicioXML..];
return parteCuerpo;
}
/// <summary>
/// Interpreta el arreglo de bytes que codifica uno o mas mensajes del parámetro
/// asumiendo el formato CHEC/POSBC, es decir, por mensaje:
/// 4 bytes con longitud del mensaje
/// encabezado
/// cuerpo del mensaje.
/// Retorna un string con esta representación, separando cada mensaje y cada
/// componente con un salto de línea.
/// Usado para documentar los mensajes.
/// </summary>
public static string DetalleMensajes(byte[] buffer)
{
StringBuilder contenido = new();
contenido.Append($"Mensje en buffer de {buffer.Length} bytes.");
bool continua = true;
int puntero = 4; // Se salta los primeros 4 bytes con la longitud.
while (continua)
{
// Extrae longitud.
int longitud = Convert.ToInt32(LongitudMensaje(buffer));
contenido.Append($"\nlongitud: {longitud}");
if (longitud + puntero > buffer.Length)
{
contenido.Append($"\nlongitud {longitud} codificada en mensaje supera longitud restante en buffer.");
continua = false;
continue;
}
string mensaje = Encoding.UTF8.GetString(buffer, puntero, longitud);
// Notar que se asume que puede haber mas de un mensaje incluido en el buffer.
// Extrae encabezado.
string encabezado = ExtraeEncabezado(mensaje);
contenido.Append($"\nencabezado:\n{encabezado}");
// Extrae mensaje.
string cuerpo = ExtraeCuerpo(mensaje);
contenido.Append($"\ncuerpo:\n{cuerpo}");
// Hay mas datos?.
if (longitud + puntero < buffer.Length)
{
// El buffer tiene mas datos de los que indica la longitud en su encabezado.
puntero += longitud + 4;
} else {
continua = false;
}
}
return contenido.ToString();
}
/// <summary> /// <summary>
/// Utilitario para convertir en string contenido XML de objeto XmlNode. /// Utilitario para convertir en string contenido XML de objeto XmlNode.
/// </summary> /// </summary>
......
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using EvaPosSCOSrv; using GatewaySCO;
using Serilog; using Serilog;
namespace gatewaySCO.POSBC; namespace gatewaySCO.POSBC;
...@@ -10,39 +10,41 @@ namespace gatewaySCO.POSBC; ...@@ -10,39 +10,41 @@ namespace gatewaySCO.POSBC;
public class ClienteServidorPOSBC(string ip, int pto) public class ClienteServidorPOSBC(string ip, int pto)
{ {
ILogger log = Log.ForContext<ClienteServidorPOSBC>(); ILogger log = Log.ForContext<ClienteServidorPOSBC>();
int _pto = pto; private int _pto = pto;
string _ip = ip; private string _ip = ip;
Socket _socket = null; private Socket _socket = null;
public bool ConexionActiva { get; private set; } = false; public bool ConexionActiva { get; private set; } = false;
int _contadorMensajesEnviados = 0; private int _contadorMensajesEnviados = 0;
int _contadorMensajesRecibidos = 0; private int _contadorMensajesRecibidos = 0;
public void AbreConexion() public void AbreConexion()
{ {
_socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Conectarse al servidor
try try
{ {
// Conectarse al servidor
_socket.Connect(new IPEndPoint(IPAddress.Parse(_ip), _pto)); _socket.Connect(new IPEndPoint(IPAddress.Parse(_ip), _pto));
ConexionActiva = true; ConexionActiva = true;
Log.Information("Conectado a POSBC {ip}:{pto} - {conectado}", _ip, _pto, _socket.Connected); Log.Information("Conectado a POSBC {ip}:{pto} - {conectado}", _ip, _pto, _socket.Connected);
} }
catch (SocketException ex) catch (SocketException ex)
{ {
ConexionActiva = false;
Log.Error("Excepción socket conexión a POSBC {ip}:{pto} - código error socket {codigo} - {e}", _ip, ex.SocketErrorCode, _pto, ex); Log.Error("Excepción socket conexión a POSBC {ip}:{pto} - código error socket {codigo} - {e}", _ip, ex.SocketErrorCode, _pto, ex);
} }
catch (Exception e) catch (Exception e)
{ {
ConexionActiva = false;
Log.Error("Excepción en conexión a POSBC {ip}:{pto} - {e}", _ip, _pto, e); Log.Error("Excepción en conexión a POSBC {ip}:{pto} - {e}", _ip, _pto, e);
} }
} }
public void CierraConexion() public void CierraConexion()
{ {
// Cerrar el socket
ConexionActiva = false;
try try
{ {
// Cerrar el socket
ConexionActiva = false;
_socket.Shutdown(SocketShutdown.Both); _socket.Shutdown(SocketShutdown.Both);
_socket.Close(); _socket.Close();
} }
...@@ -53,25 +55,6 @@ public class ClienteServidorPOSBC(string ip, int pto) ...@@ -53,25 +55,6 @@ public class ClienteServidorPOSBC(string ip, int pto)
Log.Information("Conexión cerrada a POSBC {ip}:{pto} - {conectado}", _ip, _pto, _socket.Connected); Log.Information("Conexión cerrada a POSBC {ip}:{pto} - {conectado}", _ip, _pto, _socket.Connected);
} }
// Convierte el string del parámetro a un arreglo de bytes,
// anteponiendo 4 bytes con la longitud del mensaje.
public static byte[] ConvertirEnBytes(string mensaje)
{
// Codifica longitud del mensaje en un entero sin signo en los 4 primeros bytes.
var longitud = Convert.ToUInt32(mensaje.Length);
byte[] bytesConLongMensaje = BitConverter.GetBytes(longitud);
// Bytes mas significativos deben ir primero, usa 'big-endian'.
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesConLongMensaje);
// Codifica en bytes texto del mensaje.
byte[] bytesConMensaje = Encoding.UTF8.GetBytes(mensaje);
// Copia los 2 arreglos de bytes en un arreglo unificado.
byte[] bytes = new byte[bytesConLongMensaje.Length + bytesConMensaje.Length];
Buffer.BlockCopy(bytesConLongMensaje, 0, bytes, 0, bytesConLongMensaje.Length);
Buffer.BlockCopy(bytesConMensaje, 0, bytes, bytesConLongMensaje.Length, bytesConMensaje.Length);
return bytes;
}
// Envia mensaje, espera respuesta y la retorna como un arreglo de bytes. // Envia mensaje, espera respuesta y la retorna como un arreglo de bytes.
// El mensaje es un string, convertido a arreglo de bytes, y le antepone // El mensaje es un string, convertido a arreglo de bytes, y le antepone
// 4 bytes con la longitud del mensaje. // 4 bytes con la longitud del mensaje.
...@@ -81,7 +64,7 @@ public class ClienteServidorPOSBC(string ip, int pto) ...@@ -81,7 +64,7 @@ public class ClienteServidorPOSBC(string ip, int pto)
// Remite mensaje. // Remite mensaje.
_socket.Send(bytesMensaje); _socket.Send(bytesMensaje);
_contadorMensajesEnviados++; _contadorMensajesEnviados++;
Log.Information("Mensaje #{contador} para POSBIC {ip} - {msj}", _contadorMensajesEnviados, _ip, bytesMensaje.ToString); Log.Information("Mensaje #{contador} para POSBIC {ip}", _contadorMensajesEnviados, _ip);
Log.Information("Esperando respuesta.."); Log.Information("Esperando respuesta..");
// Leer la respuesta del servidor // Leer la respuesta del servidor
...@@ -107,7 +90,9 @@ public class ClienteServidorPOSBC(string ip, int pto) ...@@ -107,7 +90,9 @@ public class ClienteServidorPOSBC(string ip, int pto)
} }
_contadorMensajesRecibidos++; _contadorMensajesRecibidos++;
Log.Debug("Respuesta #{contador} de POSBC {ip} - {msj}", _contadorMensajesRecibidos, _ip, bufferEntrada.ToString); Log.Information("Respuesta #{contador} de POSBC {ip}", _contadorMensajesRecibidos, _ip);
Log.Debug("Mensaje - {msj}", _contadorMensajesRecibidos, _ip, Encoding.UTF8.GetString(bufferEntrada));
Log.Debug(Util.DetalleMensajes(bufferEntrada));
return bufferEntrada; return bufferEntrada;
} }
} }
\ No newline at end of file
...@@ -11,12 +11,12 @@ namespace GatewaySCO ...@@ -11,12 +11,12 @@ namespace GatewaySCO
/// EvaPos-API : servidor api, sockets y rest. /// EvaPos-API : servidor api, sockets y rest.
/// Usa Serilog para bitácora. /// Usa Serilog para bitácora.
/// </summary> /// </summary>
public class Program public class ProgramGatewaySCO
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
Console.WriteLine("*** Gateway SCO - Servidor: procesa peticiones de Toshiba CHEC ***"); Console.WriteLine("*** Gateway SCO - Servidor: procesa peticiones de Toshiba CHEC ***");
Program program = new(); ProgramGatewaySCO program = new();
LeeActivaConfiguracion(args); LeeActivaConfiguracion(args);
} }
...@@ -45,10 +45,46 @@ namespace GatewaySCO ...@@ -45,10 +45,46 @@ namespace GatewaySCO
Log.Information(config.ToString()); Log.Information(config.ToString());
Entorno<Config>.Instancia.set(config); Entorno<Config>.Instancia.set(config);
// Lee del archivo de configuración sección POS Gk si aplica. // // Lee del archivo de configuración sección POS Gk si aplica.
// TODO - valor de parámetro POS de archivo de configuración en ENUM? // // TODO - valor de parámetro POS de archivo de configuración en ENUM?
if (config.POS == "gk") // if (config.POS == "gk")
// {
// ConfigGk configGk = configBuilder.GetRequiredSection("DataGK").Get<ConfigGk>()
// ?? throw new ApplicationException("Archivo de configuración sin sección 'DataGK'.");
// Log.Debug(configGk.ToString());
// // Inicializa el entorno Gk.
// Entorno<EntornoGK>.Instancia.set(new EntornoGK());
// Entorno<EntornoGK>.Instancia.get().Language = config.Language;
// Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk;
// Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}");
// }
// // Configuración para pruebas de emulación del POSBC.
// if (config.POS == "ECO_POSBC")
// {
// ConfigPOSBC configPOSBC = configBuilder.GetRequiredSection("POSBC").Get<ConfigPOSBC>()
// ?? throw new ApplicationException("Archivo de configuración sin sección 'POSBC'.");
// Log.Information(configPOSBC.ToString());
// Entorno<EntornoPOSBC>.Instancia.set(new EntornoPOSBC());
// Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC = configPOSBC.IpPOSBC;
// Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC = configPOSBC.PortPOSBC;
// // Se activa lógica de cliente POSBC
// // La conexión con el POSBC se almacena en el entorno para uso
// // posterior en la emisión y recepción de mensajes.
// Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC = new ClienteServidorPOSBC(configPOSBC.IpPOSBC, configPOSBC.PortPOSBC);
// Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.AbreConexion();
// if (Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.ConexionActiva == false)
// {
// throw new ApplicationException("Error en conexión al POSBC.");
// }
// }
// Activa servidor Gateway.
// En este punto, el programa se bloquea esperando conexión y mensajes desde CHEC.
switch (config.POS)
{ {
case "gk":
{
// Procesa sección DataGK del archivo de configuración.
ConfigGk configGk = configBuilder.GetRequiredSection("DataGK").Get<ConfigGk>() ConfigGk configGk = configBuilder.GetRequiredSection("DataGK").Get<ConfigGk>()
?? throw new ApplicationException("Archivo de configuración sin sección 'DataGK'."); ?? throw new ApplicationException("Archivo de configuración sin sección 'DataGK'.");
Log.Debug(configGk.ToString()); Log.Debug(configGk.ToString());
...@@ -57,28 +93,33 @@ namespace GatewaySCO ...@@ -57,28 +93,33 @@ namespace GatewaySCO
Entorno<EntornoGK>.Instancia.get().Language = config.Language; Entorno<EntornoGK>.Instancia.get().Language = config.Language;
Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk; Entorno<EntornoGK>.Instancia.get().ConfigGk = configGk;
Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}"); Log.Information($"GK {Entorno<EntornoGK>.Instancia.get().UrlBase}");
break;
} }
// Configuración para pruebas de emulación del POSBC. case "ECO_POSBC":
if (config.POS == "ECO_POSBC")
{ {
// Procesa sección POSBC del archivo de configuración.
ConfigPOSBC configPOSBC = configBuilder.GetRequiredSection("POSBC").Get<ConfigPOSBC>() ConfigPOSBC configPOSBC = configBuilder.GetRequiredSection("POSBC").Get<ConfigPOSBC>()
?? throw new ApplicationException("Archivo de configuración sin sección 'POSBC'."); ?? throw new ApplicationException("Archivo de configuración sin sección 'POSBC'.");
Log.Information(configPOSBC.ToString()); Log.Information(configPOSBC.ToString());
Entorno<EntornoPOSBC>.Instancia.set(new EntornoPOSBC()); Entorno<EntornoPOSBC>.Instancia.set(new EntornoPOSBC());
Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC = configPOSBC.IpPOSBC; Entorno<EntornoPOSBC>.Instancia.get().IpPOSBC = configPOSBC.IpPOSBC;
Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC = configPOSBC.PortPOSBC; Entorno<EntornoPOSBC>.Instancia.get().PortPOSBC = configPOSBC.PortPOSBC;
// Se activa lógica de cliente POSBC // Activa cliente de conexión a POSBC. La conexión con el POSBC
// La conexión con el POSBC se almacena en el entorno para uso // se almacena en el entorno para uso posterior en la emisión y recepción de mensajes.
// posterior en la emisión y recepción de mensajes.
Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC = new ClienteServidorPOSBC(configPOSBC.IpPOSBC, configPOSBC.PortPOSBC); Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC = new ClienteServidorPOSBC(configPOSBC.IpPOSBC, configPOSBC.PortPOSBC);
Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.AbreConexion(); Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.AbreConexion();
if (Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.ConexionActiva == false) if (Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.ConexionActiva == false)
{ {
throw new ApplicationException("Error en conexión al POSBC."); throw new ApplicationException("Error en conexión al POSBC.");
} }
break;
} }
// Activa servidor Gateway. default:
// En este punto, el programa se bloquea esperando conexión y mensajes desde CHEC. {
throw new ApplicationException($"Configuración POS desconocida: {config.POS}");
}
}
ActivaServidor(config.IpGateway, config.PortGateway, config.POS); ActivaServidor(config.IpGateway, config.PortGateway, config.POS);
} }
......
...@@ -244,30 +244,6 @@ namespace EvaPosSCOSrv ...@@ -244,30 +244,6 @@ namespace EvaPosSCOSrv
} }
} }
/// <summary>
/// Longitud del mensaje, retorna entero representado en 4 bytes, sin signo.
/// </summary>
public static uint LongitudMensaje(byte[] bytesMensaje)
{
// Extrae longitud. Primeros 4 bytes del arreglo.
byte[] bytesLongitudMensaje = new byte[4];
Buffer.BlockCopy(bytesMensaje, 0, bytesLongitudMensaje, 0, 4);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesLongitudMensaje);
return BitConverter.ToUInt32(bytesLongitudMensaje, 0);
}
/// <summary>
/// Concatena dos arreglos de bytes en un tercero que retorna como respuesta.
/// </summary>
public static byte[] ConcatenateArrays(byte[] array1, byte[] array2)
{
byte[] result = new byte[array1.Length + array2.Length];
Buffer.BlockCopy(array1, 0, result, 0, array1.Length);
Buffer.BlockCopy(array2, 0, result, array1.Length, array2.Length);
return result;
}
/// <summary> /// <summary>
/// Procesa socket entrada de conexión. /// Procesa socket entrada de conexión.
/// </summary> /// </summary>
...@@ -300,7 +276,7 @@ namespace EvaPosSCOSrv ...@@ -300,7 +276,7 @@ namespace EvaPosSCOSrv
Log.Debug("Arriba un mensaje."); Log.Debug("Arriba un mensaje.");
// Lee porción de datos del mensaje, hasta la longitud indicada en los 4 primeros bytes. // Lee porción de datos del mensaje, hasta la longitud indicada en los 4 primeros bytes.
uint longitudMensaje = LongitudMensaje(bufferLongitud); uint longitudMensaje = Util.LongitudMensaje(bufferLongitud);
if (longitudMensaje > LongMaxMensaje) throw new Exception($"Mensaje {longitudMensaje} bytes supera máximo permitido de {LongMaxMensaje} bytes."); if (longitudMensaje > LongMaxMensaje) throw new Exception($"Mensaje {longitudMensaje} bytes supera máximo permitido de {LongMaxMensaje} bytes.");
var bufferEntrada = new byte[longitudMensaje]; var bufferEntrada = new byte[longitudMensaje];
bytesLeidos = 0; bytesLeidos = 0;
...@@ -322,12 +298,11 @@ namespace EvaPosSCOSrv ...@@ -322,12 +298,11 @@ namespace EvaPosSCOSrv
{ {
// Modo "past-throught": lo que entra se remite sin cambios al POSBC y // Modo "past-throught": lo que entra se remite sin cambios al POSBC y
// su respeusta se remite sin cambios a CHEC. // su respeusta se remite sin cambios a CHEC.
byte[] mensajeEntrada = ConcatenateArrays(bufferLongitud, bufferEntrada); byte[] mensajeEntrada = Util.ConcatenaArreglosBytes(bufferLongitud, bufferEntrada);
// Enviar mensaje de entrada a POSBC y retornar respuestas. // Enviar mensaje de entrada a POSBC y retornar respuestas.
byte[] bufferSalida = Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.EnviaRecibe(mensajeEntrada); byte[] bufferSalida = Entorno<EntornoPOSBC>.Instancia.get().ClientePOSBC.EnviaRecibe(mensajeEntrada);
// Remitir respuestas sin cambio a CHEC. // Remitir respuestas sin cambio a CHEC.
socket.Send(bufferSalida, 0, bufferSalida.Length, SocketFlags.None); socket.Send(bufferSalida, 0, bufferSalida.Length, SocketFlags.None);
Log.Information("Respuesta remitida: {bytes}", bufferSalida);
// Procesando entrada: se obtiene mensaje, con el cual se // Procesando entrada: se obtiene mensaje, con el cual se
// identifica comando que lo procesa. // identifica comando que lo procesa.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment