etm-powersync-app/lib/models/modbus_models.dart
Patrick Schurig 3a535e05fa feat(installateur): écran Protocoles — CRUD masters Modbus RTU
- modbus_models.dart : ModbusRtuMaster, SerialPort, enums parity/dataBits/stopBits, modbusErrorMessage()
- nymea_service : 5 RPC typées (GetSerialPorts, GetModbusRtuMasters, Add/Reconfigure/Remove), erreurs ModbusRtuError -> FR, guards sim/déco
- protocols_screen : liste + empty state, form add/edit (dropdowns contraints), suppression confirmée (avert. orphelinage), garde-fou installateur
- main.dart : route /settings/system/protocols (remplace le stub)

Validé contre nymea réel (hems .75) : champs/enums/modbusError confirmés, cas port USB absent géré.
Réf. UI_DATA_CONTRACT.md §6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 13:25:58 +02:00

169 lines
6.1 KiB
Dart

// Modèles Modbus RTU — calqués exactement sur l'introspection nymea
// (docs/reference/jsonRPC.txt, namespace ModbusRtu). Valeurs d'enum = les
// chaînes fil préfixées ; aucun champ inventé.
/// Port série exposé par l'hôte (`ModbusRtu.GetSerialPorts` → `SerialPort`).
/// L'identifiant passé en paramètre `serialPort` est [systemLocation].
class SerialPort {
final String systemLocation; // ex. /dev/ttyUSB0 — c'est l'id à envoyer
final String description;
final String manufacturer;
final String serialNumber;
const SerialPort({
required this.systemLocation,
this.description = '',
this.manufacturer = '',
this.serialNumber = '',
});
factory SerialPort.fromJson(Map<String, dynamic> j) => SerialPort(
systemLocation: (j['systemLocation'] as String?) ?? '',
description: (j['description'] as String?) ?? '',
manufacturer: (j['manufacturer'] as String?) ?? '',
serialNumber: (j['serialNumber'] as String?) ?? '',
);
String get label =>
description.isNotEmpty ? '$systemLocation · $description' : systemLocation;
}
/// Master Modbus RTU configuré (`ModbusRtu.GetModbusRtuMasters`).
class ModbusRtuMaster {
final String modbusUuid;
final String serialPort;
final int baudrate;
final String parity; // enum SerialPortParity (valeur fil)
final String dataBits; // enum SerialPortDataBits
final String stopBits; // enum SerialPortStopBits
final int numberOfRetries;
final int timeout; // ms
final bool connected;
const ModbusRtuMaster({
required this.modbusUuid,
required this.serialPort,
required this.baudrate,
required this.parity,
required this.dataBits,
required this.stopBits,
required this.numberOfRetries,
required this.timeout,
this.connected = false,
});
factory ModbusRtuMaster.fromJson(Map<String, dynamic> j) => ModbusRtuMaster(
modbusUuid: (j['modbusUuid'] as String?) ?? '',
serialPort: (j['serialPort'] as String?) ?? '',
baudrate: (j['baudrate'] as num?)?.toInt() ?? 0,
parity: (j['parity'] as String?) ?? kDefaultParity,
dataBits: (j['dataBits'] as String?) ?? kDefaultDataBits,
stopBits: (j['stopBits'] as String?) ?? kDefaultStopBits,
numberOfRetries: (j['numberOfRetries'] as num?)?.toInt() ?? 0,
timeout: (j['timeout'] as num?)?.toInt() ?? 0,
connected: j['connected'] == true,
);
/// Résumé compact « 9600 bps · 8N1 ».
String get summary =>
'$baudrate bps · ${dataBitsLabel(dataBits)}${parityShort(parity)}${stopBitsShort(stopBits)}';
}
// ── Valeurs autorisées (dropdowns contraints) ────────────────────────────────
/// Baudrates standard proposés. À l'édition, la valeur courante du master est
/// ajoutée si non standard (voir l'écran).
const List<int> kBaudrates = [9600, 19200, 38400, 57600, 115200];
/// Enum SerialPortParity — sans la sentinelle `Unknown`.
const List<String> kParityValues = [
'SerialPortParityNoParity',
'SerialPortParityEvenParity',
'SerialPortParityOddParity',
'SerialPortParitySpaceParity',
'SerialPortParityMarkParity',
];
/// Enum SerialPortDataBits — sans `Unknown`.
const List<String> kDataBitsValues = [
'SerialPortDataBitsData5',
'SerialPortDataBitsData6',
'SerialPortDataBitsData7',
'SerialPortDataBitsData8',
];
/// Enum SerialPortStopBits — sans `Unknown`.
const List<String> kStopBitsValues = [
'SerialPortStopBitsOneStop',
'SerialPortStopBitsOneAndHalfStop',
'SerialPortStopBitsTwoStop',
];
// Défauts Modbus RTU classiques : 9600 8N1, 3 retries, 1000 ms.
const String kDefaultParity = 'SerialPortParityNoParity';
const String kDefaultDataBits = 'SerialPortDataBitsData8';
const String kDefaultStopBits = 'SerialPortStopBitsOneStop';
const int kDefaultBaudrate = 9600;
const int kDefaultRetries = 3;
const int kDefaultTimeout = 1000;
// ── Labels d'affichage ───────────────────────────────────────────────────────
String parityLabel(String v) => switch (v) {
'SerialPortParityNoParity' => 'Aucune (N)',
'SerialPortParityEvenParity' => 'Paire (E)',
'SerialPortParityOddParity' => 'Impaire (O)',
'SerialPortParitySpaceParity' => 'Space',
'SerialPortParityMarkParity' => 'Mark',
_ => v,
};
String parityShort(String v) => switch (v) {
'SerialPortParityNoParity' => 'N',
'SerialPortParityEvenParity' => 'E',
'SerialPortParityOddParity' => 'O',
'SerialPortParitySpaceParity' => 'S',
'SerialPortParityMarkParity' => 'M',
_ => '?',
};
String dataBitsLabel(String v) => switch (v) {
'SerialPortDataBitsData5' => '5',
'SerialPortDataBitsData6' => '6',
'SerialPortDataBitsData7' => '7',
'SerialPortDataBitsData8' => '8',
_ => '?',
};
String stopBitsLabel(String v) => switch (v) {
'SerialPortStopBitsOneStop' => '1',
'SerialPortStopBitsOneAndHalfStop' => '1.5',
'SerialPortStopBitsTwoStop' => '2',
_ => '?',
};
String stopBitsShort(String v) => switch (v) {
'SerialPortStopBitsOneStop' => '1',
'SerialPortStopBitsOneAndHalfStop' => '1.5',
'SerialPortStopBitsTwoStop' => '2',
_ => '?',
};
/// Mappe un `ModbusRtuError` fil → message lisible (français). Chaîne vide pour
/// `NoError`.
String modbusErrorMessage(String code) => switch (code) {
'ModbusRtuErrorNoError' => '',
'ModbusRtuErrorNotAvailable' => 'Service Modbus RTU indisponible sur la box.',
'ModbusRtuErrorUuidNotFound' => 'Master introuvable (déjà supprimé ?).',
'ModbusRtuErrorHardwareNotFound' =>
'Port série introuvable (adaptateur débranché ?).',
'ModbusRtuErrorResourceBusy' =>
'Port série occupé par un autre processus.',
'ModbusRtuErrorNotSupported' => 'Opération non supportée.',
'ModbusRtuErrorInvalidTimeoutValue' =>
'Timeout invalide (minimum 10 ms).',
'ModbusRtuErrorConnectionFailed' =>
'Échec de connexion au port série.',
_ => 'Erreur Modbus : $code',
};