Merge branch 'branch_percobaan'
This commit is contained in:
commit
c5f5b7d900
|
|
@ -30,10 +30,14 @@ Future<void> exportWindSpeedPdf({
|
||||||
title: 'Data Terkini',
|
title: 'Data Terkini',
|
||||||
subtitle: 'Periode: $period',
|
subtitle: 'Periode: $period',
|
||||||
items: [
|
items: [
|
||||||
SummaryItem('Kecepatan', '${currentSpeed.toStringAsFixed(1)} m/s', '💨'),
|
SummaryItem(
|
||||||
SummaryItem('Rata-rata', '${stats['avg']!.toStringAsFixed(1)} m/s', '📊'),
|
'Kecepatan', '${currentSpeed.toStringAsFixed(1)} m/s', '💨'),
|
||||||
SummaryItem('Maksimum', '${stats['max']!.toStringAsFixed(1)} m/s', '⬆️'),
|
SummaryItem(
|
||||||
SummaryItem('Minimum', '${stats['min']!.toStringAsFixed(1)} m/s', '⬇️'),
|
'Rata-rata', '${stats['avg']!.toStringAsFixed(1)} m/s', '📊'),
|
||||||
|
SummaryItem(
|
||||||
|
'Maksimum', '${stats['max']!.toStringAsFixed(1)} m/s', '⬆️'),
|
||||||
|
SummaryItem(
|
||||||
|
'Minimum', '${stats['min']!.toStringAsFixed(1)} m/s', '⬇️'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
pw.SizedBox(height: 24),
|
pw.SizedBox(height: 24),
|
||||||
|
|
@ -63,11 +67,15 @@ pw.Widget _buildPeriodTable(List<double> speeds, String period) {
|
||||||
final labels = _periodLabels(speeds.length, period);
|
final labels = _periodLabels(speeds.length, period);
|
||||||
return buildDataTable(
|
return buildDataTable(
|
||||||
headers: ['No', 'Label', 'Kecepatan (m/s)'],
|
headers: ['No', 'Label', 'Kecepatan (m/s)'],
|
||||||
rows: speeds.asMap().entries.map((e) => [
|
rows: speeds
|
||||||
'${e.key + 1}',
|
.asMap()
|
||||||
labels[e.key],
|
.entries
|
||||||
e.value.toStringAsFixed(2),
|
.map((e) => [
|
||||||
]).toList(),
|
'${e.key + 1}',
|
||||||
|
labels[e.key],
|
||||||
|
e.value.toStringAsFixed(2),
|
||||||
|
])
|
||||||
|
.toList(),
|
||||||
colWidths: {
|
colWidths: {
|
||||||
0: const pw.FixedColumnWidth(30),
|
0: const pw.FixedColumnWidth(30),
|
||||||
1: const pw.FlexColumnWidth(2),
|
1: const pw.FlexColumnWidth(2),
|
||||||
|
|
@ -78,7 +86,7 @@ pw.Widget _buildPeriodTable(List<double> speeds, String period) {
|
||||||
|
|
||||||
pw.Widget _buildHistoryTable(List<Map<String, dynamic>> data) {
|
pw.Widget _buildHistoryTable(List<Map<String, dynamic>> data) {
|
||||||
return buildDataTable(
|
return buildDataTable(
|
||||||
headers: ['No', 'Waktu', 'Kecepatan (m/s)', 'Pulse'],
|
headers: ['No', 'Waktu', 'Kecepatan (m/s)'],
|
||||||
rows: data.asMap().entries.map((e) {
|
rows: data.asMap().entries.map((e) {
|
||||||
final d = e.value;
|
final d = e.value;
|
||||||
final ts = d['timestamp'] as DateTime?;
|
final ts = d['timestamp'] as DateTime?;
|
||||||
|
|
@ -86,21 +94,20 @@ pw.Widget _buildHistoryTable(List<Map<String, dynamic>> data) {
|
||||||
'${e.key + 1}',
|
'${e.key + 1}',
|
||||||
ts != null ? pdfFullFormat.format(ts) : '-',
|
ts != null ? pdfFullFormat.format(ts) : '-',
|
||||||
(d['speed'] as double? ?? 0).toStringAsFixed(2),
|
(d['speed'] as double? ?? 0).toStringAsFixed(2),
|
||||||
'${d['pulse'] ?? 0}',
|
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
colWidths: {
|
colWidths: {
|
||||||
0: const pw.FixedColumnWidth(25),
|
0: const pw.FixedColumnWidth(25),
|
||||||
1: const pw.FlexColumnWidth(2.5),
|
1: const pw.FlexColumnWidth(2.5),
|
||||||
2: const pw.FlexColumnWidth(1.5),
|
2: const pw.FlexColumnWidth(1.5),
|
||||||
3: const pw.FlexColumnWidth(1),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> _periodLabels(int count, String period) {
|
List<String> _periodLabels(int count, String period) {
|
||||||
if (period == 'Hari Ini') {
|
if (period == 'Hari Ini') {
|
||||||
return List.generate(count, (i) => 'Jam ${i.toString().padLeft(2, '0')}:00');
|
return List.generate(
|
||||||
|
count, (i) => 'Jam ${i.toString().padLeft(2, '0')}:00');
|
||||||
} else if (period == 'Minggu Ini') {
|
} else if (period == 'Minggu Ini') {
|
||||||
const days = ['Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab', 'Min'];
|
const days = ['Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab', 'Min'];
|
||||||
return List.generate(count, (i) => days[i % 7]);
|
return List.generate(count, (i) => days[i % 7]);
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,12 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
|
||||||
final updatedGraph = TimeSeriesMapper.smooth(raw);
|
final updatedGraph = TimeSeriesMapper.smooth(raw);
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
dailySpeeds: updatedGraph,
|
dailySpeeds:
|
||||||
|
event.period == "Hari Ini" ? updatedGraph : state.dailySpeeds,
|
||||||
|
weeklySpeeds:
|
||||||
|
event.period == "Minggu Ini" ? updatedGraph : state.weeklySpeeds,
|
||||||
|
monthlySpeeds:
|
||||||
|
event.period == "Bulan Ini" ? updatedGraph : state.monthlySpeeds,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ class WindSpeedScreen extends StatelessWidget {
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
'timestamp': e.timestamp,
|
'timestamp': e.timestamp,
|
||||||
'speed': e.speed,
|
'speed': e.speed,
|
||||||
'pulse': e.pulse,
|
|
||||||
})
|
})
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,19 @@ class FirebaseMonitoringRepo implements MonitoringRepository {
|
||||||
if (snapshot.exists && snapshot.value is Map) {
|
if (snapshot.exists && snapshot.value is Map) {
|
||||||
final Map<dynamic, dynamic> data = snapshot.value as Map;
|
final Map<dynamic, dynamic> data = snapshot.value as Map;
|
||||||
|
|
||||||
// Karena RTDB push() menghasilkan Map dengan ID unik,
|
final list = data.values.map((item) {
|
||||||
// kita ambil values-nya saja dan ubah jadi List objek
|
|
||||||
return data.values.map((item) {
|
|
||||||
return mapper(item as Map<dynamic, dynamic>);
|
return mapper(item as Map<dynamic, dynamic>);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
// ✅ Sort by timestamp — Firebase push tidak jamin urutan
|
||||||
|
list.sort((a, b) {
|
||||||
|
if (a is MyWindSpeed && b is MyWindSpeed) {
|
||||||
|
return a.timestamp.compareTo(b.timestamp);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,22 @@
|
||||||
class MyWindSpeed {
|
class MyWindSpeed {
|
||||||
double speed;
|
double speed;
|
||||||
int pulse;
|
|
||||||
DateTime timestamp;
|
DateTime timestamp;
|
||||||
|
|
||||||
MyWindSpeed({
|
MyWindSpeed({required this.speed, required this.timestamp});
|
||||||
required this.speed,
|
|
||||||
required this.pulse,
|
|
||||||
required this.timestamp,
|
|
||||||
});
|
|
||||||
|
|
||||||
static final empty = MyWindSpeed(
|
static final empty = MyWindSpeed(
|
||||||
speed: 0.0,
|
speed: 0.0,
|
||||||
pulse: 0,
|
|
||||||
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
factory MyWindSpeed.fromJson(Map<dynamic, dynamic> json) {
|
factory MyWindSpeed.fromJson(Map<dynamic, dynamic> json) {
|
||||||
return MyWindSpeed(
|
return MyWindSpeed(
|
||||||
speed: (json['speed'] ?? 0).toDouble(),
|
speed: (json['speed'] ?? 0).toDouble(),
|
||||||
pulse: (json['pulse'] ?? 0) as int,
|
|
||||||
timestamp: DateTime.fromMillisecondsSinceEpoch(
|
timestamp: DateTime.fromMillisecondsSinceEpoch(
|
||||||
(json['timesTamp'] ?? 0) * 1000, // kalau dari Unix detik
|
(json['timestamp'] ?? 0) * 1000, // kalau dari Unix detik
|
||||||
).toLocal(),
|
).toLocal(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue