vadavo / NEscPos

ESC/POS Library for .NET Standard.
MIT License
15 stars 4 forks source link

ESCPOS.NET #1

Open lukevp opened 5 years ago

lukevp commented 5 years ago

Hello, Curious if you tried ESCPOS.NET (my .net standard ESC POS library) before you started this project, and if you had any concerns or comments on it which you felt could be better solved by implementing it, or if you just hadn't tried it?

Would you be interested in applying a license to this project in case others want to use it? Or contributing to my library?

montyclt commented 5 years ago

Hello,

Sorry, I din't try your library (for now, I promise you I'll try it)

This project was started 11 months ago (called ESCPOS.NET) when VADAVO opened its first physical store because I wasn't found any library other ESC/POS library that works with network and .NET Core (https://github.com/yukimizake/ThermalDotNet only works in serial port).

A week ago I made some changes and I renamed to NEscPos (Net Esc Pos). This is why the solution file name is ESCPOS.NET, but I will change it.

The idea is upload this project to NuGet and apply an open-source license like MIT. For now, you are free to download the sources, compile and use in your projects.

The main roadmap to this project is:

If you like it, we can merge the two projects ;)

Thanks!

lukevp commented 5 years ago

Sounds like a good roadmap, pretty similar to mine. My library is .net standard so it works in full framework as well as .net core (it is cross platform and is tested on Linux and windows). Serial printing and USB printing are both currently implemented (on Windows you use virtual com over USB), but I plan to add network printing as well.

I just updated the library to support automatic status back yesterday, so you can monitor the status in real time. It is also MIT licensed and currently published to nuget. It also has some optimizations on write buffer flushing with multi threading on the communications layer. I also feel that the way I have implemented the command generation is robust and concise when using it, since I have added helper methods and overloads that make dealing with the byte generation easy.

The main reason I started mine was because I believe that command generation should be decoupled from the printing. I intend for my library to eventually have a typescript wrapper and self hosting in Kestrel so it can be used from Electron applications or other networked applications.

I would be interested in merging your network code if you could give it a try on your network printer. Does the code explicitly list the MIT license anywhere? Or can you add the license config to this repo? I want to be sure the licenses are compatible before I merge anything.

montyclt commented 5 years ago

NEscPos targets too .NET Standard and is tested in a WPF Desktop app (.NET Framework 4.7) and in a console app (.NET Core 2.2) in Windows.

In NEscPos, the commands generators are decuopled of the printer via a IPrintable interface, for example, the cut command is in a Cut class that implements IPrintable interface.

Today, I added a LICENSE file for you.

The printer class has some methods for most used commands like feed or cut, but I will delete these methods from the printer class and provide extensions methods.

The usage of NEscPos is very simple:

class Receipt : IPrintable
{
    ...
    public byte[] GetBytes() {...}
}

using (IPrinter printer = new Printer(new NetworkConnector(“10.0.0.1”, 9100)) // UsbConnector, FileConnector and SerialConnector will be supported
{
    printer.Print("Hello!");
    printer.Cut(); // the same that printer.Print(new Cut())

    var receipt = new Receipt() {...};
    printer.Print(receipt); // The Print method is overloaded for support string and IPrintable instance.
}

In my roadmap is delete the Cut, Feed and others methods from IPrinter and add this methods from extensions methods.

lukevp commented 5 years ago

Thank you for the update. I like your plan to remove the commands from IPrinter. You should give my library a try and let me know how it works for you. I will add network printer support this weekend.

montyclt commented 5 years ago

Sorry for not reply, I see the comment today because we are in Fallas (traditional festivals of my city). The next weekend I will try your library and works on mine.

montyclt commented 5 years ago

@lukevp I can't try your library until network connector will done because I don't have USB cable or serial cable.

lukevp commented 5 years ago

Hey @montyclt , I've pushed a new version that includes support for network printers. Thank you for placing your work under MIT so I could use the same network strategy (although I use streams to interact with each device type so I can use a base printer type).

The library is live on nuget as package version 0.3.0 and is on Github as well. Can you please check out the project locally and run ESCPOS_NET.ConsoleTest?

I do not have a network printer, so unfortunately I am unable to test it! so I would be really grateful if you could test it and let me know how it works! Feel free to enter an issue if something doesn't work and I'll look into it. I'm especially interested if the optimized write buffer flushing and the live status back are properly working on network printers.

The library is also available on NuGet but I would recommend starting with the test app. https://www.nuget.org/packages/ESCPOS_NET/

montyclt commented 5 years ago

@lukevp I cloned your repository and executed all test via network. All OK, my printer prints the 5 examples without any problems.

lukevp commented 5 years ago

@montyclt thank you for testing the library! What do you think of the functionality, is there any feature you are currently missing that you would like me to investigate? Are there some functions in your library that you wish to see in mine?

montyclt commented 5 years ago

I don't like the idea of the CommandEmitter interface that contains all commands in the same class. I preffer a class per command, p.e. CutCommand class that implement a Command interface with a method like byte[] GetBytes() or similar.

This allow to create a class that implements this interface, can be directly passed to the printer, for example, a Invoice class with all products, quantities and amount. The instance of Invoice class can be passed to the printer, and the invoice is printed.

Something like this:

class Invoice : IPrintable
{
...
}
var invoice = new Invoice {...};

printer.Print(invoice);

Each commands like feed, cut, etc. should be a implementation of the interface.

printer.Print(new Cut());

An extension method can be added optionally:

static class CutExtension
{
    static void Cut(this IPrinter printer)
    {
        printer.Print(new Cut());
    }
}

Can be used:

printer.Cut();

I think this can be a good design, but I able to read opinions.

I will work this week in the USB and Serials connector and in the implementation of a test project. When was done, can you test it with USB and serial connectors?

montyclt commented 5 years ago

Can you decouple your ArrayByteBuilder on another project? I like to use it in my library. Thank you!

lukevp commented 5 years ago

Sure, I will test your USB / Serial for you! Just let me know.

The architecture of my library is intended to support multiple raw / binary printers that create different command sets, as well as supporting remote usage and reprints. For example, with my library you could have a Command Emitter running on one machine and a SerialPrinter running on another, with the Serial Printer the only one that could physically connect to the printer being used. It seems wasteful right now when there is only an Epson implementation of the command emitter, but theoretically it could be expanded to emit Zebra commands or others. In this case, communication to the Zebra printer is the same as communication to an Epson, the only difference is the bytes sent. For that reason, it makes sense to prevent tight coupling between the abstraction on the device and the abstraction on the command sets.

Another use case could be receipt reprints. You could either store all pieces of data in the database and re-create the receipt on demand, or you could generate the raw bytes, save them to disk, and then pass them to the printer for immediate printing as well. With the model where the commands are directly on the printer, to support this you would have to make multiple printers (a file printer and a serial printer, for example) and then perform the calls onto both.

The only purpose I see of having the commands directly on the printer is that the calling pattern makes slightly more sense, but if you look at the receipt example, it's pretty easy to generate a joined byte array and pass it to the printer in one line, so it's not significantly beneficial either way and I feel the trade-offs are not worth the gains. Your approach is the more common one, most libraries are implemented the way yours is.

As for the ArrayByteBuilder, I will look into that this weekend and let you know!

lukevp commented 5 years ago

I should mention as well that there is no reason an IPrintable could not be created that my Printer could take as a parameter to the Write method and pull the byte array out of it as well. In that case, the byte array would be created in the ToByteArray method of the IPrintable object. I will investigate this as well.

montyclt commented 5 years ago

I created a test project that run in console like your project and implemented a serial connector (that only writes).

Simple clone the repository and run NEscPos.Test project, select "serial connector", set the serial port name and the baud rate and test if the printer prints a ticket.

montyclt commented 5 years ago

Hello @lukevp,

I worked today improving my library and the changes that I made was:

Now, constructing a IPrintable class is easier, imagine I'm writting a software that prints a role card for a board game (I need print a card for each player that contains his role).

/// <summary>
///    This class represents a role class.
/// </summary>
class Card : IPrintable
{
    public string PlayerName {get;set;}
    public string Role {get;set;}

    public Card(string playerName, string role)
    {
        PlayerName = playerName;
        Role = role;
    }

    byte[] IPrintable.GetBytes()
    {
        return new PrintableBuilder()
                .Add(new TextLine($"Hello {PlayerName}, your role is:"))
                .Add(new Justification(JustificationType.Center))
                .Add(new Font(FontMode.DoubleWidth | FontMode.DoubleHeight))
                .Add(new TextLine(Role))
                .ToArray();
    }
}

And you can instance it and pass direcly to the printer:

var card = new Card("Monty", "Villager");
printer.Print(card);

And this is the result:

Villager card

The problem here is Role.GetBytes() method returns an byte array coupled to EPSON commands. I'm investigating how I can modify the PrintableBuilder class to allow receive a command set and generate commands for that command set, allowing using Zebra or others commands.

efrainburton13 commented 2 years ago

Hello. A query. How can I disconnect the port? I've tried and I can't seem to. I would like you to help me. IsConnected is always true.

montyclt commented 2 years ago

Hello. A query. How can I disconnect the port? I've tried and I can't seem to. I would like you to help me. IsConnected is always true.

What port are you trying to disconnect?