add POS895 hardware smoke test project
Pos895Test is a console app that drives a POS895 over TCP through the existing EpsonPrinter class, to answer the first question in the feasibility notes: does the printer accept our ESC/POS at all. It builds the whole job as one byte array and sends that exact array, so the hex listing it prints is the wire content by construction rather than a reconstruction that can drift. Current job prints a "|---" column ruler at 48/width characters per row for width magnifications 1x, 2x and 3x at double height, so every row should end at the same right margin. Confirmed against the hardware so far: - DLE EOT 1..4 are answered and follow the Epson bit layout exactly, with the 0x12 fixed bits set on every reply, so EpsonPrinterStatus parses this printer unmodified. - Font A is 48 columns on the 576-dot line, as the manuals claim. - GS ! magnification works, verified at width 1x/2x/3x and height 2x. - ESC ! 0x00 selects Font A. Not yet confirmed: Font B and its claimed 64 columns, the GS I 1 model byte, ESC d n feed (this firmware appears to ignore it, hence the literal LF bytes before the cut), code page 18 being CP852, and raster graphics. Deliberately does not call GS I 1: the model byte is still an open unknown and a smoke test must not be able to fail on one. All settings are constants at the top of Program.cs; there are no arguments. Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
<Folder Name="/Utils/">
|
||||
<Project Path="EpsonTest.Templates/EpsonTest.Templates.csproj" />
|
||||
<Project Path="EpsonTest/EpsonTest.csproj" />
|
||||
<Project Path="Pos895Test/Pos895Test.csproj" />
|
||||
<Project Path="Inspectron.PrintServer.TemplateEditor/Inspectron.PrintServer.TemplateEditor.csproj" Id="253ec869-47ac-41d4-985a-5923006ef449" />
|
||||
</Folder>
|
||||
<Project Path="EpsonPrintService/EpsonPrintService.csproj" />
|
||||
|
||||
14
Pos895Test/Pos895Test.csproj
Normal file
14
Pos895Test/Pos895Test.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Inspectron.Epson\Inspectron.Epson.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
160
Pos895Test/Program.cs
Normal file
160
Pos895Test/Program.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Text;
|
||||
using Inspectron.Epson;
|
||||
|
||||
// POS895 smoke test: Font A column count vs. GS ! width magnification.
|
||||
//
|
||||
// Prints one ruler row per width magnification, each at 48/width characters, so
|
||||
// every row should end at the same right margin with progressively larger type.
|
||||
// A row that stops short means that magnification was ignored.
|
||||
//
|
||||
// All settings are the constants below. Edit them here; there are no arguments.
|
||||
|
||||
// An AnyDesk TCP tunnel forwards this local endpoint to the remote printer's
|
||||
// port 9100, so the printer is reachable as though it sat on this machine.
|
||||
// A refused connection here usually means the AnyDesk session or its tunnel is
|
||||
// down, not that the printer is off.
|
||||
const string PrinterIp = "127.0.0.1";
|
||||
const int PrinterPort = 8888;
|
||||
const int ConnectTimeoutSeconds = 5;
|
||||
|
||||
// Font A is 12 dots wide on a 576-dot line. Width magnification divides this.
|
||||
const int FontAColumns = 48;
|
||||
const int HeightMagnification = 2;
|
||||
int[] widths = { 1, 2, 3 };
|
||||
|
||||
// Blank lines between the last row and the cut. Literal LF bytes rather than
|
||||
// ESC d n: LF is the most universally implemented feed in ESC/POS, and this
|
||||
// firmware appears to ignore ESC d.
|
||||
const int BlankLinesAfterText = 10;
|
||||
|
||||
// Set true to drop the ESC t 18 (code page 18 / CP852) selection. The ruler is
|
||||
// pure ASCII either way; this only isolates whether ESC t itself is honoured.
|
||||
bool useRawAscii = false;
|
||||
|
||||
byte[] job = BuildJob(FontAColumns, widths, HeightMagnification, BlankLinesAfterText, useRawAscii);
|
||||
|
||||
Console.WriteLine($"POS895 smoke test -> {PrinterIp}:{PrinterPort}");
|
||||
Console.WriteLine($"Font A, height {HeightMagnification}x, widths {string.Join("x / ", widths)}x");
|
||||
Console.WriteLine();
|
||||
foreach (int w in widths)
|
||||
{
|
||||
int cols = FontAColumns / w;
|
||||
Console.WriteLine($" GS ! 0x{SizeByte(w, HeightMagnification):X2} w={w} h={HeightMagnification} {cols,2} cols {Ruler(cols)}");
|
||||
}
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Byte sequence:");
|
||||
Console.WriteLine(Describe(job));
|
||||
Console.WriteLine();
|
||||
|
||||
try
|
||||
{
|
||||
await using var printer = new EpsonPrinter();
|
||||
|
||||
Console.Write("Connecting... ");
|
||||
await printer.ConnectAsync(PrinterIp, PrinterPort, ConnectTimeoutSeconds);
|
||||
Console.WriteLine("connected.");
|
||||
|
||||
Console.Write($"Sending {job.Length} bytes... ");
|
||||
await printer.SendRawCommandAsync(job);
|
||||
Console.WriteLine("sent.");
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("[OK] Bytes sent. All three rows should end at the same right");
|
||||
Console.WriteLine(" margin, each in larger type than the one above it.");
|
||||
return 0;
|
||||
}
|
||||
catch (EpsonConnectionException ex)
|
||||
{
|
||||
Console.Error.WriteLine();
|
||||
Console.Error.WriteLine($"[FAIL] Could not reach {PrinterIp}:{PrinterPort}.");
|
||||
Console.Error.WriteLine(" Is the port forward to the printer's 9100 up?");
|
||||
Console.Error.WriteLine($" {ex.Message}");
|
||||
if (ex.InnerException is not null)
|
||||
{
|
||||
Console.Error.WriteLine($" {ex.InnerException.Message}");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine();
|
||||
Console.Error.WriteLine($"[FAIL] {ex.GetType().Name}: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The whole job as one array, so what gets listed above is exactly what goes on
|
||||
// the wire.
|
||||
static byte[] BuildJob(int fontACols, int[] widths, int heightMag, int blankLines, bool rawAscii)
|
||||
{
|
||||
var job = new List<byte> { 0x1B, 0x40 }; // ESC @ initialize
|
||||
|
||||
if (!rawAscii)
|
||||
{
|
||||
job.AddRange(new byte[] { 0x1B, 0x74, 0x12 }); // ESC t 18 code page 852
|
||||
}
|
||||
|
||||
job.AddRange(new byte[] { 0x1B, 0x21, 0x00 }); // ESC ! 0 Font A, no attributes
|
||||
|
||||
foreach (int w in widths)
|
||||
{
|
||||
job.AddRange(new byte[] { 0x1D, 0x21, SizeByte(w, heightMag) });
|
||||
job.AddRange(Encoding.ASCII.GetBytes(Ruler(fontACols / w)));
|
||||
job.Add(0x0A);
|
||||
}
|
||||
|
||||
job.AddRange(new byte[] { 0x1D, 0x21, 0x00 }); // GS ! 0 back to 1x1
|
||||
|
||||
for (int i = 0; i < blankLines; i++)
|
||||
{
|
||||
job.Add(0x0A); // blank line
|
||||
}
|
||||
|
||||
job.AddRange(new byte[] { 0x1D, 0x56, 0x00 }); // GS V 0 full cut
|
||||
return job.ToArray();
|
||||
}
|
||||
|
||||
// GS ! n: bits 4-6 are width magnification - 1, bits 0-2 height - 1. Same
|
||||
// formula as EpsonPrinter.SetFontSizeAsync.
|
||||
static byte SizeByte(int width, int height) =>
|
||||
(byte)(((height - 1) & 0x07) | (((width - 1) & 0x07) << 4));
|
||||
|
||||
// "|---" repeated and cut to an exact column count, so the pipes fall every
|
||||
// 4 characters and the row ends precisely at the claimed right margin.
|
||||
static string Ruler(int columns)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
while (sb.Length < columns)
|
||||
{
|
||||
sb.Append("|---");
|
||||
}
|
||||
|
||||
return sb.ToString(0, columns);
|
||||
}
|
||||
|
||||
// Hex plus a printable-ASCII gutter, so the output can be compared against the
|
||||
// printer's hex dump mode (open cover, FEED twice within 3 s, close cover).
|
||||
static string Describe(byte[] bytes)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (int offset = 0; offset < bytes.Length; offset += 16)
|
||||
{
|
||||
int count = Math.Min(16, bytes.Length - offset);
|
||||
sb.Append($" {offset:X4} ");
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
sb.Append(i < count ? $"{bytes[offset + i]:X2} " : " ");
|
||||
}
|
||||
|
||||
sb.Append(' ');
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
byte b = bytes[offset + i];
|
||||
sb.Append(b >= 0x20 && b < 0x7F ? (char)b : '.');
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
Reference in New Issue
Block a user