- pricegod-urovo-daemon: C# :7790 HTTP service, drives the Urovo D812R+ via the GTSPL SDK; prints a label AND encodes the UHF chip in one pass. Drop-in for the PriceGod extension's daemon.js (same :7790, CORS *). - chafcheck: Chafon H-102 UHFPrimeReader P/Invoke probe. - ASSESSMENT.md: everything done + learned this session -- the ribbon-latch fix, PET-labels-are-not-direct-thermal finding, unreliable-status-byte quirk, SKU->EPC scheme, the Chafon HID-mode dead end, and how to port the print+encode recipe to the Mac (GTSPL Java SDK / raw TSPL). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
153 lines
7.2 KiB
C#
153 lines
7.2 KiB
C#
// chafcheck — probe the Chafon UHF reader via the native UHFPrimeReader.dll C API (P/Invoke).
|
|
// Validates connection + read + write before wiring a Chafon /write-tag into the daemon.
|
|
// Needs UHFPrimeReader.dll + hidapi.dll (x64) beside the exe.
|
|
//
|
|
// chafcheck info -> list USB-HID readers (no tag needed)
|
|
// chafcheck read -> read the EPC of the tag in the field
|
|
// chafcheck write <24-hex-EPC> -> write EPC to the tag in the field, then read back
|
|
//
|
|
// Build (x64, .NET Framework):
|
|
// csc /platform:x64 /out:chafcheck.exe chafcheck.cs
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
class ChafCheck
|
|
{
|
|
const string DLL = "UHFPrimeReader.dll";
|
|
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int CFHid_GetUsbCount();
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int CFHid_GetUsbInfo(ushort index, byte[] pucDeviceInfo);
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int OpenHidConnection(ref long hComm, ushort index);
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int OpenDevice(ref long hComm, string pcCom, int iBaudRate);
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int CloseDevice(long hComm);
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int GetTagUii(long hComm, ref TagInfo tag, ushort timeout);
|
|
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int WriteTag(long hComm, byte option, byte[] accPwd, byte memBank, ushort wordPtr, byte wordCount, byte[] writeData);
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct TagInfo
|
|
{
|
|
public ushort NO;
|
|
public short rssi;
|
|
public byte antenna;
|
|
public byte channel;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] crc;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] pc;
|
|
public byte codeLen;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] public byte[] code;
|
|
}
|
|
|
|
static TagInfo NewTag() { return new TagInfo { crc = new byte[2], pc = new byte[2], code = new byte[255] }; }
|
|
|
|
static long Connect(string com)
|
|
{
|
|
long h = 0;
|
|
int cnt = CFHid_GetUsbCount();
|
|
Console.WriteLine("CFHid_GetUsbCount = " + cnt);
|
|
if (cnt > 0)
|
|
{
|
|
int r = OpenHidConnection(ref h, 0);
|
|
Console.WriteLine("OpenHidConnection(0) = 0x" + r.ToString("X") + (r == 0 ? " (ok)" : " (FAIL)"));
|
|
if (r == 0) return h;
|
|
}
|
|
// COM4/COM5 here are the Urovo's Bluetooth links — skip them. The H-102 is the CH340 port.
|
|
string[] ports = com != null ? new[] { com } : new[] { "COM3", "COM6", "COM7", "COM8" };
|
|
foreach (var c in ports)
|
|
{
|
|
long hh = 0;
|
|
int r = OpenDevice(ref hh, c, 115200);
|
|
Console.WriteLine("OpenDevice(" + c + ",115200) = 0x" + r.ToString("X"));
|
|
if (r == 0) return hh;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static string Hex(byte[] b, int n) { var sb = new StringBuilder(); for (int i = 0; i < n && i < b.Length; i++) sb.Append(b[i].ToString("X2")); return sb.ToString(); }
|
|
static byte[] FromHex(string h) { byte[] b = new byte[h.Length / 2]; for (int i = 0; i < b.Length; i++) b[i] = Convert.ToByte(h.Substring(2 * i, 2), 16); return b; }
|
|
|
|
static string CodeName(int c)
|
|
{
|
|
switch ((uint)c)
|
|
{
|
|
case 0x00000000: return "(OK)";
|
|
case 0xFFFFFF08: return "(TAG no-resp — reader ANSWERS, just no tag in field ← good baud!)";
|
|
case 0xFFFFFF12: return "(comm timeout — reader not answering at this baud)";
|
|
case 0xFFFFFF13: return "(serial write failed)";
|
|
case 0xFFFFFF14: return "(serial read failed)";
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
string action = args.Length > 0 ? args[0] : "info";
|
|
try
|
|
{
|
|
if (action == "info")
|
|
{
|
|
int cnt = CFHid_GetUsbCount();
|
|
Console.WriteLine("USB-HID Chafon reader count = " + cnt);
|
|
for (ushort i = 0; i < cnt; i++)
|
|
{
|
|
var buf = new byte[512];
|
|
CFHid_GetUsbInfo(i, buf);
|
|
Console.WriteLine(" [" + i + "] " + Encoding.ASCII.GetString(buf).Trim('\0'));
|
|
}
|
|
if (cnt == 0) Console.WriteLine("(none on USB-HID; reader may be on a COM port — try 'read')");
|
|
return;
|
|
}
|
|
|
|
string com = null;
|
|
foreach (var a in args) if (System.Text.RegularExpressions.Regex.IsMatch(a, "^(?i)COM\\d+$")) com = a.ToUpper();
|
|
|
|
if (action == "scan")
|
|
{
|
|
int[] bauds = { 115200, 57600, 38400, 19200, 9600, 230400, 460800 };
|
|
foreach (int b in bauds)
|
|
{
|
|
long hh = 0;
|
|
int r = OpenDevice(ref hh, com ?? "COM3", b);
|
|
if (r != 0) { Console.WriteLine("baud " + b + ": open failed 0x" + r.ToString("X")); continue; }
|
|
var t = NewTag();
|
|
int rr = GetTagUii(hh, ref t, 1200);
|
|
Console.WriteLine("baud " + b + ": GetTagUii = 0x" + rr.ToString("X") + " " + CodeName(rr) + (rr == 0 ? (" EPC=" + Hex(t.code, 12)) : ""));
|
|
CloseDevice(hh);
|
|
}
|
|
return;
|
|
}
|
|
|
|
long h = Connect(com);
|
|
if (h == 0) { Console.WriteLine("RESULT: could not connect to the Chafon reader"); return; }
|
|
try
|
|
{
|
|
if (action == "read")
|
|
{
|
|
var tag = NewTag();
|
|
int r = GetTagUii(h, ref tag, 3000);
|
|
Console.WriteLine("GetTagUii = 0x" + r.ToString("X"));
|
|
if (r == 0) Console.WriteLine("EPC = " + Hex(tag.code, 12) + " len=" + tag.codeLen + " rssi=" + (tag.rssi / 10) + " ant=" + tag.antenna);
|
|
else Console.WriteLine("(no tag in field / error)");
|
|
}
|
|
else if (action == "write")
|
|
{
|
|
string epc = args.Length > 1 ? args[1] : "1234567890ABCDEF12345678";
|
|
byte[] data = FromHex(epc); // 12 bytes
|
|
byte[] pwd = new byte[4]; // access pwd 00000000
|
|
int r = WriteTag(h, 0, pwd, 1, 2, (byte)(data.Length / 2), data); // memBank 1 = EPC/UII, wordPtr 2, 6 words
|
|
Console.WriteLine("WriteTag(EPC=" + epc + ") = 0x" + r.ToString("X") + (r == 0 ? " (ok)" : " (FAIL)"));
|
|
var tag = NewTag();
|
|
if (GetTagUii(h, ref tag, 2000) == 0) Console.WriteLine("read-back EPC = " + Hex(tag.code, 12));
|
|
else Console.WriteLine("read-back: no tag / error");
|
|
}
|
|
}
|
|
finally { CloseDevice(h); Console.WriteLine("closed."); }
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("EXCEPTION: " + ex.GetType().Name + ": " + ex.Message);
|
|
if (ex.InnerException != null) Console.WriteLine(" inner: " + ex.InnerException.Message);
|
|
}
|
|
}
|
|
}
|