This commit is contained in:
Nedifinita
2025-08-29 13:28:14 +08:00
commit 25f66000cb
148 changed files with 10964 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
import 'dart:convert';
import 'package:flutter/services.dart';
class LocoInfoUtil {
static final List<LocoInfo> _locoData = [];
static bool _initialized = false;
static Future<void> initialize() async {
if (_initialized) return;
try {
final csvData = await rootBundle.loadString('assets/loco_info.csv');
final lines = csvData.split('\n');
for (final line in lines) {
if (line.trim().isEmpty) continue;
final fields = _parseCsvLine(line);
if (fields.length >= 4) {
try {
final model = fields[0];
final start = int.parse(fields[1]);
final end = int.parse(fields[2]);
final owner = fields[3];
final alias = fields.length > 4 ? fields[4] : '';
final manufacturer = fields.length > 5 ? fields[5] : '';
_locoData.add(LocoInfo(
model: model,
start: start,
end: end,
owner: owner,
alias: alias,
manufacturer: manufacturer,
));
} catch (e) {}
}
}
_initialized = true;
} catch (e) {
_initialized = true;
}
}
static List<String> _parseCsvLine(String line) {
final fields = <String>[];
final buffer = StringBuffer();
bool inQuotes = false;
for (int i = 0; i < line.length; i++) {
final char = line[i];
if (char == '"') {
inQuotes = !inQuotes;
} else if (char == ',' && !inQuotes) {
fields.add(buffer.toString().trim());
buffer.clear();
} else {
buffer.write(char);
}
}
fields.add(buffer.toString().trim());
return fields;
}
static LocoInfo? findLocoInfo(String model, String number) {
if (!_initialized || model.isEmpty || number.isEmpty) {
return null;
}
try {
final cleanNumber = number.trim().replaceAll('-', '').replaceAll(' ', '');
final num = cleanNumber.length > 4
? int.parse(cleanNumber.substring(cleanNumber.length - 4))
: int.parse(cleanNumber);
for (final info in _locoData) {
if (info.model == model && num >= info.start && num <= info.end) {
return info;
}
}
} catch (e) {
return null;
}
return null;
}
static String? getLocoInfoDisplay(String model, String number) {
if (_locoData.isEmpty) return null;
final modelTrimmed = model.trim();
final numberTrimmed = number.trim();
if (modelTrimmed.isEmpty ||
numberTrimmed.isEmpty ||
numberTrimmed == "<NUL>") {
return null;
}
final cleanNumber = numberTrimmed.replaceAll('-', '').replaceAll(' ', '');
final numberSuffix = cleanNumber.length >= 4
? cleanNumber.substring(cleanNumber.length - 4)
: cleanNumber.padLeft(4, '0');
final numberInt = int.tryParse(numberSuffix);
if (numberInt == null) {
return null;
}
for (final info in _locoData) {
if (info.model == modelTrimmed &&
numberInt >= info.start &&
numberInt <= info.end) {
final buffer = StringBuffer();
buffer.write(info.owner);
if (info.alias.isNotEmpty) {
buffer.write(' - ${info.alias}');
}
if (info.manufacturer.isNotEmpty) {
buffer.write(' - ${info.manufacturer}');
}
return buffer.toString();
}
}
return null;
}
}
class LocoInfo {
final String model;
final int start;
final int end;
final String owner;
final String alias;
final String manufacturer;
LocoInfo({
required this.model,
required this.start,
required this.end,
required this.owner,
required this.alias,
required this.manufacturer,
});
}

View File

@@ -0,0 +1,54 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
class LocoTypeUtil {
static final LocoTypeUtil _instance = LocoTypeUtil._internal();
factory LocoTypeUtil() => _instance;
LocoTypeUtil._internal() {
_syncInitialize();
}
final Map<String, String> _locoTypeMap = {};
bool _isInitialized = false;
void _syncInitialize() {
try {
rootBundle.loadString('assets/loco_type_info.csv').then((csvData) {
final lines = const LineSplitter().convert(csvData);
for (final line in lines) {
final trimmedLine = line.trim();
if (trimmedLine.isEmpty) continue;
final parts = trimmedLine.split(',');
if (parts.length >= 2) {
final code = parts[0].trim();
final type = parts[1].trim();
_locoTypeMap[code] = type;
}
}
_isInitialized = true;
});
} catch (e) {}
}
@deprecated
Future<void> initialize() async {}
String? getLocoTypeByCode(String code) {
return _locoTypeMap[code];
}
String? getLocoTypeByLocoNumber(String locoNumber) {
if (locoNumber.length < 3) return null;
final prefix = locoNumber.substring(0, 3);
return getLocoTypeByCode(prefix);
}
Map<String, String> getAllMappings() {
return Map.from(_locoTypeMap);
}
bool get isInitialized => _isInitialized;
int get mappingCount => _locoTypeMap.length;
}

View File

@@ -0,0 +1,68 @@
import 'dart:convert';
import 'package:flutter/services.dart';
class TrainTypeUtil {
static final List<_TrainTypePattern> _patterns = [];
static bool _initialized = false;
static Future<void> initialize() async {
if (_initialized) return;
try {
final csvData =
await rootBundle.loadString('assets/train_number_info.csv');
final lines = csvData.split('\n');
for (final line in lines) {
if (line.trim().isEmpty) continue;
final firstQuoteEnd = line.indexOf('"', 1);
if (firstQuoteEnd > 0 && firstQuoteEnd < line.length - 1) {
final regex = line.substring(1, firstQuoteEnd);
final remainingPart = line.substring(firstQuoteEnd + 1).trim();
if (remainingPart.startsWith(',"') && remainingPart.endsWith('"')) {
final type = remainingPart.substring(2, remainingPart.length - 1);
try {
_patterns.add(_TrainTypePattern(RegExp(regex), type));
} catch (e) {}
}
}
}
_initialized = true;
} catch (e) {
_initialized = true;
}
}
static String? getTrainType(String lbjClass, String train) {
if (!_initialized) {
return null;
}
final lbjClassTrimmed = lbjClass.trim();
final trainTrimmed = train.trim();
if (trainTrimmed.isEmpty || trainTrimmed == "<NUL>") {
return null;
}
final actualTrain =
lbjClassTrimmed == "NA" ? trainTrimmed : lbjClassTrimmed + trainTrimmed;
for (final pattern in _patterns) {
if (pattern.regex.hasMatch(actualTrain)) {
return pattern.type;
}
}
return null;
}
}
class _TrainTypePattern {
final RegExp regex;
final String type;
_TrainTypePattern(this.regex, this.type);
}