Files
biz_xfj_cloud/lib/common/models/plugs/serial_card_models.dart
T
2026-08-06 20:55:09 +08:00

196 lines
5.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:json_annotation/json_annotation.dart';
part 'serial_card_models.g.dart';
const String _serialCardDefaultKeyHex = 'FFFFFFFFFFFF';
/// DK25-ST 可以识别的卡片类型。
enum SerialCardType {
undefined(0x00, '未定义', null),
mifare(0x01, 'Mifare(M1)', 4),
ultralight(0x02, 'Ultralight / NFC 标签', 7),
iso14443B(0x03, 'ISO14443-B', 8),
iso14443ACpu(0x04, 'ISO14443-A CPU 卡', 4),
iso15693(0x05, 'ISO15693', 8);
const SerialCardType(this.code, this.title, this.uidLength);
final int code;
final String title;
final int? uidLength;
static SerialCardType fromCode(int code) {
return values.where((type) => type.code == code).firstOrNull ?? undefined;
}
}
/// 业务卡号读取策略,旧饭卡默认保留 M1 数据块优先的行为。
@JsonEnum(valueField: 'code')
enum SerialCardReadMode {
smart('smart', '智能识别'),
uidOnly('uid_only', '仅读取 UID'),
mifareBlockOnly('mifare_block_only', '仅 M1 业务块');
const SerialCardReadMode(this.code, this.title);
final String code;
final String title;
}
/// M1 卡扇区认证使用的密钥类型。
@JsonEnum(valueField: 'code')
enum SerialMifareKeyType {
keyA('key_a', 'KEY A', 0x0A),
keyB('key_b', 'KEY B', 0x0B);
const SerialMifareKeyType(this.code, this.title, this.protocolValue);
final String code;
final String title;
final int protocolValue;
}
/// 读写卡配置保存在已有 setting.sysJson 扩展键中,不改变数据库结构。
@JsonSerializable()
class SerialCardConfig {
static const String defaultKeyHex = _serialCardDefaultKeyHex;
const SerialCardConfig({
this.readMode = SerialCardReadMode.smart,
this.mifareBlockAddress = 1,
this.mifareKeyType = SerialMifareKeyType.keyA,
this.keyAHex = _serialCardDefaultKeyHex,
this.keyBHex = _serialCardDefaultKeyHex,
this.showMifareScanButton = false,
});
@JsonKey(unknownEnumValue: SerialCardReadMode.smart)
final SerialCardReadMode readMode;
@JsonKey(fromJson: _serialCardBlockAddressFromJson)
final int mifareBlockAddress;
@JsonKey(unknownEnumValue: SerialMifareKeyType.keyA)
final SerialMifareKeyType mifareKeyType;
@JsonKey(
fromJson: _serialCardKeyFromJson,
toJson: _serialCardKeyToJson,
)
final String keyAHex;
@JsonKey(
fromJson: _serialCardKeyFromJson,
toJson: _serialCardKeyToJson,
)
final String keyBHex;
/// M1 全块扫描属于现场诊断能力,默认隐藏,避免普通业务读卡误触耗时扫描。
final bool showMifareScanButton;
String get selectedKeyHex =>
mifareKeyType == SerialMifareKeyType.keyA ? keyAHex : keyBHex;
SerialCardConfig copyWith({
SerialCardReadMode? readMode,
int? mifareBlockAddress,
SerialMifareKeyType? mifareKeyType,
String? keyAHex,
String? keyBHex,
bool? showMifareScanButton,
}) {
return SerialCardConfig(
readMode: readMode ?? this.readMode,
mifareBlockAddress: mifareBlockAddress ?? this.mifareBlockAddress,
mifareKeyType: mifareKeyType ?? this.mifareKeyType,
keyAHex: keyAHex ?? this.keyAHex,
keyBHex: keyBHex ?? this.keyBHex,
showMifareScanButton: showMifareScanButton ?? this.showMifareScanButton,
);
}
factory SerialCardConfig.fromJson(Map<String, dynamic> json) =>
_$SerialCardConfigFromJson(json);
Map<String, dynamic> toJson() => _$SerialCardConfigToJson(this);
}
/// M1 数据块扫描中发现的非空内容。
///
/// 扫描严格跳过厂商块和扇区密钥块,因此这里只包含允许读取的普通数据块。
class SerialMifareBlockScanItem {
const SerialMifareBlockScanItem({
required this.blockAddress,
required this.hex,
this.text,
});
final int blockAddress;
final String hex;
final String? text;
/// 单个 M1 数据块最多保存 16 字节文本;不再限定历史卡号必须为 12 位。
///
/// 该判断只用于界面标记候选位置,最终仍需结合实际发卡规则确认。
bool get isCardNumberCandidate {
final value = text;
return value != null && RegExp(r'^[a-zA-Z0-9]{1,16}$').hasMatch(value);
}
}
/// M1 安全数据块只读扫描进度。
class SerialMifareScanProgress {
const SerialMifareScanProgress({
required this.processedBlocks,
required this.totalBlocks,
required this.readableBlocks,
required this.failedBlocks,
required this.items,
this.currentBlockAddress,
this.completed = false,
this.detectedS50 = false,
this.stopReason,
});
const SerialMifareScanProgress.initial({required this.totalBlocks})
: processedBlocks = 0,
readableBlocks = 0,
failedBlocks = 0,
items = const [],
currentBlockAddress = null,
completed = false,
detectedS50 = false,
stopReason = null;
final int processedBlocks;
final int totalBlocks;
final int readableBlocks;
final int failedBlocks;
final List<SerialMifareBlockScanItem> items;
final int? currentBlockAddress;
final bool completed;
/// 旧版扫描容量推断标记;当前完整扫描 215 个安全块时固定为 false,仅保留结果模型兼容性。
final bool detectedS50;
/// 非正常完整遍历时的结束原因;已读取到的块结果仍然保留并展示。
final String? stopReason;
double get fraction => totalBlocks <= 0
? 0
: (processedBlocks / totalBlocks).clamp(0, 1).toDouble();
}
int _serialCardBlockAddressFromJson(Object? value) {
return value is int && value >= 0 && value <= 255 ? value : 1;
}
String _serialCardKeyFromJson(Object? value) {
final key = value?.toString().replaceAll(RegExp(r'\s+'), '').toUpperCase();
return key != null && RegExp(r'^[0-9A-F]{12}$').hasMatch(key)
? key
: SerialCardConfig.defaultKeyHex;
}
String _serialCardKeyToJson(String value) => _serialCardKeyFromJson(value);