oke
This commit is contained in:
parent
4fd56dc5f9
commit
74a1609c21
|
|
@ -19,27 +19,21 @@ class DeviceIdChanged extends DeviceSetupEvent {
|
||||||
DeviceIdChanged(this.deviceId);
|
DeviceIdChanged(this.deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class KFaktorChanged extends DeviceSetupEvent {
|
|
||||||
final double value;
|
|
||||||
KFaktorChanged(this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RadiusChanged extends DeviceSetupEvent {
|
|
||||||
final double value;
|
|
||||||
RadiusChanged(this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
class IntervalRealtimeChanged extends DeviceSetupEvent {
|
class IntervalRealtimeChanged extends DeviceSetupEvent {
|
||||||
final int ms;
|
final int ms;
|
||||||
IntervalRealtimeChanged(this.ms);
|
IntervalRealtimeChanged(this.ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IntervalAverageChanged extends DeviceSetupEvent {
|
||||||
|
final int ms;
|
||||||
|
IntervalAverageChanged(this.ms);
|
||||||
|
}
|
||||||
|
|
||||||
class IntervalHistoryChanged extends DeviceSetupEvent {
|
class IntervalHistoryChanged extends DeviceSetupEvent {
|
||||||
final int ms;
|
final int ms;
|
||||||
IntervalHistoryChanged(this.ms);
|
IntervalHistoryChanged(this.ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// User ubah jumlah magnet (1 atau 3)
|
|
||||||
class MagnetCountChanged extends DeviceSetupEvent {
|
class MagnetCountChanged extends DeviceSetupEvent {
|
||||||
final int count;
|
final int count;
|
||||||
MagnetCountChanged(this.count);
|
MagnetCountChanged(this.count);
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,8 @@ class DeviceSetupState {
|
||||||
final String espIp;
|
final String espIp;
|
||||||
|
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
final double kFaktor;
|
|
||||||
final double radiusM;
|
|
||||||
final int intervalRealtimeMs;
|
final int intervalRealtimeMs;
|
||||||
|
final int intervalAverageMs;
|
||||||
final int intervalHistoryMs;
|
final int intervalHistoryMs;
|
||||||
final int magnetCount;
|
final int magnetCount;
|
||||||
final List<Map<String, dynamic>> logs;
|
final List<Map<String, dynamic>> logs;
|
||||||
|
|
@ -38,9 +37,8 @@ class DeviceSetupState {
|
||||||
this.successMessage,
|
this.successMessage,
|
||||||
this.espIp = '192.168.4.1',
|
this.espIp = '192.168.4.1',
|
||||||
this.deviceId = 'esp_lapangan',
|
this.deviceId = 'esp_lapangan',
|
||||||
this.kFaktor = 50.0,
|
|
||||||
this.radiusM = 0.08,
|
|
||||||
this.intervalRealtimeMs = 1000,
|
this.intervalRealtimeMs = 1000,
|
||||||
|
this.intervalAverageMs = 60000,
|
||||||
this.intervalHistoryMs = 3600000,
|
this.intervalHistoryMs = 3600000,
|
||||||
this.magnetCount = 1, // ← default 1 magnet
|
this.magnetCount = 1, // ← default 1 magnet
|
||||||
this.logs = const [],
|
this.logs = const [],
|
||||||
|
|
@ -58,9 +56,8 @@ class DeviceSetupState {
|
||||||
String? successMessage,
|
String? successMessage,
|
||||||
String? espIp,
|
String? espIp,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
double? kFaktor,
|
|
||||||
double? radiusM,
|
|
||||||
int? intervalRealtimeMs,
|
int? intervalRealtimeMs,
|
||||||
|
int? intervalAverageMs,
|
||||||
int? intervalHistoryMs,
|
int? intervalHistoryMs,
|
||||||
int? magnetCount,
|
int? magnetCount,
|
||||||
List<Map<String, dynamic>>? logs,
|
List<Map<String, dynamic>>? logs,
|
||||||
|
|
@ -74,9 +71,8 @@ class DeviceSetupState {
|
||||||
successMessage: successMessage,
|
successMessage: successMessage,
|
||||||
espIp: espIp ?? this.espIp,
|
espIp: espIp ?? this.espIp,
|
||||||
deviceId: deviceId ?? this.deviceId,
|
deviceId: deviceId ?? this.deviceId,
|
||||||
kFaktor: kFaktor ?? this.kFaktor,
|
|
||||||
radiusM: radiusM ?? this.radiusM,
|
|
||||||
intervalRealtimeMs: intervalRealtimeMs ?? this.intervalRealtimeMs,
|
intervalRealtimeMs: intervalRealtimeMs ?? this.intervalRealtimeMs,
|
||||||
|
intervalAverageMs: intervalAverageMs ?? this.intervalAverageMs,
|
||||||
intervalHistoryMs: intervalHistoryMs ?? this.intervalHistoryMs,
|
intervalHistoryMs: intervalHistoryMs ?? this.intervalHistoryMs,
|
||||||
magnetCount: magnetCount ?? this.magnetCount,
|
magnetCount: magnetCount ?? this.magnetCount,
|
||||||
logs: logs ?? this.logs,
|
logs: logs ?? this.logs,
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ class DeviceSetupBloc extends Bloc<DeviceSetupEvent, DeviceSetupState> {
|
||||||
// Settings
|
// Settings
|
||||||
on<DeviceSettingsStarted>(_onSettingsStarted);
|
on<DeviceSettingsStarted>(_onSettingsStarted);
|
||||||
on<DeviceIdChanged>(_onDeviceIdChanged);
|
on<DeviceIdChanged>(_onDeviceIdChanged);
|
||||||
on<KFaktorChanged>((e, emit) => emit(state.copyWith(kFaktor: e.value)));
|
|
||||||
on<RadiusChanged>((e, emit) => emit(state.copyWith(radiusM: e.value)));
|
|
||||||
on<IntervalRealtimeChanged>(
|
on<IntervalRealtimeChanged>(
|
||||||
(e, emit) => emit(state.copyWith(intervalRealtimeMs: e.ms)));
|
(e, emit) => emit(state.copyWith(intervalRealtimeMs: e.ms)));
|
||||||
|
on<IntervalAverageChanged>(
|
||||||
|
(e, emit) => emit(state.copyWith(intervalAverageMs: e.ms)));
|
||||||
on<IntervalHistoryChanged>(
|
on<IntervalHistoryChanged>(
|
||||||
(e, emit) => emit(state.copyWith(intervalHistoryMs: e.ms)));
|
(e, emit) => emit(state.copyWith(intervalHistoryMs: e.ms)));
|
||||||
on<MagnetCountChanged>(
|
on<MagnetCountChanged>(
|
||||||
|
|
@ -56,19 +56,14 @@ class DeviceSetupBloc extends Bloc<DeviceSetupEvent, DeviceSetupState> {
|
||||||
// Baca device ID tersimpan lokal
|
// Baca device ID tersimpan lokal
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final savedId = prefs.getString(_kDeviceIdKey) ?? state.deviceId;
|
final savedId = prefs.getString(_kDeviceIdKey) ?? state.deviceId;
|
||||||
|
|
||||||
// Baca settings dari Firebase
|
|
||||||
final s = await _repository.getAnemometerSettings();
|
final s = await _repository.getAnemometerSettings();
|
||||||
|
|
||||||
// Baca logs
|
|
||||||
final logs = await _repository.getDeviceLogs(savedId);
|
final logs = await _repository.getDeviceLogs(savedId);
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
status: DeviceSetupStatus.settingsLoaded,
|
status: DeviceSetupStatus.settingsLoaded,
|
||||||
deviceId: savedId,
|
deviceId: savedId,
|
||||||
kFaktor: s['k_faktor'] as double,
|
|
||||||
radiusM: s['radius_m'] as double,
|
|
||||||
intervalRealtimeMs: s['interval_realtime_ms'] as int,
|
intervalRealtimeMs: s['interval_realtime_ms'] as int,
|
||||||
|
intervalAverageMs: s['interval_average_ms'] as int,
|
||||||
intervalHistoryMs: s['interval_history_ms'] as int,
|
intervalHistoryMs: s['interval_history_ms'] as int,
|
||||||
magnetCount: (s['magnet_count'] as int?) ?? 1,
|
magnetCount: (s['magnet_count'] as int?) ?? 1,
|
||||||
logs: logs,
|
logs: logs,
|
||||||
|
|
@ -104,9 +99,8 @@ class DeviceSetupBloc extends Bloc<DeviceSetupEvent, DeviceSetupState> {
|
||||||
emit(state.copyWith(status: DeviceSetupStatus.settingsSaving));
|
emit(state.copyWith(status: DeviceSetupStatus.settingsSaving));
|
||||||
try {
|
try {
|
||||||
await _repository.updateAnemometerSettings(
|
await _repository.updateAnemometerSettings(
|
||||||
kFaktor: state.kFaktor,
|
|
||||||
radiusM: state.radiusM,
|
|
||||||
intervalRealtimeMs: state.intervalRealtimeMs,
|
intervalRealtimeMs: state.intervalRealtimeMs,
|
||||||
|
intervalAverageMs: state.intervalAverageMs,
|
||||||
intervalHistoryMs: state.intervalHistoryMs,
|
intervalHistoryMs: state.intervalHistoryMs,
|
||||||
magnetCount: state.magnetCount,
|
magnetCount: state.magnetCount,
|
||||||
);
|
);
|
||||||
|
|
@ -335,9 +329,8 @@ class DeviceSetupBloc extends Bloc<DeviceSetupEvent, DeviceSetupState> {
|
||||||
) {
|
) {
|
||||||
emit(DeviceSetupState(
|
emit(DeviceSetupState(
|
||||||
deviceId: state.deviceId,
|
deviceId: state.deviceId,
|
||||||
kFaktor: state.kFaktor,
|
|
||||||
radiusM: state.radiusM,
|
|
||||||
intervalRealtimeMs: state.intervalRealtimeMs,
|
intervalRealtimeMs: state.intervalRealtimeMs,
|
||||||
|
intervalAverageMs: state.intervalAverageMs,
|
||||||
intervalHistoryMs: state.intervalHistoryMs,
|
intervalHistoryMs: state.intervalHistoryMs,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,14 @@ const _kRealtimeOptions = <String, int>{
|
||||||
'1 jam': 3600000,
|
'1 jam': 3600000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _kAverageOptions = <String, int>{
|
||||||
|
'30 detik': 30000,
|
||||||
|
'1 menit': 60000,
|
||||||
|
'5 menit': 300000,
|
||||||
|
'10 menit': 600000,
|
||||||
|
'30 menit': 1800000,
|
||||||
|
};
|
||||||
|
|
||||||
// ── Opsi interval history ─────────────────────────────────────
|
// ── Opsi interval history ─────────────────────────────────────
|
||||||
const _kHistoryOptions = <String, int>{
|
const _kHistoryOptions = <String, int>{
|
||||||
'10 menit': 600000,
|
'10 menit': 600000,
|
||||||
|
|
@ -45,8 +53,6 @@ class _DeviceSetupScreenState extends State<DeviceSetupScreen>
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
// Settings form controllers
|
// Settings form controllers
|
||||||
final _kFaktorController = TextEditingController();
|
|
||||||
final _radiusController = TextEditingController();
|
|
||||||
String? _customDeviceId;
|
String? _customDeviceId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -60,20 +66,11 @@ class _DeviceSetupScreenState extends State<DeviceSetupScreen>
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
_ssidController.dispose();
|
_ssidController.dispose();
|
||||||
_passwordController.dispose();
|
_passwordController.dispose();
|
||||||
_kFaktorController.dispose();
|
|
||||||
_radiusController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Sync text controllers dengan state saat settings loaded ──
|
// ── Sync text controllers dengan state saat settings loaded ──
|
||||||
void _syncControllers(DeviceSetupState state) {
|
void _syncControllers(DeviceSetupState state) {}
|
||||||
if (_kFaktorController.text != state.kFaktor.toString()) {
|
|
||||||
_kFaktorController.text = state.kFaktor.toStringAsFixed(2);
|
|
||||||
}
|
|
||||||
if (_radiusController.text != state.radiusM.toString()) {
|
|
||||||
_radiusController.text = state.radiusM.toStringAsFixed(3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -144,8 +141,6 @@ class _DeviceSetupScreenState extends State<DeviceSetupScreen>
|
||||||
children: [
|
children: [
|
||||||
_SensorSettingsTab(
|
_SensorSettingsTab(
|
||||||
state: state,
|
state: state,
|
||||||
kFaktorController: _kFaktorController,
|
|
||||||
radiusController: _radiusController,
|
|
||||||
customDeviceId: _customDeviceId,
|
customDeviceId: _customDeviceId,
|
||||||
onCustomDeviceIdChanged: (v) =>
|
onCustomDeviceIdChanged: (v) =>
|
||||||
setState(() => _customDeviceId = v),
|
setState(() => _customDeviceId = v),
|
||||||
|
|
@ -215,15 +210,11 @@ class _DeviceSetupScreenState extends State<DeviceSetupScreen>
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
class _SensorSettingsTab extends StatelessWidget {
|
class _SensorSettingsTab extends StatelessWidget {
|
||||||
final DeviceSetupState state;
|
final DeviceSetupState state;
|
||||||
final TextEditingController kFaktorController;
|
|
||||||
final TextEditingController radiusController;
|
|
||||||
final String? customDeviceId;
|
final String? customDeviceId;
|
||||||
final ValueChanged<String?> onCustomDeviceIdChanged;
|
final ValueChanged<String?> onCustomDeviceIdChanged;
|
||||||
|
|
||||||
const _SensorSettingsTab({
|
const _SensorSettingsTab({
|
||||||
required this.state,
|
required this.state,
|
||||||
required this.kFaktorController,
|
|
||||||
required this.radiusController,
|
|
||||||
required this.customDeviceId,
|
required this.customDeviceId,
|
||||||
required this.onCustomDeviceIdChanged,
|
required this.onCustomDeviceIdChanged,
|
||||||
});
|
});
|
||||||
|
|
@ -259,36 +250,6 @@ class _SensorSettingsTab extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// ── Kalibrasi ────────────────────────────────────────
|
|
||||||
_sectionTitle('Kalibrasi'),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
_SettingsCard(children: [
|
|
||||||
_NumericField(
|
|
||||||
controller: kFaktorController,
|
|
||||||
label: 'K-Faktor',
|
|
||||||
hint: 'Contoh: 50.0',
|
|
||||||
suffix: '×',
|
|
||||||
helper: 'Konstanta kalibrasi vs AWS. Awal: 50.0',
|
|
||||||
onChanged: (v) {
|
|
||||||
final d = double.tryParse(v);
|
|
||||||
if (d != null && d > 0) bloc.add(KFaktorChanged(d));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Divider(height: 24),
|
|
||||||
_NumericField(
|
|
||||||
controller: radiusController,
|
|
||||||
label: 'Jari-jari Lengan',
|
|
||||||
hint: 'Contoh: 0.08',
|
|
||||||
suffix: 'm',
|
|
||||||
helper: 'Jarak pusat ke ujung cup anemometer. Default: 0.08 m',
|
|
||||||
onChanged: (v) {
|
|
||||||
final d = double.tryParse(v);
|
|
||||||
if (d != null && d > 0) bloc.add(RadiusChanged(d));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// ── Jumlah Magnet ─────────────────────────────────────
|
// ── Jumlah Magnet ─────────────────────────────────────
|
||||||
_sectionTitle('Jumlah Magnet'),
|
_sectionTitle('Jumlah Magnet'),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|
@ -320,6 +281,21 @@ class _SensorSettingsTab extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
_sectionTitle('Interval Rata-rata (Average)'),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Seberapa sering data rata-rata dikirim ke Firebase. '
|
||||||
|
'Harus lebih pendek dari interval history.',
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.grey.shade500),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_ChipSelector(
|
||||||
|
options: _kAverageOptions,
|
||||||
|
selectedMs: state.intervalAverageMs,
|
||||||
|
onSelected: (ms) => bloc.add(IntervalAverageChanged(ms)),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// ── Interval History ──────────────────────────────────
|
// ── Interval History ──────────────────────────────────
|
||||||
_sectionTitle('Interval History (Rata-rata Jangka Panjang)'),
|
_sectionTitle('Interval History (Rata-rata Jangka Panjang)'),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|
@ -969,66 +945,6 @@ class _ChipSelector extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NumericField extends StatelessWidget {
|
|
||||||
final TextEditingController controller;
|
|
||||||
final String label;
|
|
||||||
final String hint;
|
|
||||||
final String suffix;
|
|
||||||
final String helper;
|
|
||||||
final ValueChanged<String> onChanged;
|
|
||||||
|
|
||||||
const _NumericField({
|
|
||||||
required this.controller,
|
|
||||||
required this.label,
|
|
||||||
required this.hint,
|
|
||||||
required this.suffix,
|
|
||||||
required this.helper,
|
|
||||||
required this.onChanged,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return TextField(
|
|
||||||
controller: controller,
|
|
||||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
||||||
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[0-9.]'))],
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: label,
|
|
||||||
hintText: hint,
|
|
||||||
suffixText: suffix,
|
|
||||||
helperText: helper,
|
|
||||||
helperMaxLines: 2,
|
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
|
||||||
isDense: true,
|
|
||||||
),
|
|
||||||
onChanged: onChanged,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SettingsCard extends StatelessWidget {
|
|
||||||
final List<Widget> children;
|
|
||||||
const _SettingsCard({required this.children});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(14),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 6),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start, children: children),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MagnetCountSelector extends StatelessWidget {
|
class _MagnetCountSelector extends StatelessWidget {
|
||||||
final int selected;
|
final int selected;
|
||||||
final ValueChanged<int> onSelected;
|
final ValueChanged<int> onSelected;
|
||||||
|
|
|
||||||
|
|
@ -103,46 +103,49 @@ class FirebaseMonitoringRepo implements MonitoringRepository {
|
||||||
Future<Map<String, dynamic>> getAnemometerSettings() async {
|
Future<Map<String, dynamic>> getAnemometerSettings() async {
|
||||||
// Default sama dengan cfg_config.h di ESP
|
// Default sama dengan cfg_config.h di ESP
|
||||||
const defaults = <String, dynamic>{
|
const defaults = <String, dynamic>{
|
||||||
'k_faktor': 50.0,
|
|
||||||
'radius_m': 0.08,
|
|
||||||
'interval_realtime_ms': 1000,
|
'interval_realtime_ms': 1000,
|
||||||
|
'interval_average_ms': 60000,
|
||||||
'interval_history_ms': 3600000,
|
'interval_history_ms': 3600000,
|
||||||
|
'magnet_count': 3,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
final snapshot = await _db.ref('anemometer/settings').get();
|
final snapshot = await _db.ref('anemometer/settings').get();
|
||||||
if (snapshot.exists && snapshot.value is Map) {
|
if (snapshot.exists && snapshot.value is Map) {
|
||||||
final raw = snapshot.value as Map<dynamic, dynamic>;
|
final raw = snapshot.value as Map<dynamic, dynamic>;
|
||||||
return {
|
return {
|
||||||
'k_faktor': (raw['k_faktor'] ?? defaults['k_faktor']).toDouble(),
|
|
||||||
'radius_m': (raw['radius_m'] ?? defaults['radius_m']).toDouble(),
|
|
||||||
'interval_realtime_ms':
|
'interval_realtime_ms':
|
||||||
(raw['interval_realtime_ms'] ?? defaults['interval_realtime_ms'])
|
(raw['interval_realtime_ms'] ?? defaults['interval_realtime_ms'])
|
||||||
as int,
|
as int,
|
||||||
|
'interval_average_ms':
|
||||||
|
(raw['interval_average_ms'] ?? defaults['interval_average_ms'])
|
||||||
|
as int,
|
||||||
'interval_history_ms':
|
'interval_history_ms':
|
||||||
(raw['interval_history_ms'] ?? defaults['interval_history_ms'])
|
(raw['interval_history_ms'] ?? defaults['interval_history_ms'])
|
||||||
as int,
|
as int,
|
||||||
|
'magnet_count':
|
||||||
|
(raw['magnet_count'] as int? ?? defaults['magnet_count']) as int,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Tulis settings ke Firebase (dari app) ────────────────────
|
// ── Tulis settings ke Firebase (dari app) ────────────────────
|
||||||
@override
|
@override
|
||||||
Future<void> updateAnemometerSettings({
|
Future<void> updateAnemometerSettings({
|
||||||
double? kFaktor,
|
|
||||||
double? radiusM,
|
|
||||||
int? intervalRealtimeMs,
|
int? intervalRealtimeMs,
|
||||||
|
int? intervalAverageMs,
|
||||||
int? intervalHistoryMs,
|
int? intervalHistoryMs,
|
||||||
int? magnetCount,
|
int? magnetCount,
|
||||||
}) async {
|
}) async {
|
||||||
final updates = <String, dynamic>{};
|
final updates = <String, dynamic>{};
|
||||||
if (kFaktor != null) updates['k_faktor'] = kFaktor;
|
|
||||||
if (intervalRealtimeMs != null)
|
if (intervalRealtimeMs != null)
|
||||||
updates['interval_realtime_ms'] = intervalRealtimeMs;
|
updates['interval_realtime_ms'] = intervalRealtimeMs;
|
||||||
|
if (intervalAverageMs != null)
|
||||||
|
updates['interval_average_ms'] = intervalAverageMs;
|
||||||
if (intervalHistoryMs != null)
|
if (intervalHistoryMs != null)
|
||||||
updates['interval_history_ms'] = intervalHistoryMs;
|
updates['interval_history_ms'] = intervalHistoryMs;
|
||||||
|
if (magnetCount != null) updates['magnet_count'] = magnetCount;
|
||||||
if (updates.isNotEmpty) {
|
if (updates.isNotEmpty) {
|
||||||
await _db.ref('anemometer/settings').update(updates);
|
await _db.ref('anemometer/settings').update(updates);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,38 @@
|
||||||
class MyWindSpeed {
|
class MyWindSpeed {
|
||||||
final double speed; // realtime: speed_ms | history: avg_ms
|
final double windwegKm;
|
||||||
final double maxSpeed; // history only (0 jika dari realtime)
|
final double maxWindwegKm;
|
||||||
final double kFaktor;
|
|
||||||
final int sampleCount;
|
final int sampleCount;
|
||||||
final DateTime timestamp;
|
final DateTime timestamp;
|
||||||
|
|
||||||
MyWindSpeed({
|
MyWindSpeed({
|
||||||
required this.speed,
|
required this.windwegKm,
|
||||||
this.maxSpeed = 0.0,
|
this.maxWindwegKm = 0.0,
|
||||||
this.kFaktor = 1.0,
|
|
||||||
this.sampleCount = 0,
|
this.sampleCount = 0,
|
||||||
required this.timestamp,
|
required this.timestamp,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Getter alias untuk kompatibilitas dengan bloc/chart yang pakai .speed
|
||||||
|
double get speed => windwegKm;
|
||||||
|
double get maxSpeed => maxWindwegKm;
|
||||||
|
|
||||||
static final empty = MyWindSpeed(
|
static final empty = MyWindSpeed(
|
||||||
speed: 0.0,
|
windwegKm: 0.0,
|
||||||
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dipakai untuk realtime node
|
|
||||||
factory MyWindSpeed.fromJson(Map<dynamic, dynamic> json) {
|
factory MyWindSpeed.fromJson(Map<dynamic, dynamic> json) {
|
||||||
return MyWindSpeed(
|
return MyWindSpeed(
|
||||||
// support field lama 'speed' dan baru 'speed_ms'/'avg_ms'
|
// realtime → windweg_km
|
||||||
speed: ((json['speed_ms'] ?? json['avg_ms'] ?? json['speed']) ?? 0)
|
// history → avg_windweg_km
|
||||||
.toDouble(),
|
// average → windweg_km
|
||||||
maxSpeed: (json['max_ms'] ?? 0).toDouble(),
|
windwegKm:
|
||||||
kFaktor: (json['k_faktor'] ?? 1.0).toDouble(),
|
((json['windweg_km'] ??
|
||||||
|
json['avg_windweg_km'] ??
|
||||||
|
json['speed_ms'] ?? // fallback data lama
|
||||||
|
json['speed'] ??
|
||||||
|
0))
|
||||||
|
.toDouble(),
|
||||||
|
maxWindwegKm: (json['max_windweg_km'] ?? 0).toDouble(),
|
||||||
sampleCount: (json['sample_count'] ?? 0) as int,
|
sampleCount: (json['sample_count'] ?? 0) as int,
|
||||||
timestamp: DateTime.fromMillisecondsSinceEpoch(
|
timestamp: DateTime.fromMillisecondsSinceEpoch(
|
||||||
(json['timestamp'] ?? 0) * 1000,
|
(json['timestamp'] ?? 0) * 1000,
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,11 @@ abstract class MonitoringRepository {
|
||||||
T Function(Map<dynamic, dynamic> json) mapper,
|
T Function(Map<dynamic, dynamic> json) mapper,
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── Anemometer Settings ──────────────────────────────────────
|
|
||||||
/// Baca semua settings dari /anemometer/settings/
|
|
||||||
/// Return map dengan keys:
|
|
||||||
/// k_faktor (double), radius_m (double),
|
|
||||||
/// interval_realtime_ms (int), interval_history_ms (int)
|
|
||||||
Future<Map<String, dynamic>> getAnemometerSettings();
|
Future<Map<String, dynamic>> getAnemometerSettings();
|
||||||
|
|
||||||
/// Tulis settings ke /anemometer/settings/ (field yang null tidak ditulis)
|
|
||||||
Future<void> updateAnemometerSettings({
|
Future<void> updateAnemometerSettings({
|
||||||
double? kFaktor,
|
|
||||||
double? radiusM,
|
|
||||||
int? intervalRealtimeMs,
|
int? intervalRealtimeMs,
|
||||||
|
int? intervalAverageMs,
|
||||||
int? intervalHistoryMs,
|
int? intervalHistoryMs,
|
||||||
int? magnetCount,
|
int? magnetCount,
|
||||||
});
|
});
|
||||||
|
|
@ -41,15 +34,11 @@ abstract class MonitoringRepository {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> sendRemoteRestart(String deviceId);
|
Future<void> sendRemoteRestart(String deviceId);
|
||||||
|
|
||||||
Future<void> deleteDeviceLogs(String deviceId, List<String> keys);
|
Future<void> deleteDeviceLogs(String deviceId, List<String> keys);
|
||||||
|
|
||||||
Future<Map<String, T>> getSensorHistoryWithKeys<T>(
|
Future<Map<String, T>> getSensorHistoryWithKeys<T>(
|
||||||
String path,
|
String path,
|
||||||
T Function(Map<dynamic, dynamic> json) mapper,
|
T Function(Map<dynamic, dynamic> json) mapper,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> deleteHistoryByKeys(String path, List<String> keys);
|
Future<void> deleteHistoryByKeys(String path, List<String> keys);
|
||||||
|
|
||||||
Future<void> deleteAllHistory(String path);
|
Future<void> deleteAllHistory(String path);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue