Public Access
Align status decoding with the manual
Decodes the model code (byte 4) to a name, adds the cooling notification values, and rewords the error-information-2 bit 0 message. That bit is a media mismatch, which in practice means a monochrome job sent to a black/red roll — the exact failure seen on the QL-820NWB here.
This commit is contained in:
+40
-15
@@ -1,23 +1,26 @@
|
|||||||
/**
|
/**
|
||||||
* Decoder for the 32-byte status frame the QL series sends back.
|
* Decoder for the 32-byte status frame the QL series sends back.
|
||||||
*
|
*
|
||||||
* This is what the Python brother_ql path could never give us: real printer
|
* Field layout and every code below is from Brother's "Raster Command
|
||||||
* state rather than a parsed traceback. Cover open, roll empty and wrong roll
|
* Reference, QL-800/810W/820NWB v1.01", section 4, "Status information
|
||||||
* width can all be reported before a label is wasted.
|
* request".
|
||||||
|
*
|
||||||
|
* Note this is only useful over USB. Manual section 5.9 shows the network
|
||||||
|
* flow: over a TCP/IP port the print data is simply sent as-is and no status
|
||||||
|
* comes back. See probeStatus in ql-transport.js.
|
||||||
*
|
*
|
||||||
* Layout (confirmed against Brother's reference and live QL-8xx captures):
|
|
||||||
* 0 print head mark (0x80)
|
* 0 print head mark (0x80)
|
||||||
* 1 size (0x20)
|
* 1 size (0x20)
|
||||||
* 2 fixed 'B' (0x42)
|
* 2 fixed 'B' (0x42)
|
||||||
* 3-4 device dependent (model code)
|
* 3 series code, fixed '4' (0x34)
|
||||||
* 5 fixed '0' (0x30)
|
* 4 model code
|
||||||
* 6-7 fixed
|
* 5-7 fixed
|
||||||
* 8 error information 1
|
* 8 error information 1
|
||||||
* 9 error information 2
|
* 9 error information 2
|
||||||
* 10 media width in mm
|
* 10 media width in mm
|
||||||
* 11 media type
|
* 11 media type
|
||||||
* 12-13 fixed
|
* 12-13 fixed
|
||||||
* 14 reserved
|
* 14 fixed 0x3F
|
||||||
* 15 mode
|
* 15 mode
|
||||||
* 16 fixed
|
* 16 fixed
|
||||||
* 17 media length in mm
|
* 17 media length in mm
|
||||||
@@ -41,13 +44,17 @@ const ERROR_BITS_1 = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const ERROR_BITS_2 = [
|
const ERROR_BITS_2 = [
|
||||||
[0x01, 'Wrong media for this job'],
|
[
|
||||||
|
0x01,
|
||||||
|
'Media mismatch — the loaded roll does not match the job. A black/red ' +
|
||||||
|
'roll rejects a monochrome job, and vice versa.',
|
||||||
|
],
|
||||||
[0x02, 'Expansion buffer full'],
|
[0x02, 'Expansion buffer full'],
|
||||||
[0x04, 'Transmission or communication error'],
|
[0x04, 'Communication error'],
|
||||||
[0x08, 'Communication buffer full'],
|
[0x08, 'Communication buffer full'],
|
||||||
[0x10, 'Cover is open'],
|
[0x10, 'Cover is open'],
|
||||||
[0x20, 'Cancelled at the printer'],
|
[0x20, 'Cancelled at the printer'],
|
||||||
[0x40, 'Media cannot be fed'],
|
[0x40, 'Media cannot be fed, or the end of the media was detected'],
|
||||||
[0x80, 'System error'],
|
[0x80, 'System error'],
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -65,6 +72,20 @@ const PHASE_TYPES = {
|
|||||||
0x01: 'printing',
|
0x01: 'printing',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Notification numbers. Cooling pauses printing but is not a failure. */
|
||||||
|
const NOTIFICATIONS = {
|
||||||
|
0x00: null,
|
||||||
|
0x03: 'Print head cooling (started)',
|
||||||
|
0x04: 'Print head cooling (finished)',
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Byte 4 of the status frame. */
|
||||||
|
const MODEL_CODES = {
|
||||||
|
0x38: 'QL-800',
|
||||||
|
0x39: 'QL-810W',
|
||||||
|
0x41: 'QL-820NWB',
|
||||||
|
};
|
||||||
|
|
||||||
const MEDIA_TYPES = {
|
const MEDIA_TYPES = {
|
||||||
0x00: 'none',
|
0x00: 'none',
|
||||||
0x0a: 'continuous',
|
0x0a: 'continuous',
|
||||||
@@ -94,7 +115,7 @@ function decodeStatus(buf) {
|
|||||||
const statusType = STATUS_TYPES[buf[18]] || `unknown_0x${buf[18].toString(16)}`;
|
const statusType = STATUS_TYPES[buf[18]] || `unknown_0x${buf[18].toString(16)}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modelCode: buf.readUInt16BE(3),
|
model: MODEL_CODES[buf[4]] || `unknown_0x${buf[4].toString(16)}`,
|
||||||
errors,
|
errors,
|
||||||
hasError: errors.length > 0 || buf[18] === 0x02,
|
hasError: errors.length > 0 || buf[18] === 0x02,
|
||||||
mediaWidthMm: buf[10],
|
mediaWidthMm: buf[10],
|
||||||
@@ -104,7 +125,8 @@ function decodeStatus(buf) {
|
|||||||
statusType,
|
statusType,
|
||||||
phaseType: PHASE_TYPES[buf[19]] || `unknown_0x${buf[19].toString(16)}`,
|
phaseType: PHASE_TYPES[buf[19]] || `unknown_0x${buf[19].toString(16)}`,
|
||||||
phaseNumber: buf.readUInt16BE(20),
|
phaseNumber: buf.readUInt16BE(20),
|
||||||
notification: buf[22],
|
notification: NOTIFICATIONS[buf[22]] ?? `unknown_0x${buf[22].toString(16)}`,
|
||||||
|
cooling: buf[22] === 0x03,
|
||||||
raw: Buffer.from(buf.subarray(0, FRAME_LENGTH)),
|
raw: Buffer.from(buf.subarray(0, FRAME_LENGTH)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -140,8 +162,10 @@ function describeProblem(status) {
|
|||||||
/**
|
/**
|
||||||
* Check the loaded roll matches what the job expects.
|
* Check the loaded roll matches what the job expects.
|
||||||
*
|
*
|
||||||
* Worth doing before sending: a two-colour job on a plain roll is refused with
|
* Worth doing before sending where status is available: a monochrome job on a
|
||||||
* "wrong roll type", which gives nobody a clue that a colour setting caused it.
|
* black/red roll is refused outright, and the printer's own message ("change
|
||||||
|
* it to Monochrome media") points at the driver rather than at the roll
|
||||||
|
* setting, which sends people the wrong way.
|
||||||
*
|
*
|
||||||
* @returns {string|null} a problem description, or null if it matches.
|
* @returns {string|null} a problem description, or null if it matches.
|
||||||
*/
|
*/
|
||||||
@@ -163,6 +187,7 @@ function checkMediaMatches(status, media) {
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
FRAME_LENGTH,
|
FRAME_LENGTH,
|
||||||
|
MODEL_CODES,
|
||||||
decodeStatus,
|
decodeStatus,
|
||||||
decodeAll,
|
decodeAll,
|
||||||
describeProblem,
|
describeProblem,
|
||||||
|
|||||||
Reference in New Issue
Block a user