yourovo/chafcheck.cs
Monster Robot Party 3056fd70fc Windows Urovo D812R+ print+encode daemon + full assessment
- 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>
2026-07-22 15:18:39 +10:00

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);
}
}
}