273 lines
9.1 KiB
Dart
273 lines
9.1 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/common/models/plugs/serial_card_models.dart';
|
|
import 'package:pj_supermarket_cloud/common/utils/manage/tool/serial_read_card_tool.dart';
|
|
import 'package:pj_supermarket_cloud/common/utils/manage/tool/serial_tool.dart';
|
|
|
|
void main() {
|
|
group('DK25-ST 帧解码', () {
|
|
test('按长度字段合并分包', () {
|
|
final decoder = SerialCardFrameDecoder();
|
|
|
|
expect(decoder.add([0xAA, 0x05, 0x01]), isEmpty);
|
|
final frames = decoder.add([0x16, 0xAB, 0xE1, 0xC5]);
|
|
|
|
expect(frames, hasLength(1));
|
|
expect(frames.single.command, SerialReadCardTool.getUidCommand);
|
|
expect(frames.single.payload, [0x16, 0xAB, 0xE1, 0xC5]);
|
|
});
|
|
|
|
test('一次提取粘连的多个帧并忽略帧前噪声', () {
|
|
final decoder = SerialCardFrameDecoder();
|
|
final frames = decoder.add([
|
|
0x00,
|
|
0x55,
|
|
0xAA,
|
|
0x02,
|
|
0x02,
|
|
0x01,
|
|
0xAA,
|
|
0x01,
|
|
0xFE,
|
|
]);
|
|
|
|
expect(frames, hasLength(2));
|
|
expect(frames.first.command, SerialReadCardTool.getCardTypeCommand);
|
|
expect(frames.first.payload, [SerialCardType.mifare.code]);
|
|
expect(frames.last.isAck, isTrue);
|
|
|
|
final malformedAck = decoder.add([0xAA, 0x02, 0xFE, 0x00]).single;
|
|
expect(malformedAck.isAck, isFalse);
|
|
});
|
|
|
|
test('AA1204 空块响应按协议拆出块地址和 16 字节数据', () {
|
|
final decoder = SerialCardFrameDecoder();
|
|
final frame = decoder.add([
|
|
0xAA,
|
|
0x12,
|
|
0x04,
|
|
0x01,
|
|
...List<int>.filled(16, 0),
|
|
]).single;
|
|
|
|
expect(frame.command, SerialReadCardTool.mifareReadBlockCommand);
|
|
expect(frame.payload.first, 1);
|
|
expect(frame.payload.sublist(1), List<int>.filled(16, 0));
|
|
expect(
|
|
SerialReadCardTool.decodeMifareBlockData(frame.payload.sublist(1)),
|
|
isNull,
|
|
);
|
|
});
|
|
});
|
|
|
|
group('DK25-ST 命令组装', () {
|
|
test('通用、M1 密钥与块读写命令符合手册长度定义', () {
|
|
expect(SerialReadCardTool.getUidRequest(), [0xAA, 0x01, 0x01]);
|
|
expect(SerialReadCardTool.getCardTypeRequest(), [0xAA, 0x01, 0x02]);
|
|
expect(
|
|
SerialReadCardTool.saveMifareKeyRequest(
|
|
keyType: SerialMifareKeyType.keyA,
|
|
keyBytes: List.filled(6, 0xFF),
|
|
),
|
|
[0xAA, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
|
);
|
|
expect(
|
|
SerialReadCardTool.setMifareKeyTypeRequest(
|
|
SerialMifareKeyType.keyB,
|
|
),
|
|
[0xAA, 0x02, 0x0C, 0x0B],
|
|
);
|
|
expect(
|
|
SerialReadCardTool.readMifareBlockRequest(1),
|
|
[0xAA, 0x02, 0x04, 0x01],
|
|
);
|
|
expect(
|
|
SerialReadCardTool.canSafelyRepeatRequest(
|
|
SerialReadCardTool.getCardTypeRequest(),
|
|
),
|
|
isTrue,
|
|
);
|
|
expect(
|
|
SerialReadCardTool.canSafelyRepeatRequest(
|
|
SerialReadCardTool.saveMifareKeyRequest(
|
|
keyType: SerialMifareKeyType.keyA,
|
|
keyBytes: List.filled(6, 0xFF),
|
|
),
|
|
),
|
|
isFalse,
|
|
);
|
|
expect(
|
|
SerialReadCardTool.canSafelyRepeatRequest(
|
|
SerialReadCardTool.writeMifareBlockRequest(
|
|
blockAddress: 1,
|
|
blockData: List.filled(16, 0),
|
|
),
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('M1 业务卡号保留完整长度并使用 0x00 补齐', () {
|
|
final block = SerialReadCardTool.encodeMifareBlockData('12345678901234');
|
|
|
|
expect(block, hasLength(16));
|
|
expect(
|
|
SerialReadCardTool.decodeMifareBlockData(block!), '12345678901234');
|
|
expect(block.sublist(14), [0, 0]);
|
|
expect(
|
|
SerialReadCardTool.encodeMifareBlockData('12345678901234567'),
|
|
isNull,
|
|
);
|
|
expect(SerialReadCardTool.encodeMifareBlockData('中文卡号'), isNull);
|
|
expect(
|
|
SerialReadCardTool.decodeMifareBlockData(List.filled(16, 0)), isNull);
|
|
});
|
|
|
|
test('显式 UID 与自动寻卡带类型 UID 均保留完整位数', () {
|
|
const uid = [0x16, 0xAB, 0xE1, 0xC5];
|
|
|
|
expect(
|
|
SerialReadCardTool.extractUidPayload(
|
|
cardType: SerialCardType.mifare,
|
|
payload: uid,
|
|
),
|
|
uid,
|
|
);
|
|
expect(
|
|
SerialReadCardTool.extractUidPayload(
|
|
cardType: SerialCardType.mifare,
|
|
payload: [SerialCardType.mifare.code, ...uid],
|
|
),
|
|
uid,
|
|
);
|
|
expect(SerialReadCardTool.uidToCardNumber(uid), '16ABE1C5');
|
|
expect(
|
|
SerialReadCardTool.extractUidPayload(
|
|
cardType: SerialCardType.undefined,
|
|
payload: [SerialCardType.undefined.code, ...List.filled(7, 0x11)],
|
|
),
|
|
List.filled(7, 0x11),
|
|
);
|
|
});
|
|
|
|
test('忽略复用 0x01 指令码但长度不符合卡型的自动寻卡扩展数据', () {
|
|
expect(
|
|
SerialReadCardTool.extractUidPayload(
|
|
cardType: SerialCardType.iso14443ACpu,
|
|
payload: [
|
|
SerialCardType.iso14443ACpu.code,
|
|
...List<int>.filled(20, 0x31),
|
|
],
|
|
),
|
|
isNull,
|
|
);
|
|
});
|
|
|
|
test('禁止把 M1 扇区密钥块当作业务数据块', () {
|
|
expect(SerialReadCardTool.isMifareTrailerBlock(3), isTrue);
|
|
expect(SerialReadCardTool.isMifareTrailerBlock(127), isTrue);
|
|
expect(SerialReadCardTool.isMifareTrailerBlock(143), isTrue);
|
|
expect(SerialReadCardTool.isMifareTrailerBlock(159), isTrue);
|
|
expect(SerialReadCardTool.isMifareTrailerBlock(1), isFalse);
|
|
expect(SerialReadCardTool.isMifareTrailerBlock(142), isFalse);
|
|
expect(SerialReadCardTool.isMifareBusinessDataBlock(0), isFalse);
|
|
expect(SerialReadCardTool.isMifareBusinessDataBlock(1), isTrue);
|
|
expect(SerialReadCardTool.isMifareBusinessDataBlock(3), isFalse);
|
|
});
|
|
|
|
test('M1 扫描地址只包含 S50/S70 的安全数据块', () {
|
|
final addresses = SerialReadCardTool.mifareSafeDataBlockAddresses();
|
|
|
|
expect(addresses, hasLength(215));
|
|
expect(addresses.where((address) => address <= 63), hasLength(47));
|
|
expect(addresses.first, 1);
|
|
expect(addresses.last, 254);
|
|
expect(addresses, isNot(contains(0)));
|
|
expect(addresses, isNot(contains(3)));
|
|
expect(addresses, isNot(contains(127)));
|
|
expect(addresses, isNot(contains(143)));
|
|
expect(addresses, isNot(contains(255)));
|
|
});
|
|
|
|
test('扫描忽略全零和全 FF 块并识别不限 12 位的卡号候选文本', () {
|
|
expect(
|
|
SerialReadCardTool.isBlankMifareBlockData(List.filled(16, 0x00)),
|
|
isTrue,
|
|
);
|
|
expect(
|
|
SerialReadCardTool.isBlankMifareBlockData(List.filled(16, 0xFF)),
|
|
isTrue,
|
|
);
|
|
final block = SerialReadCardTool.encodeMifareBlockData('123456789012')!;
|
|
expect(SerialReadCardTool.isBlankMifareBlockData(block), isFalse);
|
|
expect(
|
|
SerialMifareBlockScanItem(
|
|
blockAddress: 4,
|
|
hex: SerialTool.bytesToString(block),
|
|
text: SerialReadCardTool.decodeMifareBlockData(block),
|
|
).isCardNumberCandidate,
|
|
isTrue,
|
|
);
|
|
expect(
|
|
const SerialMifareBlockScanItem(
|
|
blockAddress: 5,
|
|
hex: '4131',
|
|
text: 'A1',
|
|
).isCardNumberCandidate,
|
|
isTrue,
|
|
);
|
|
});
|
|
});
|
|
|
|
group('读写卡配置', () {
|
|
test('旧设置缺失时使用兼容默认值并支持 JSON 往返', () {
|
|
const config = SerialCardConfig(
|
|
readMode: SerialCardReadMode.uidOnly,
|
|
mifareBlockAddress: 4,
|
|
mifareKeyType: SerialMifareKeyType.keyB,
|
|
keyAHex: 'A0A1A2A3A4A5',
|
|
keyBHex: 'B0B1B2B3B4B5',
|
|
showMifareScanButton: true,
|
|
);
|
|
|
|
final restored = SerialCardConfig.fromJson(config.toJson());
|
|
expect(restored.readMode, SerialCardReadMode.uidOnly);
|
|
expect(restored.mifareBlockAddress, 4);
|
|
expect(restored.mifareKeyType, SerialMifareKeyType.keyB);
|
|
expect(restored.selectedKeyHex, 'B0B1B2B3B4B5');
|
|
expect(restored.showMifareScanButton, isTrue);
|
|
|
|
final defaults = SerialCardConfig.fromJson(const {});
|
|
expect(defaults.readMode, SerialCardReadMode.smart);
|
|
expect(defaults.mifareBlockAddress, 1);
|
|
expect(defaults.keyAHex, SerialCardConfig.defaultKeyHex);
|
|
expect(defaults.showMifareScanButton, isFalse);
|
|
|
|
final compatible = SerialCardConfig.fromJson(const {
|
|
'readMode': 'future_mode',
|
|
'mifareKeyType': 'future_key',
|
|
'keyAHex': 'invalid',
|
|
});
|
|
expect(compatible.readMode, SerialCardReadMode.smart);
|
|
expect(compatible.mifareKeyType, SerialMifareKeyType.keyA);
|
|
expect(compatible.keyAHex, SerialCardConfig.defaultKeyHex);
|
|
});
|
|
|
|
test('不同卡型保留手册定义的 UID 长度', () {
|
|
expect(SerialCardType.mifare.uidLength, 4);
|
|
expect(SerialCardType.ultralight.uidLength, 7);
|
|
expect(SerialCardType.iso14443B.uidLength, 8);
|
|
expect(SerialCardType.iso14443ACpu.uidLength, 4);
|
|
expect(SerialCardType.iso15693.uidLength, 8);
|
|
expect(
|
|
SerialReadCardTool.isValidUidLength(SerialCardType.undefined, 7),
|
|
isTrue,
|
|
);
|
|
expect(
|
|
SerialReadCardTool.isValidUidLength(SerialCardType.undefined, 3),
|
|
isFalse,
|
|
);
|
|
});
|
|
});
|
|
}
|