a
This commit is contained in:
319
ScanBox/TcpReaderAccessor.cs
Normal file
319
ScanBox/TcpReaderAccessor.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ScaBox30.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Reemplazo del ReaderAccessor del SDK de Keyence
|
||||
/// Implementa conexión TCP/IP directa para SR-X300W
|
||||
/// </summary>
|
||||
public class TcpReaderAccessor : IDisposable
|
||||
{
|
||||
private TcpClient client;
|
||||
private NetworkStream stream;
|
||||
private bool isConnected = false;
|
||||
private CancellableBackgroundWorker receiverWorker;
|
||||
private Action<byte[]> onDataReceived;
|
||||
|
||||
// Propiedades compatibles con el SDK original
|
||||
public string IpAddress { get; set; }
|
||||
public int Port { get; set; } = 9004; // Puerto TCP de comandos SR-X300W
|
||||
public ErrorCode LastErrorInfo { get; private set; } = ErrorCode.None;
|
||||
public int ReceiveTimeout { get; set; } = 30000; // 5 segundos
|
||||
|
||||
public TcpReaderAccessor()
|
||||
{
|
||||
receiverWorker = new CancellableBackgroundWorker();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conecta al sensor SR-X300W y registra callback para datos recibidos
|
||||
/// Compatible con: m_reader.Connect((data) => { ... })
|
||||
/// </summary>
|
||||
public bool Connect(Action<byte[]> dataReceivedCallback)
|
||||
{
|
||||
if (string.IsNullOrEmpty(IpAddress))
|
||||
{
|
||||
LastErrorInfo = ErrorCode.InvalidParameter;
|
||||
Console.WriteLine("ERROR: IpAddress no configurada");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"[TCP] Conectando a {IpAddress}:{Port}...");
|
||||
|
||||
// Crear y conectar TcpClient
|
||||
client = new TcpClient();
|
||||
client.Connect(IpAddress, Port);
|
||||
stream = client.GetStream();
|
||||
stream.ReadTimeout = ReceiveTimeout;
|
||||
|
||||
isConnected = true;
|
||||
onDataReceived = dataReceivedCallback;
|
||||
LastErrorInfo = ErrorCode.None;
|
||||
|
||||
Console.WriteLine($"[TCP] Conectado exitosamente a {IpAddress}:{Port}");
|
||||
|
||||
// Iniciar listener asíncrono (similar al SDK)
|
||||
//StartAsyncReceiver();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"[TCP] Error de conexión: {ex.Message}");
|
||||
LastErrorInfo = ErrorCode.OpenFailed;
|
||||
isConnected = false;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[TCP] Error inesperado: {ex.Message}");
|
||||
LastErrorInfo = ErrorCode.OpenFailed;
|
||||
isConnected = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ejecuta un comando y espera respuesta COMPLETA usando async/await
|
||||
/// BASADO EN LA APP DE PRUEBA QUE FUNCIONA CORRECTAMENTE
|
||||
/// </summary>
|
||||
public string ExecCommand(string command)
|
||||
{
|
||||
if (!isConnected || stream == null)
|
||||
{
|
||||
LastErrorInfo = ErrorCode.Closed;
|
||||
Console.WriteLine($"[TCP] ERROR: No conectado al ejecutar '{command}'");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Agregar terminador de línea si no lo tiene
|
||||
string fullCommand = command.EndsWith("\r") ? command : command + "\r";
|
||||
byte[] cmdBytes = Encoding.ASCII.GetBytes(fullCommand);
|
||||
|
||||
Console.WriteLine($"[TCP] Enviando comando: '{command}' ({cmdBytes.Length} bytes)");
|
||||
Console.WriteLine($"[TCP] Bytes a enviar: [{BitConverter.ToString(cmdBytes)}]");
|
||||
|
||||
// Enviar comando (SÍNCRONO como en tu app original)
|
||||
stream.Write(cmdBytes, 0, cmdBytes.Length);
|
||||
stream.Flush();
|
||||
Console.WriteLine("[TCP] Bytes escritos ✓");
|
||||
|
||||
// **CAMBIO CRÍTICO**: Usar el patrón de la app de prueba
|
||||
Console.WriteLine($"[TCP] Leyendo respuesta (timeout {ReceiveTimeout}ms)...");
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
StringBuilder responseBuilder = new StringBuilder();
|
||||
|
||||
// Crear task de lectura asíncrona
|
||||
Task<int> readTask = stream.ReadAsync(buffer, 0, buffer.Length);
|
||||
|
||||
// Esperar con timeout (igual que la app de prueba)
|
||||
if (readTask.Wait(ReceiveTimeout))
|
||||
{
|
||||
int bytesRead = readTask.Result;
|
||||
Console.WriteLine($"[TCP] Primera lectura: {bytesRead} bytes");
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
string chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead);
|
||||
responseBuilder.Append(chunk);
|
||||
|
||||
// Si contiene \r, ya terminó
|
||||
if (chunk.Contains("\r"))
|
||||
{
|
||||
string response = responseBuilder.ToString();
|
||||
Console.WriteLine($"[TCP] Respuesta completa en primer paquete: {response.Length} bytes");
|
||||
Console.WriteLine($"[TCP] Respuesta HEX: [{BitConverter.ToString(buffer, 0, bytesRead)}]");
|
||||
|
||||
string preview = response.Length > 100
|
||||
? response.Substring(0, 50) + "..." + response.Substring(response.Length - 50)
|
||||
: response.TrimEnd('\r', '\n');
|
||||
Console.WriteLine($"[TCP] Preview: '{preview}'");
|
||||
|
||||
LastErrorInfo = ErrorCode.None;
|
||||
return response;
|
||||
}
|
||||
|
||||
// Si NO terminó, leer más paquetes
|
||||
Console.WriteLine("[TCP] Respuesta incompleta, leyendo más paquetes...");
|
||||
DateTime startTime = DateTime.Now;
|
||||
int packetsRead = 1;
|
||||
|
||||
while ((DateTime.Now - startTime).TotalMilliseconds < ReceiveTimeout)
|
||||
{
|
||||
if (stream.DataAvailable)
|
||||
{
|
||||
readTask = stream.ReadAsync(buffer, 0, buffer.Length);
|
||||
if (readTask.Wait(5000)) // 5s para paquetes adicionales
|
||||
{
|
||||
bytesRead = readTask.Result;
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
packetsRead++;
|
||||
chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead);
|
||||
responseBuilder.Append(chunk);
|
||||
Console.WriteLine($"[TCP] Paquete {packetsRead}: {bytesRead} bytes");
|
||||
|
||||
if (chunk.Contains("\r"))
|
||||
{
|
||||
break; // Terminador encontrado
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Threading.Thread.Sleep(50); // Esperar más datos
|
||||
}
|
||||
}
|
||||
|
||||
string finalResponse = responseBuilder.ToString();
|
||||
Console.WriteLine($"[TCP] Respuesta COMPLETA: {finalResponse.Length} bytes en {packetsRead} paquetes");
|
||||
|
||||
string finalPreview = finalResponse.Length > 100
|
||||
? finalResponse.Substring(0, 50) + "..." + finalResponse.Substring(finalResponse.Length - 50)
|
||||
: finalResponse.TrimEnd('\r', '\n');
|
||||
Console.WriteLine($"[TCP] Preview final: '{finalPreview}'");
|
||||
|
||||
LastErrorInfo = ErrorCode.None;
|
||||
return finalResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("[TCP] Primera lectura devolvió 0 bytes");
|
||||
LastErrorInfo = ErrorCode.None;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[TCP] TIMEOUT esperando respuesta de '{command}'");
|
||||
LastErrorInfo = ErrorCode.Timeout;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[TCP] Error en ExecCommand '{command}': {ex.Message}");
|
||||
Console.WriteLine($"[TCP] StackTrace: {ex.StackTrace}");
|
||||
LastErrorInfo = ErrorCode.SendFailed;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inicia un worker en background para recibir datos asíncronos
|
||||
/// Esto simula el comportamiento del SDK cuando hay lecturas continuas
|
||||
/// </summary>
|
||||
private void StartAsyncReceiver()
|
||||
{
|
||||
receiverWorker.DoWork += (sender, e) =>
|
||||
{
|
||||
byte[] buffer = new byte[8192];
|
||||
|
||||
while (!receiverWorker.CancellationPending && isConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (stream != null && stream.DataAvailable)
|
||||
{
|
||||
int bytesRead = stream.Read(buffer, 0, buffer.Length);
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
byte[] data = new byte[bytesRead];
|
||||
Array.Copy(buffer, data, bytesRead);
|
||||
|
||||
// Llamar al callback registrado
|
||||
onDataReceived?.Invoke(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(50); // Pequeña pausa para no saturar CPU
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[TCP] Error en receiver: {ex.Message}");
|
||||
LastErrorInfo = ErrorCode.BeginReceiveFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
receiverWorker.RunWorkerAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cierra la conexión TCP
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine("[TCP] Desconectando...");
|
||||
|
||||
isConnected = false;
|
||||
|
||||
// Detener receiver
|
||||
if (receiverWorker != null && receiverWorker.IsBusy)
|
||||
{
|
||||
receiverWorker.CancelAsync();
|
||||
Thread.Sleep(100); // Dar tiempo para que termine
|
||||
}
|
||||
|
||||
stream?.Close();
|
||||
client?.Close();
|
||||
|
||||
LastErrorInfo = ErrorCode.None;
|
||||
Console.WriteLine("[TCP] Desconectado correctamente");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[TCP] Error al desconectar: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disconnect();
|
||||
stream?.Dispose();
|
||||
client?.Dispose();
|
||||
receiverWorker?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeración de códigos de error compatible con el SDK
|
||||
/// </summary>
|
||||
public enum ErrorCode
|
||||
{
|
||||
None = 0,
|
||||
AlreadyOpen = 1,
|
||||
Closed = 2,
|
||||
OpenFailed = 3,
|
||||
Timeout = 4,
|
||||
SendFailed = 5,
|
||||
BeginReceiveFailed = 6,
|
||||
InvalidParameter = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BackgroundWorker con soporte para cancelación
|
||||
/// </summary>
|
||||
public class CancellableBackgroundWorker : System.ComponentModel.BackgroundWorker
|
||||
{
|
||||
public CancellableBackgroundWorker()
|
||||
{
|
||||
WorkerSupportsCancellation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user