const assert = require('assert'); const { Jh2028Decoder, crc8, } = require('../decoder'); const { buildBloodPressurePayload, buildFrame, buildRealtimePayload, } = require('../tcp-simulator'); describe('Jh2028Decoder', () => { it('parses realtime frame with little-endian fields and scale rules', () => { const decoder = new Jh2028Decoder({ alModelPath: './alModel.json' }); const frame = buildFrame(1, 0x01, 0x00, buildRealtimePayload()); const [result] = decoder.push(frame); assert.strictEqual(result.publish, true); assert.strictEqual(result.messageType, 'realtime'); assert.deepStrictEqual(result.metric, { AF: 36.5, F: 36.8, A: 2000, C: 500, B: 250, K: 90, L: 500, D: 320, H: -100, o: 120, J: -80, U: 2048, G: 138, Na: 140, HCO3: 25, O2Sat: 98.7, Hct: 36.5, Hb: 13.2, Tblood: 36.7, ktv: 1.5, }); }); it('parses blood pressure frame and ignores mean pressure and irregular pulse', () => { const decoder = new Jh2028Decoder({ alModelPath: './alModel.json' }); const frame = buildFrame(2, 0x01, 0x01, buildBloodPressurePayload()); const [result] = decoder.push(frame); assert.strictEqual(result.publish, true); assert.strictEqual(result.messageType, 'blood-pressure'); assert.deepStrictEqual(result.metric, { N: 120, O: 80, P: 76, }); assert.deepStrictEqual(result.ignored, { irregularPulse: 0, meanPressure: 93, }); }); it('skips blood pressure error code frames', () => { const decoder = new Jh2028Decoder({ alModelPath: './alModel.json' }); const frame = buildFrame(3, 0x01, 0x01, Buffer.from([2, 0, 0, 0, 0])); const [result] = decoder.push(frame); assert.strictEqual(result.publish, false); assert.strictEqual(result.ok, true); assert.strictEqual(result.reason, 'blood-pressure-error-code'); assert.strictEqual(result.errorCode, 2); }); it('rejects invalid CRC frames', () => { const decoder = new Jh2028Decoder({ alModelPath: './alModel.json' }); const frame = Buffer.from(buildFrame(1, 0x01, 0x00, buildRealtimePayload())); frame[frame.length - 1] ^= 0xFF; const [result] = decoder.push(frame); assert.strictEqual(result.publish, false); assert.strictEqual(result.ok, false); assert.strictEqual(result.reason, 'crc-invalid'); }); it('handles sticky packets and split packets', () => { const decoder = new Jh2028Decoder({ alModelPath: './alModel.json' }); const realtimeFrame = buildFrame(1, 0x01, 0x00, buildRealtimePayload()); const bpFrame = buildFrame(2, 0x01, 0x01, buildBloodPressurePayload()); const firstPart = realtimeFrame.slice(0, 10); const secondPart = Buffer.concat([realtimeFrame.slice(10), bpFrame]); assert.deepStrictEqual(decoder.push(firstPart), []); const results = decoder.push(secondPart); assert.strictEqual(results.length, 2); assert.strictEqual(results[0].messageType, 'realtime'); assert.strictEqual(results[1].messageType, 'blood-pressure'); }); it('calculates CRC8 from the PDF algorithm', () => { const frameWithoutCrc = Buffer.from([0x55, 0xAA, 0x07, 0x01, 0x01, 0x7F]); assert.strictEqual(crc8(frameWithoutCrc), 0x1B); }); });