diff --git a/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj b/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj index 915b7dc..90f5af2 100644 --- a/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj +++ b/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/ESCPOS_NET.ConsoleTest/Program.cs b/ESCPOS_NET.ConsoleTest/Program.cs index 3a93888..c0165e3 100644 --- a/ESCPOS_NET.ConsoleTest/Program.cs +++ b/ESCPOS_NET.ConsoleTest/Program.cs @@ -92,6 +92,8 @@ static void Main(string[] args) printer = new NetworkPrinter(settings: new NetworkPrinterSettings() { ConnectionString = $"{ip}:{networkPort}" }); } + printer.Connect(); + bool monitor = false; Thread.Sleep(500); Console.Write("Turn on Live Status Back Monitoring? (y/n): "); diff --git a/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs b/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs index 5dac730..b3756cb 100644 --- a/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs +++ b/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs @@ -13,6 +13,12 @@ public abstract partial class BaseCommandEmitter : ICommandEmitter public virtual byte[] RightAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Right }; + public virtual byte[] LeftAlignAlt() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.LeftAlt }; + + public virtual byte[] CenterAlignAlt() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.CenterAlt }; + + public virtual byte[] RightAlignAlt() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.RightAlt }; + public virtual byte[] RightCharacterSpacing(int spaceCount) => new byte[] { Cmd.ESC, Chars.RightCharacterSpacing, (byte)spaceCount }; public virtual byte[] CodePage(CodePage codePage) => new byte[] { Cmd.ESC, Chars.CodePage, (byte)codePage }; diff --git a/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs b/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs index 8f40497..00ed00e 100644 --- a/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs +++ b/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs @@ -10,5 +10,7 @@ public abstract partial class BaseCommandEmitter : ICommandEmitter public virtual byte[] Enable() => new byte[] { Cmd.ESC, Ops.EnableDisable, 1 }; public virtual byte[] Disable() => new byte[] { Cmd.ESC, Ops.EnableDisable, 0 }; + + public virtual byte[] StandardMode() => new byte[] { Cmd.ESC, Ops.StandardMode }; } } diff --git a/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs b/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs index 9cf4263..e9bdf27 100644 --- a/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs +++ b/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs @@ -6,5 +6,6 @@ public static class Ops public static readonly byte EnableDisable = 0x3D; public static readonly byte PaperCut = 0x56; public static readonly byte CashDrawerPulse = 0x70; + public static readonly byte StandardMode = 0x1B; } } diff --git a/ESCPOS_NET/Emitters/Enums/Align.cs b/ESCPOS_NET/Emitters/Enums/Align.cs index be7ef6b..ba54a09 100644 --- a/ESCPOS_NET/Emitters/Enums/Align.cs +++ b/ESCPOS_NET/Emitters/Enums/Align.cs @@ -6,5 +6,8 @@ public enum Align Left = 0, Center = 1, Right = 2, + LeftAlt = 48, + CenterAlt = 49, + RightAlt = 50 } } diff --git a/ESCPOS_NET/Emitters/Enums/PrintStyle.cs b/ESCPOS_NET/Emitters/Enums/PrintStyle.cs index ab2b921..48c4b31 100644 --- a/ESCPOS_NET/Emitters/Enums/PrintStyle.cs +++ b/ESCPOS_NET/Emitters/Enums/PrintStyle.cs @@ -8,9 +8,12 @@ public enum PrintStyle { None = 0, FontB = 1, + Proportional = 1 << 1, + Condensed = 1 << 2, Bold = 1 << 3, DoubleHeight = 1 << 4, DoubleWidth = 1 << 5, + Italic = 1 << 6, Underline = 1 << 7, } } diff --git a/ESCPOS_NET/Printers/BasePrinter.cs b/ESCPOS_NET/Printers/BasePrinter.cs index 11a7c16..008001e 100644 --- a/ESCPOS_NET/Printers/BasePrinter.cs +++ b/ESCPOS_NET/Printers/BasePrinter.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -18,7 +19,9 @@ public abstract partial class BasePrinter : IPrinter, IDisposable //private volatile bool _isMonitoring; private CancellationTokenSource _readCancellationTokenSource; + private bool _readTaskRunning = false; private CancellationTokenSource _writeCancellationTokenSource; + private bool _writeTaskRunning = false; private readonly int _maxBytesPerWrite = 15000; // max byte chunks to write at once. @@ -27,12 +30,6 @@ public abstract partial class BasePrinter : IPrinter, IDisposable public event EventHandler StatusChanged; public event EventHandler Disconnected; public event EventHandler Connected; - //public event EventHandler WriteFailed; - //public event EventHandler Idle; - - protected BinaryWriter Writer { get; set; } - - protected BinaryReader Reader { get; set; } protected ConcurrentQueue ReadBuffer { get; set; } = new ConcurrentQueue(); @@ -40,14 +37,17 @@ public abstract partial class BasePrinter : IPrinter, IDisposable protected int BytesWrittenSinceLastFlush { get; set; } = 0; - protected volatile bool IsConnected = true; + protected volatile bool IsConnected = true; public string PrinterName { get; protected set; } + protected abstract int ReadBytesUnderlying(byte[] buffer, int offset, int bufferSize); + protected abstract void WriteBytesUnderlying(byte[] buffer, int offset, int count); + protected abstract void FlushUnderlying(); + protected BasePrinter() { PrinterName = Guid.NewGuid().ToString(); - Init(); } protected BasePrinter(string printerName) { @@ -56,105 +56,110 @@ protected BasePrinter(string printerName) printerName = Guid.NewGuid().ToString(); } PrinterName = printerName; - Init(); } - private void Init() + + public virtual void Connect(bool reconnecting = false) { - _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(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); + if (!reconnecting) + { + _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(WriteLongRunningAsync, _writeCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false); + Task.Factory.StartNew(ReadLongRunningAsync, _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); + } } - protected void InvokeConnect() { - Task.Run(() => Connected?.Invoke(this, new ConnectionEventArgs() { IsConnected = true })); + Connected?.Invoke(this, new ConnectionEventArgs() { IsConnected = true }); } protected void InvokeDisconnect() { - Task.Run(() => Disconnected?.Invoke(this, new ConnectionEventArgs() { IsConnected = false })); + Disconnected?.Invoke(this, new ConnectionEventArgs() { IsConnected = false }); } - protected virtual void Reconnect() - { - // Implemented in the network printer - } - protected virtual async void WriteLongRunningTask() + protected virtual async void WriteLongRunningAsync() { + _writeTaskRunning = true; + List internalWriteBuffer = new List(); while (true) { - if (_writeCancellationTokenSource != null && _writeCancellationTokenSource.IsCancellationRequested) - { - 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; - } try { var didDequeue = WriteBuffer.TryDequeue(out var nextBytes); if (didDequeue && nextBytes?.Length > 0) { - WriteToBinaryWriter(nextBytes); + internalWriteBuffer.AddRange(nextBytes); + WriteToBinaryWriter(ref internalWriteBuffer); } } 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."); + 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."); + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers."); + } + + if (!WriteBuffer.Any() && _writeCancellationTokenSource != null && _writeCancellationTokenSource.IsCancellationRequested) + { + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Write Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + break; } } + + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Write Long-Running Task has exited.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + _writeTaskRunning = false; } - protected virtual async void ReadLongRunningTask() + protected virtual async void ReadLongRunningAsync() { + _readTaskRunning = true; while (true) { - if (_readCancellationTokenSource != null && _readCancellationTokenSource.IsCancellationRequested) - { - Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Read Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - break; - } - await Task.Delay(100); - if (Reader == null) continue; - if (!IsConnected) continue; - try { // Sometimes the serial port lib will throw an exception and read past the end of the queue if a // status changes while data is being written. We just ignore these bytes. - var b = Reader.BaseStream.ReadByte(); - if (b >= 0 && b <= 255) + byte[] buffer = new byte[4096]; + int readBytes = this.ReadBytesUnderlying(buffer, 0, 4096); + if (readBytes > 0) { - ReadBuffer.Enqueue((byte)b); - DataAvailable(); + for (int ix = 0; ix < readBytes; ix++) + { + ReadBuffer.Enqueue((byte)buffer[ix]); + DataAvailable(); + } } } - + catch - { + { // Swallow the exception //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); } + + if (_readCancellationTokenSource != null && _readCancellationTokenSource.IsCancellationRequested) + { + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Read Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + break; + } } + + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Read Long-Running Task has exited.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + _readTaskRunning = false; } public virtual void Write(params byte[][] arrays) @@ -167,56 +172,31 @@ public virtual void Write(byte[] bytes) WriteBuffer.Enqueue(bytes); } - protected virtual void WriteToBinaryWriter(byte[] bytes) + protected void WriteToBinaryWriter(ref List bytes) { - - if (!IsConnected) - { - Logging.Logger?.LogInformation("[{Function}]:[{PrinterName}] Attempted to write but printer isn't connected. Attempting to reconnect...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - Reconnect(); - } - - if (!IsConnected) + try { - Logging.Logger?.LogError("[{Function}]:[{PrinterName}] Unrecoverable connectivity error writing to printer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - throw new IOException("Unrecoverable connectivity error writing to printer."); - } + while (bytes.Count > 0) + { - int bytePointer = 0; - int bytesLeft = bytes.Length; - bool hasFlushed = false; - while (bytesLeft > 0) - { + int count = Math.Min(_maxBytesPerWrite, bytes.Count); + this.WriteBytesUnderlying(bytes.ToArray(), 0, count); + bytes.RemoveRange(0, count); - int count = Math.Min(_maxBytesPerWrite, bytesLeft); - try - { - Writer.Write(bytes, bytePointer, count); - } - catch (IOException e) - { - Reconnect(); - if (!IsConnected) + BytesWrittenSinceLastFlush += count; + if (BytesWrittenSinceLastFlush >= 200) { - Logging.Logger?.LogError(e, "[{Function}]:[{PrinterName}] Unrecoverable connectivity error writing to printer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + // Immediately trigger a flush before proceeding so the output buffer will not be delayed. + Flush(null, null); } - Writer.Write(bytes, bytePointer, count); - } - BytesWrittenSinceLastFlush += count; - if (BytesWrittenSinceLastFlush >= 200) - { - // Immediately trigger a flush before proceeding so the output buffer will not be delayed. - hasFlushed = true; - Flush(null, null); } - bytePointer += count; - bytesLeft -= count; + Flush(null, null); } - - if (!hasFlushed) + catch (IOException e) { - Task.Run(async () => { await Task.Delay(50); Flush(null, null); }); + // Network or serial connection failed, dont consume the buffer this time around + Logging.Logger?.LogDebug(e, "Device appears disconnected. No more bytes will be written until it is reconnected."); } } @@ -224,9 +204,8 @@ protected virtual void Flush(object sender, ElapsedEventArgs e) { try { - BytesWrittenSinceLastFlush = 0; - Writer.Flush(); + this.FlushUnderlying(); } catch (Exception ex) { @@ -292,6 +271,7 @@ private void TryUpdatePrinterStatus(byte[] bytes) ~BasePrinter() { + Flush(this, null); Dispose(false); } @@ -317,43 +297,15 @@ protected virtual void Dispose(bool disposing) try { _readCancellationTokenSource?.Cancel(); + _writeCancellationTokenSource?.Cancel(); } catch (Exception e) { Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during cancellation token cancellation call.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); } - try - { - Reader?.Close(); - } - catch (Exception e) - { - Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue closing reader.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - } - try - { - Reader?.Dispose(); - } - catch (Exception e) - { - Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue disposing reader.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - } - try - { - Writer?.Close(); - } - catch (Exception e) - { - Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue closing writer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - } - try - { - Writer?.Dispose(); - } - catch (Exception e) - { - Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue disposing writer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); - } + + while (_readTaskRunning || _writeTaskRunning) Thread.Sleep(100); + try { OverridableDispose(); diff --git a/ESCPOS_NET/Printers/FilePrinter.cs b/ESCPOS_NET/Printers/FilePrinter.cs index d49ad23..4f38536 100644 --- a/ESCPOS_NET/Printers/FilePrinter.cs +++ b/ESCPOS_NET/Printers/FilePrinter.cs @@ -4,11 +4,19 @@ namespace ESCPOS_NET { public class FilePrinter : BasePrinter { - private readonly FileStream _file; + private FileStream _file; + private bool createIfNotExists; + private string filePath; // TODO: default values to their default values in ctor. public FilePrinter(string filePath, bool createIfNotExists = false) : base() + { + this.createIfNotExists = createIfNotExists; + this.filePath = filePath; + } + + public override void Connect(bool reconnecting = false) { if (createIfNotExists) { @@ -17,9 +25,25 @@ public FilePrinter(string filePath, bool createIfNotExists = false) else { _file = File.Open(filePath, FileMode.Open); + //if (_file.CanSeek) _file.Seek(0, SeekOrigin.End); } - Writer = new BinaryWriter(_file); - Reader = new BinaryReader(_file); + + base.Connect(reconnecting); + } + + protected override int ReadBytesUnderlying(byte[] buffer, int offset, int bufferSize) + { + return _file.Read(buffer, offset, bufferSize); + } + + protected override void WriteBytesUnderlying(byte[] buffer, int offset, int count) + { + _file.Write(buffer, offset, count); + } + + protected override void FlushUnderlying() + { + _file.Flush(); } ~FilePrinter() diff --git a/ESCPOS_NET/Printers/MemoryPrinter.cs b/ESCPOS_NET/Printers/MemoryPrinter.cs index 11060f1..8563656 100644 --- a/ESCPOS_NET/Printers/MemoryPrinter.cs +++ b/ESCPOS_NET/Printers/MemoryPrinter.cs @@ -11,7 +11,6 @@ public MemoryPrinter() : base() { _ms = new MemoryStream(); - Writer = new BinaryWriter(_ms); } ~MemoryPrinter() @@ -24,6 +23,21 @@ public byte[] GetAllData() return _ms.ToArray(); } + protected override int ReadBytesUnderlying(byte[] buffer, int offset, int bufferSize) + { + return 0; + } + + protected override void WriteBytesUnderlying(byte[] buffer, int offset, int count) + { + _ms.Write(buffer, offset, count); + } + + protected override void FlushUnderlying() + { + _ms.Flush(); + } + protected override void OverridableDispose() { _ms?.Close(); diff --git a/ESCPOS_NET/Printers/NetworkPrinter.cs b/ESCPOS_NET/Printers/NetworkPrinter.cs index a1dba44..1d77c89 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.Threading; namespace ESCPOS_NET { @@ -21,6 +22,7 @@ public class NetworkPrinterSettings //public uint? MaxReconnectAttempts { get; set; } public string PrinterName { get; set; } } + public class NetworkPrinter : BasePrinter { private readonly NetworkPrinterSettings _settings; @@ -37,7 +39,6 @@ public NetworkPrinter(NetworkPrinterSettings settings) : base(settings.PrinterNa { Disconnected += settings.DisconnectedHandler; } - Connect(); } private void ConnectedEvent(object sender, ClientConnectedEventArgs e) @@ -46,34 +47,19 @@ private void ConnectedEvent(object sender, ClientConnectedEventArgs e) IsConnected = true; 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() - { - try - { - //_tcpConnection.ConnectWithRetries(300000); - _tcpConnection.ConnectWithRetries(3000); - } - 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(); }); - } + Connect(true); } - private void Connect() + public override void Connect(bool reconnecting = false) { - if (_tcpConnection != null) - { - _tcpConnection.Connected -= ConnectedEvent; - _tcpConnection.Disconnected -= DisconnectedEvent; - } + + OverridableDispose(); // instantiate _tcpConnection = new TCPConnection(_settings.ConnectionString); @@ -82,15 +68,35 @@ private void Connect() _tcpConnection.Connected += ConnectedEvent; _tcpConnection.Disconnected += DisconnectedEvent; - Reader = new BinaryReader(_tcpConnection.ReadStream); - Writer = new BinaryWriter(_tcpConnection.WriteStream); + _tcpConnection.ConnectWithRetries(3000); - Task.Run(() => { AttemptReconnectInfinitely(); }); + base.Connect(reconnecting); + } + + protected override void WriteBytesUnderlying(byte[] buffer, int offset, int count) + { + _tcpConnection.WriteStream?.Write(buffer, offset, count); + } + + protected override int ReadBytesUnderlying(byte[] buffer, int offset, int bufferSize) + { + return _tcpConnection.ReadStream?.Read(buffer, offset, bufferSize) ?? 0; + } + + protected override void FlushUnderlying() + { + _tcpConnection.WriteStream?.Flush(); } protected override void OverridableDispose() { - _tcpConnection = null; + if (_tcpConnection != null) + { + _tcpConnection.Connected -= ConnectedEvent; + _tcpConnection.Disconnected -= DisconnectedEvent; + _tcpConnection?.Dispose(); + _tcpConnection = null; + } } } } diff --git a/ESCPOS_NET/Printers/SerialPrinter.cs b/ESCPOS_NET/Printers/SerialPrinter.cs index a9a6041..5440367 100644 --- a/ESCPOS_NET/Printers/SerialPrinter.cs +++ b/ESCPOS_NET/Printers/SerialPrinter.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.IO.Ports; using System.Threading.Tasks; @@ -6,22 +7,49 @@ namespace ESCPOS_NET { public class SerialPrinter : BasePrinter { - private readonly SerialPort _serialPort; + private readonly string portName; + private readonly int baudRate; + private SerialPort _serialPort; public SerialPrinter(string portName, int baudRate) : base() + { + this.portName = portName; + this.baudRate = baudRate; + } + + public override void Connect(bool reconnecting = false) { _serialPort = new SerialPort(portName, baudRate); _serialPort.Open(); - Writer = new BinaryWriter(_serialPort.BaseStream); - Reader = new BinaryReader(_serialPort.BaseStream); + + base.Connect(reconnecting); + } + + protected override int ReadBytesUnderlying(byte[] buffer, int offset, int bufferSize) + { + if (this._serialPort.BytesToRead == 0) return 0; + return this._serialPort.Read(buffer, 0, Math.Min(bufferSize, this._serialPort.BytesToRead)); + } + + protected override void WriteBytesUnderlying(byte[] buffer, int offset, int count) + { + this._serialPort.Write(buffer, 0, count); + } + + protected override void FlushUnderlying() + { + // noop } protected override void OverridableDispose() { - _serialPort?.Close(); - _serialPort?.Dispose(); - Task.Delay(250).Wait(); // Based on MSDN Documentation, should sleep after calling Close or some functionality will not be determinant. + if (_serialPort != null) + { + _serialPort?.Close(); + _serialPort?.Dispose(); + Task.Delay(250).Wait(); // Based on MSDN Documentation, should sleep after calling Close or some functionality will not be determinant. + } } } } \ No newline at end of file diff --git a/ESCPOS_NET/Utils/EchoStream.cs b/ESCPOS_NET/Utils/EchoStream.cs index 7013a12..05abe4b 100644 --- a/ESCPOS_NET/Utils/EchoStream.cs +++ b/ESCPOS_NET/Utils/EchoStream.cs @@ -81,16 +81,15 @@ public EchoStream(int maxQueueDepth) // we override the xxxxAsync functions because the default base class shares state between ReadAsync and WriteAsync, which causes a hang if both are called at once public new Task WriteAsync(byte[] buffer, int offset, int count) { - return Task.Run(() => Write(buffer, offset, count)); + Write(buffer, offset, count); + + return Task.CompletedTask; } // we override the xxxxAsync functions because the default base class shares state between ReadAsync and WriteAsync, which causes a hang if both are called at once public new Task ReadAsync(byte[] buffer, int offset, int count) { - return Task.Run(() => - { - return Read(buffer, offset, count); - }); + return Task.FromResult(Read(buffer, offset, count)); } public override void Write(byte[] buffer, int offset, int count) diff --git a/ESCPOS_NET/Utils/TCPConnection.cs b/ESCPOS_NET/Utils/TCPConnection.cs index 559dbd6..2f2f1c4 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; } @@ -13,7 +13,8 @@ public class TCPConnection public event EventHandler Disconnected; public bool IsConnected => _client?.IsConnected ?? false; private SimpleTcpClient _client; - //public event EventHandler DataReceived; + private bool disposedValue; + public TCPConnection(string destination) { _client = new SimpleTcpClient(destination); @@ -24,34 +25,49 @@ public TCPConnection(string destination) ReadStream.ReadTimeout = 1500; WriteStream = new InterceptableWriteMemoryStream(bytes => _client.Send(bytes)); } + private void ConnectedEventHandler(object sender, ClientConnectedEventArgs e) { Connected?.Invoke(sender, e); } + private void DisconnectedEventHandler(object sender, ClientDisconnectedEventArgs e) { Disconnected?.Invoke(sender, e); } + private void DataReceivedEventHandler(object sender, DataReceivedEventArgs e) { ReadStream.Write(e.Data, 0, e.Data.Length); } + public void ConnectWithRetries(int timeoutMs) { _client.ConnectWithRetries(timeoutMs); } - ~TCPConnection() + protected virtual void Dispose(bool disposing) { - try + if (!disposedValue) { - _client.Events.DataReceived -= DataReceivedEventHandler; - _client.Events.Connected -= ConnectedEventHandler; - _client.Events.Disconnected -= DisconnectedEventHandler; - _client?.Dispose(); + if (disposing) + { + _client.Events.DataReceived -= DataReceivedEventHandler; + _client.Events.Connected -= ConnectedEventHandler; + _client.Events.Disconnected -= DisconnectedEventHandler; + _client.Disconnect(); + _client?.Dispose(); + } + + disposedValue = true; } - catch { } } + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/README.md b/README.md index 196762a..c34a89b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ var printer = new SerialPrinter(portName: "COM5", baudRate: 115200); // Linux output to USB / Serial file var printer = new FilePrinter(filePath: "/dev/usb/lp0"); + +printer.Connect(); ``` ## Step 1a (optional): Monitor for Events - out of paper, cover open... ```csharp