diff --git a/ESCPOS_NET.ConsoleTest/Program.cs b/ESCPOS_NET.ConsoleTest/Program.cs
index 4e6c60c..0ead6f8 100644
--- a/ESCPOS_NET.ConsoleTest/Program.cs
+++ b/ESCPOS_NET.ConsoleTest/Program.cs
@@ -12,10 +12,13 @@ internal class Program
{
private static BasePrinter printer;
private static ICommandEmitter e;
+ ///
+ /// Indicate whether to test with long-lived printer object or create and dispose every time.
+ ///
+ private const bool SINGLETON_PRINTER_OBJECT = false;
static void Main(string[] args)
{
-
Console.WriteLine("Welcome to the ESCPOS_NET Test Application!");
Console.Write("Would you like to see all debug messages? (y/n): ");
var response = Console.ReadLine().Trim().ToLowerInvariant();
@@ -34,8 +37,9 @@ static void Main(string[] args)
Console.WriteLine("3 ) Test Samba-Shared Printer");
Console.Write("Choice: ");
string comPort = "";
- string ip;
- string networkPort;
+ string ip = "";
+ string networkPort = "";
+ Action createPrinter = null;
string smbPath;
response = Console.ReadLine();
var valid = new List { "1", "2", "3" };
@@ -64,7 +68,10 @@ static void Main(string[] args)
{
baudRate = 115200;
}
- printer = new SerialPrinter(portName: comPort, baudRate: baudRate);
+ if (SINGLETON_PRINTER_OBJECT)
+ printer = new SerialPrinter(portName: comPort, baudRate: baudRate);
+ else
+ createPrinter = () => { printer = new SerialPrinter(portName: comPort, baudRate: baudRate); };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
@@ -74,7 +81,10 @@ static void Main(string[] args)
{
comPort = "/dev/usb/lp0";
}
- printer = new FilePrinter(filePath: comPort, false);
+ if (SINGLETON_PRINTER_OBJECT)
+ printer = new FilePrinter(filePath: comPort, false);
+ else
+ createPrinter = () => { printer = new FilePrinter(filePath: comPort, false); };
}
}
else if (choice == 2)
@@ -83,7 +93,7 @@ static void Main(string[] args)
ip = Console.ReadLine();
if (string.IsNullOrWhiteSpace(ip))
{
- ip = "192.168.254.202";
+ ip = "127.0.0.1"; // default to local for using TCPPrintServerTest
}
Console.Write("TCP Port (enter for default 9100): ");
networkPort = Console.ReadLine();
@@ -91,7 +101,10 @@ static void Main(string[] args)
{
networkPort = "9100";
}
- printer = new NetworkPrinter(settings: new NetworkPrinterSettings() { ConnectionString = $"{ip}:{networkPort}" });
+ if (SINGLETON_PRINTER_OBJECT)
+ printer = new NetworkPrinter(settings: new NetworkPrinterSettings() { ConnectionString = $"{ip}:{networkPort}" });
+ else
+ createPrinter = () => { printer = new NetworkPrinter(settings: new NetworkPrinterSettings() { ConnectionString = $"{ip}:{networkPort}" }); };
}
else if (choice == 3)
{
@@ -147,6 +160,11 @@ static void Main(string[] args)
continue;
}
+ if (!SINGLETON_PRINTER_OBJECT)
+ {
+ createPrinter();
+ }
+
var enumChoice = (Option)choice;
if (enumChoice == Option.Exit)
{
@@ -157,50 +175,50 @@ static void Main(string[] args)
if (monitor)
{
- printer.Write(e.Initialize());
- printer.Write(e.Enable());
- printer.Write(e.EnableAutomaticStatusBack());
+ printer.WriteTest(e.Initialize());
+ printer.WriteTest(e.Enable());
+ printer.WriteTest(e.EnableAutomaticStatusBack());
}
Setup(monitor);
- printer?.Write(e.PrintLine($"== [ Start {testCases[enumChoice]} ] =="));
+ printer?.WriteTest(e.PrintLine($"== [ Start {testCases[enumChoice]} ] =="));
switch (enumChoice)
{
case Option.SingleLinePrinting:
- printer.Write(Tests.SingleLinePrinting(e));
+ printer.WriteTest(Tests.SingleLinePrinting(e));
break;
case Option.MultiLinePrinting:
- printer.Write(Tests.MultiLinePrinting(e));
+ printer.WriteTest(Tests.MultiLinePrinting(e));
break;
case Option.LineSpacing:
- printer.Write(Tests.LineSpacing(e));
+ printer.WriteTest(Tests.LineSpacing(e));
break;
case Option.BarcodeStyles:
- printer.Write(Tests.BarcodeStyles(e));
+ printer.WriteTest(Tests.BarcodeStyles(e));
break;
case Option.BarcodeTypes:
- printer.Write(Tests.BarcodeTypes(e));
+ printer.WriteTest(Tests.BarcodeTypes(e));
break;
case Option.TwoDimensionCodes:
- printer.Write(Tests.TwoDimensionCodes(e));
+ printer.WriteTest(Tests.TwoDimensionCodes(e));
break;
case Option.TextStyles:
- printer.Write(Tests.TextStyles(e));
+ printer.WriteTest(Tests.TextStyles(e));
break;
case Option.FullReceipt:
- printer.Write(Tests.Receipt(e));
+ printer.WriteTest(Tests.Receipt(e));
break;
case Option.Images:
- printer.Write(Tests.Images(e, false));
+ printer.WriteTest(Tests.Images(e, false));
break;
case Option.LegacyImages:
- printer.Write(Tests.Images(e, true));
+ printer.WriteTest(Tests.Images(e, true));
break;
case Option.LargeByteArrays:
try
{
- printer.Write(Tests.TestLargeByteArrays(e));
+ printer.WriteTest(Tests.TestLargeByteArrays(e));
}
catch (Exception e)
{
@@ -208,10 +226,10 @@ static void Main(string[] args)
}
break;
case Option.CashDrawerPin2:
- printer.Write(Tests.CashDrawerOpenPin2(e));
+ printer.WriteTest(Tests.CashDrawerOpenPin2(e));
break;
case Option.CashDrawerPin5:
- printer.Write(Tests.CashDrawerOpenPin5(e));
+ printer.WriteTest(Tests.CashDrawerOpenPin5(e));
break;
default:
Console.WriteLine("Invalid entry.");
@@ -219,9 +237,10 @@ static void Main(string[] args)
}
Setup(monitor);
- printer?.Write(e.PrintLine($"== [ End {testCases[enumChoice]} ] =="));
- printer?.Write(e.PartialCutAfterFeed(5));
-
+ printer?.WriteTest(e.PrintLine($"== [ End {testCases[enumChoice]} ] =="));
+ printer?.WriteTest(e.PartialCutAfterFeed(5));
+ if (!SINGLETON_PRINTER_OBJECT)
+ printer?.Dispose();
// TODO: also make an automatic runner that runs all tests (command line).
}
}
@@ -273,4 +292,19 @@ private static void Setup(bool enableStatusBackMonitoring)
}
}
}
+
+ internal static class TestExtensions
+ {
+ ///
+ /// Wrapper exception function for ease of switching between Write and WriteAsync function
+ ///
+ ///
+ ///
+ internal static void WriteTest(this BasePrinter printer, params byte[][] arrays)
+ {
+ // Switch to use this if need to test with obsolated Write function
+ //printer.Write(arrays);
+ printer.WriteAsync(arrays).Wait();
+ }
+ }
}
diff --git a/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs b/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs
index aa7b6a4..897fcbc 100644
--- a/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs
+++ b/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs
@@ -35,7 +35,7 @@ public static byte[] TestLargeByteArrays(ICommandEmitter e)
cube
);
MemoryPrinter mp = new MemoryPrinter();
- mp.Write(expectedResult);
+ mp.WriteTest(expectedResult);
var response = mp.GetAllData();
bool hasErrors = false;
if (expectedResult.Length != response.Length)
@@ -68,7 +68,7 @@ public static byte[] TestLargeByteArrays(ICommandEmitter e)
var filename = $"{r.NextDouble().ToString()}.tmp";
using (FilePrinter fp = new FilePrinter(filename, true))
{
- fp.Write(expectedResult);
+ fp.WriteTest(expectedResult);
}
response = File.ReadAllBytes(filename);
hasErrors = false;
diff --git a/ESCPOS_NET.TCPPrintServerTest/Program.cs b/ESCPOS_NET.TCPPrintServerTest/Program.cs
index 053ccce..fb75219 100644
--- a/ESCPOS_NET.TCPPrintServerTest/Program.cs
+++ b/ESCPOS_NET.TCPPrintServerTest/Program.cs
@@ -6,6 +6,7 @@
using System.Net.Sockets;
using System.IO;
using System.Text;
+using System.Threading;
namespace TcpEchoServer
{
@@ -18,23 +19,40 @@ public static void Main()
int port = 9100;
TcpListener listener = new TcpListener(IPAddress.Loopback, port);
listener.Start();
+ TcpClient client;
- TcpClient client = listener.AcceptTcpClient();
- NetworkStream stream = client.GetStream();
+ while (true)
+ {
+ // Accept multiple connections with a dedicated thread for each connection
+ client = listener.AcceptTcpClient();
+ ThreadPool.QueueUserWorkItem(TcpClientConnectionHandler, client);
+ }
+ }
+
+ private static void TcpClientConnectionHandler(object obj)
+ {
+ var tcp = (TcpClient)obj;
+ NetworkStream stream = tcp.GetStream();
StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true };
StreamReader reader = new StreamReader(stream, Encoding.ASCII);
-
- while (true)
+ try
{
- string inputLine = "";
- while (inputLine != null)
+ while (true)
{
- inputLine = reader.ReadLine();
- writer.Write("E");
- Console.WriteLine("Echoing string: " + inputLine);
+ string inputLine = "";
+ while (inputLine != null)
+ {
+ inputLine = reader.ReadLine();
+ writer.Write("E");
+ Console.WriteLine("Echoing string: " + inputLine);
+ }
+ Console.WriteLine("Server saw disconnect from client.");
}
- Console.WriteLine("Server saw disconnect from client.");
}
+ catch(IOException)
+ {
+ // connection is closed
+ }
}
}
}
diff --git a/ESCPOS_NET/ESCPOS_NET.csproj b/ESCPOS_NET/ESCPOS_NET.csproj
index 1ed71d2..e442415 100644
--- a/ESCPOS_NET/ESCPOS_NET.csproj
+++ b/ESCPOS_NET/ESCPOS_NET.csproj
@@ -32,10 +32,10 @@
-
+
-
+
diff --git a/ESCPOS_NET/Printers/BasePrinter.cs b/ESCPOS_NET/Printers/BasePrinter.cs
index 9b36e34..fca6215 100644
--- a/ESCPOS_NET/Printers/BasePrinter.cs
+++ b/ESCPOS_NET/Printers/BasePrinter.cs
@@ -1,6 +1,7 @@
using ESCPOS_NET.Utilities;
using Microsoft.Extensions.Logging;
using System;
+using System.Collections;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
@@ -18,7 +19,6 @@ public abstract partial class BasePrinter : IPrinter, IDisposable
//private volatile bool _isMonitoring;
private CancellationTokenSource _readCancellationTokenSource;
- private CancellationTokenSource _writeCancellationTokenSource;
private readonly int _maxBytesPerWrite = 15000; // max byte chunks to write at once.
@@ -29,19 +29,23 @@ public abstract partial class BasePrinter : IPrinter, IDisposable
public event EventHandler Connected;
protected BinaryWriter Writer { get; set; }
-
protected BinaryReader Reader { get; set; }
protected ConcurrentQueue ReadBuffer { get; set; } = new ConcurrentQueue();
-
- protected ConcurrentQueue WriteBuffer { get; set; } = new ConcurrentQueue();
+ private readonly BlockingCollection<(byte[] bytes, TaskCompletionSource taskSource)> _writeBuffer =
+ new BlockingCollection<(byte[] bytes, TaskCompletionSource taskSource)>();
protected int BytesWrittenSinceLastFlush { get; set; } = 0;
- protected volatile bool IsConnected = true;
+ protected virtual bool IsConnected { get; } = true;
public string PrinterName { get; protected set; }
+ ///
+ /// Timeout in millisecond to wait for the connection to be connected when call WriteAsync function with await. Default is 5000 milliseconds.
+ ///
+ public int WriteTimeout { get; set; } = 5000;
+
protected BasePrinter()
{
PrinterName = Guid.NewGuid().ToString();
@@ -59,10 +63,9 @@ protected BasePrinter(string printerName)
private void Init()
{
_readCancellationTokenSource = new CancellationTokenSource();
- _writeCancellationTokenSource = new CancellationTokenSource();
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Initializing Task Threads...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
//Task.Factory.StartNew(MonitorPrinterStatusLongRunningTask, _connectivityCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
- Task.Factory.StartNew(WriteLongRunningTask, _writeCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
+ Task.Factory.StartNew(WriteLongRunningTask, TaskCreationOptions.LongRunning).ConfigureAwait(false);
Task.Factory.StartNew(ReadLongRunningTask, _readCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
// TODO: read and status monitoring probably won't work for fileprinter, should let printer types disable this feature.
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Task Threads started", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
@@ -83,41 +86,60 @@ protected virtual void Reconnect()
// Implemented in the network printer
}
protected virtual async void WriteLongRunningTask()
- {
- while (true)
+ {
+ // Loop when there is a new item in the _writeBuffer, break when _writeBuffer.CompleteAdding() is called (in the dispose)
+ foreach (var (nextBytes, taskSource) in _writeBuffer.GetConsumingEnumerable())
{
- if (_writeCancellationTokenSource != null && _writeCancellationTokenSource.IsCancellationRequested)
+ var writeSuccess = false;
+ var isAwaitableWrite = taskSource != null;
+ do
{
- Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Write Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
- break;
- }
-
- await Task.Delay(100);
- if (!IsConnected)
- {
- continue;
- }
+ await Task.WhenAny(
+ Task.Delay(WriteTimeout),
+ Task.Run(async () =>
+ {
+ while (!IsConnected) // Await for the connection to the printer get restored
+ {
+ await Task.Delay(100);
+ }
+ })
+ );
- try
- {
- var didDequeue = WriteBuffer.TryDequeue(out var nextBytes);
- if (didDequeue && nextBytes?.Length > 0)
+ if (!IsConnected)
{
- WriteToBinaryWriter(nextBytes);
+ taskSource?.SetException(new IOException($"Unrecoverable connectivity error writing to printer."));
+ continue;
+ }
+ try
+ {
+ if (nextBytes?.Length > 0)
+ {
+ WriteToBinaryWriter(nextBytes);
+ taskSource?.SetResult(true);
+ }
+ else
+ {
+ taskSource?.SetResult(false);
+ }
+ writeSuccess = true;
+ }
+ catch (IOException ex)
+ {
+ // Thrown if the printer times out the socket connection
+ // default is 90 seconds
+ taskSource?.TrySetException(ex);
+ //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing IOException... sometimes happens with network printers. Should get reconnected automatically.");
+ }
+ catch (Exception ex)
+ {
+ taskSource?.TrySetException(ex);
+ //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers.");
}
}
- catch (IOException)
- {
- // Thrown if the printer times out the socket connection
- // default is 90 seconds
- //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing IOException... sometimes happens with network printers. Should get reconnected automatically.");
- }
- catch
- {
- // Swallow the exception
- //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers.");
- }
+ while (!isAwaitableWrite && !writeSuccess);
}
+
+ Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Write Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
protected virtual async void ReadLongRunningTask()
@@ -146,7 +168,6 @@ protected virtual async void ReadLongRunningTask()
DataAvailable();
}
}
-
catch
{
// Swallow the exception
@@ -155,19 +176,34 @@ protected virtual async void ReadLongRunningTask()
}
}
- public virtual void Write(params byte[][] arrays)
+ ///
+ public virtual void Write(params byte[][] byteArrays)
{
- Write(ByteSplicer.Combine(arrays));
+ Write(ByteSplicer.Combine(byteArrays));
}
+ ///
public virtual void Write(byte[] bytes)
{
- WriteBuffer.Enqueue(bytes);
+ _writeBuffer.Add((bytes, null));
}
- protected virtual void WriteToBinaryWriter(byte[] bytes)
+ ///
+ public virtual async Task WriteAsync(params byte[][] byteArrays)
{
+ await WriteAsync(ByteSplicer.Combine(byteArrays));
+ }
+ ///
+ public virtual async Task WriteAsync(byte[] bytes)
+ {
+ var taskSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ _writeBuffer.Add((bytes, taskSource));
+ await taskSource.Task;
+ }
+
+ protected virtual void WriteToBinaryWriter(byte[] bytes)
+ {
if (!IsConnected)
{
Logging.Logger?.LogInformation("[{Function}]:[{PrinterName}] Attempted to write but printer isn't connected. Attempting to reconnect...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
@@ -185,7 +221,6 @@ protected virtual void WriteToBinaryWriter(byte[] bytes)
bool hasFlushed = false;
while (bytesLeft > 0)
{
-
int count = Math.Min(_maxBytesPerWrite, bytesLeft);
try
{
@@ -222,7 +257,6 @@ public virtual void Flush(object sender, ElapsedEventArgs e)
{
try
{
-
BytesWrittenSinceLastFlush = 0;
Writer.Flush();
}
@@ -232,9 +266,9 @@ public virtual void Flush(object sender, ElapsedEventArgs e)
}
}
- public virtual void DataAvailable()
+ private void DataAvailable()
{
- if (ReadBuffer.Count() % 4 == 0)
+ if (ReadBuffer.Count % 4 == 0)
{
var bytes = new byte[4];
for (int i = 0; i < 4; i++)
@@ -321,6 +355,14 @@ protected virtual void Dispose(bool disposing)
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during cancellation token cancellation call.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
try
+ {
+ _writeBuffer.CompleteAdding();
+ }
+ catch (ObjectDisposedException e)
+ {
+ Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during closing write buffer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
+ }
+ try
{
Reader?.Close();
}
diff --git a/ESCPOS_NET/Printers/IPrinter.cs b/ESCPOS_NET/Printers/IPrinter.cs
index 82530d6..6bd596b 100644
--- a/ESCPOS_NET/Printers/IPrinter.cs
+++ b/ESCPOS_NET/Printers/IPrinter.cs
@@ -1,11 +1,55 @@
using System;
+using System.Threading.Tasks;
namespace ESCPOS_NET
{
public interface IPrinter
{
PrinterStatusEventArgs GetStatus();
- void Write(params byte[][] arrays);
+ ///
+ /// Write byte array of array to the printer stream. This function discards the and the parameter will still be written
+ /// to the printer stream when the connection restored if this instance is disposed yet
+ ///
+ /// Array of byte array which to be flattened to the overloaded function.
+ ///
+ void Write(params byte[][] byteArrays);
+ ///
+ /// Write byte array of array to the printer stream. This function discards the and the parameter will still be written
+ /// to the printer stream when the connection restored if this instance is disposed yet
+ ///
+ /// Byte array to write to the printer stream.
+ ///
void Write(byte[] bytes);
+ ///
+ /// Write byte array of array to the printer stream.
+ ///
+ /// Array of byte array which to be flattened to the overloaded function.
+ ///
+ ///
+ /// await or Wait() this function to await the operation and it would properly capture the exception otherwise the exception will be swallowed.
+ /// Not await nor Wait() this function would discard the and the parameter will still be written
+ /// to the printer stream when the connection restored if this instance is disposed yet.
+ ///
+ /// The is reach or Attempt to write stream to the disconnected connection
+ Task WriteAsync(params byte[][] byteArrays);
+ ///
+ /// Write byte array of array to the printer stream.
+ ///
+ /// Byte array to write to the printer stream.
+ ///
+ ///
+ /// await or Wait() this function to await the operation and it would properly capture the exception otherwise the exception will be swallowed.
+ /// Not await nor Wait() this function would discard the and the parameter will still be written
+ /// to the printer stream when the connection restored if this instance is disposed yet.
+ ///
+ /// The is reach or Attempt to write stream to the disconnected connection
+ Task WriteAsync(byte[] bytes);
+
+ event EventHandler StatusChanged;
+ event EventHandler Disconnected;
+ event EventHandler Connected;
+ //event EventHandler WriteFailed;
+ //event EventHandler Idle;
+ //event EventHandler IdleDisconnected; is this useful? to know that it disconnected because of idle? probably better to have this as info in disconnected event object instead.
}
}
\ No newline at end of file
diff --git a/ESCPOS_NET/Printers/NetworkPrinter.cs b/ESCPOS_NET/Printers/NetworkPrinter.cs
index a1dba44..f650987 100644
--- a/ESCPOS_NET/Printers/NetworkPrinter.cs
+++ b/ESCPOS_NET/Printers/NetworkPrinter.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Threading.Tasks;
using System.Reflection;
+using System.Net.Sockets;
namespace ESCPOS_NET
{
@@ -26,6 +27,8 @@ public class NetworkPrinter : BasePrinter
private readonly NetworkPrinterSettings _settings;
private TCPConnection _tcpConnection;
+ protected override bool IsConnected => _tcpConnection?.IsConnected??false;
+
public NetworkPrinter(NetworkPrinterSettings settings) : base(settings.PrinterName)
{
_settings = settings;
@@ -43,17 +46,20 @@ public NetworkPrinter(NetworkPrinterSettings settings) : base(settings.PrinterNa
private void ConnectedEvent(object sender, ClientConnectedEventArgs e)
{
Logging.Logger?.LogInformation("[{Function}]:[{PrinterName}] Connected successfully to network printer! Connection String: {ConnectionString}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, _settings.ConnectionString);
- IsConnected = true;
+ // Close previously created reader and writer if any (for handling reconnect after connection lose (in the future))
+ Reader?.Close();
+ Writer?.Close();
+ Reader = new BinaryReader(_tcpConnection.ReadStream);
+ Writer = new BinaryWriter(_tcpConnection.WriteStream);
InvokeConnect();
}
private void DisconnectedEvent(object sender, ClientDisconnectedEventArgs e)
{
- IsConnected = false;
InvokeDisconnect();
Logging.Logger?.LogWarning("[{Function}]:[{PrinterName}] Network printer connection terminated. Attempting to reconnect. Connection String: {ConnectionString}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, _settings.ConnectionString);
Connect();
}
- private void AttemptReconnectInfinitely()
+ private async ValueTask AttemptReconnectInfinitely()
{
try
{
@@ -63,11 +69,29 @@ private void AttemptReconnectInfinitely()
catch
{
//Logging.Logger?.LogWarning("[{Function}]:[{PrinterName}] Network printer unable to connect after 5 minutes. Attempting to reconnect. Settings: {Settings}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, JsonSerializer.Serialize(_settings));
- Task.Run(async () => { await Task.Delay(250); Connect(); });
+ await Task.Delay(250);
+ CreateTcpConnection();
+ await AttemptReconnectInfinitely();
}
}
private void Connect()
+ {
+ CreateTcpConnection();
+ try
+ {
+ _tcpConnection.Connect();
+ }
+ catch (SocketException)
+ {
+ if (!IsConnected)
+ {
+ Task.Run(async () => { await AttemptReconnectInfinitely(); });
+ }
+ }
+ }
+
+ private void CreateTcpConnection()
{
if (_tcpConnection != null)
{
@@ -81,16 +105,19 @@ private void Connect()
// set events
_tcpConnection.Connected += ConnectedEvent;
_tcpConnection.Disconnected += DisconnectedEvent;
-
- Reader = new BinaryReader(_tcpConnection.ReadStream);
- Writer = new BinaryWriter(_tcpConnection.WriteStream);
-
- Task.Run(() => { AttemptReconnectInfinitely(); });
}
protected override void OverridableDispose()
{
+ // Dispose to close tcp connection when the printer object is disposed otherwise
+ // the tcp connection is held until garbage collected
+ _tcpConnection?.Dispose();
_tcpConnection = null;
}
+
+ ~NetworkPrinter()
+ {
+ Dispose(true);
+ }
}
}
diff --git a/ESCPOS_NET/Printers/SerialPrinter.cs b/ESCPOS_NET/Printers/SerialPrinter.cs
index a9a6041..2c3e2e9 100644
--- a/ESCPOS_NET/Printers/SerialPrinter.cs
+++ b/ESCPOS_NET/Printers/SerialPrinter.cs
@@ -8,6 +8,11 @@ public class SerialPrinter : BasePrinter
{
private readonly SerialPort _serialPort;
+ ///
+ /// Expose to be reused elsewhere as Serial Port will be held open.
+ ///
+ public SerialPort SerialPort => _serialPort;
+
public SerialPrinter(string portName, int baudRate)
: base()
{
diff --git a/ESCPOS_NET/Utils/TCPConnection.cs b/ESCPOS_NET/Utils/TCPConnection.cs
index 559dbd6..f69c98f 100644
--- a/ESCPOS_NET/Utils/TCPConnection.cs
+++ b/ESCPOS_NET/Utils/TCPConnection.cs
@@ -5,7 +5,7 @@
namespace ESCPOS_NET
{
- public class TCPConnection
+ public class TCPConnection : IDisposable
{
public Stream ReadStream { get; private set; } = new EchoStream();
public Stream WriteStream { get; private set; }
@@ -36,12 +36,30 @@ private void DataReceivedEventHandler(object sender, DataReceivedEventArgs e)
{
ReadStream.Write(e.Data, 0, e.Data.Length);
}
+ ///
+ /// Establish a connection to the server without retry. SocketException will be thrown after the certain period of attempts.
+ ///
+ ///
+ public void Connect()
+ {
+ _client.Connect();
+ }
public void ConnectWithRetries(int timeoutMs)
{
_client.ConnectWithRetries(timeoutMs);
}
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
~TCPConnection()
+ {
+ Dispose(false);
+ }
+
+ private void Dispose(bool disposing)
{
try
{
@@ -49,9 +67,17 @@ public void ConnectWithRetries(int timeoutMs)
_client.Events.Connected -= ConnectedEventHandler;
_client.Events.Disconnected -= DisconnectedEventHandler;
_client?.Dispose();
+ _client = null;
+ }
+ catch { }
+ try
+ {
+ WriteStream?.Dispose();
+ ReadStream?.Dispose();
+ WriteStream = null;
+ ReadStream = null;
}
catch { }
}
-
}
}