269 lines
8.4 KiB
Dart
269 lines
8.4 KiB
Dart
// global_data.dart
|
|
import 'dart:convert';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
const String BASE_URL = 'http://192.168.100.9/kopikaocare_api';
|
|
|
|
List<Map<String, dynamic>> historyGlobal = [];
|
|
|
|
class HistoryManager {
|
|
static const String _keyHistory = 'sensor_history';
|
|
static const String _keyIntervalValue = 'interval_value';
|
|
static const String _keyIntervalUnit = 'interval_unit';
|
|
static const String _keyLastSync = 'last_sync_time';
|
|
static const String _keyCurrentRange = 'current_range';
|
|
|
|
static int intervalValue = 5;
|
|
static String intervalUnit = 'detik';
|
|
static DateTime? lastSyncTime;
|
|
static String currentRange = 'semua';
|
|
|
|
// Load history from LOCAL cache FIRST, then sync with server
|
|
static Future<void> loadHistory(
|
|
{String range = 'semua',
|
|
int limit = 1000,
|
|
bool forceRefresh = false}) async {
|
|
currentRange = range;
|
|
|
|
// First, load from local cache
|
|
await _loadHistoryLocal();
|
|
|
|
// Then, try to sync with server (if forceRefresh or cache is old)
|
|
if (forceRefresh || _shouldSyncFromServer()) {
|
|
await _syncFromServer(range, limit);
|
|
}
|
|
}
|
|
|
|
// Check if should sync from server (every 5 minutes)
|
|
static bool _shouldSyncFromServer() {
|
|
if (lastSyncTime == null) return true;
|
|
final difference = DateTime.now().difference(lastSyncTime!);
|
|
return difference.inMinutes >= 5; // Sync every 5 minutes
|
|
}
|
|
|
|
// Load from local SharedPreferences cache
|
|
static Future<void> _loadHistoryLocal() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
// Load interval settings
|
|
intervalValue = prefs.getInt(_keyIntervalValue) ?? 5;
|
|
intervalUnit = prefs.getString(_keyIntervalUnit) ?? 'detik';
|
|
|
|
// Load last sync time
|
|
String? lastSyncStr = prefs.getString(_keyLastSync);
|
|
if (lastSyncStr != null) {
|
|
lastSyncTime = DateTime.tryParse(lastSyncStr);
|
|
}
|
|
|
|
// Load current range
|
|
currentRange = prefs.getString(_keyCurrentRange) ?? 'semua';
|
|
|
|
// Load history from local cache
|
|
String? historyString = prefs.getString(_keyHistory);
|
|
if (historyString != null && historyString.isNotEmpty) {
|
|
try {
|
|
List<dynamic> decoded = jsonDecode(historyString);
|
|
historyGlobal = decoded.map((item) {
|
|
Map<String, dynamic> map = Map<String, dynamic>.from(item);
|
|
if (map['waktu'] is String) {
|
|
map['waktu'] = DateTime.parse(map['waktu']);
|
|
}
|
|
return map;
|
|
}).toList();
|
|
print('Local cache loaded: ${historyGlobal.length} records');
|
|
} catch (e) {
|
|
print('Error loading local cache: $e');
|
|
historyGlobal = [];
|
|
}
|
|
} else {
|
|
historyGlobal = [];
|
|
print('No local cache found');
|
|
}
|
|
}
|
|
|
|
// Sync data from server with range filter
|
|
static Future<void> _syncFromServer(String range, int limit) async {
|
|
try {
|
|
print('Syncing from server with range: $range');
|
|
|
|
final response = await http.get(
|
|
Uri.parse('$BASE_URL/get_history.php?range=$range&limit=$limit'),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final data = jsonDecode(response.body);
|
|
if (data['status'] == 'success') {
|
|
final serverData =
|
|
List<Map<String, dynamic>>.from(data['data']).map((item) {
|
|
return {
|
|
'suhu': double.parse(item['suhu'].toString()),
|
|
'kelembapan': double.parse(item['kelembapan'].toString()),
|
|
'berat': double.parse(item['berat'].toString()),
|
|
'waktu': DateTime.parse(item['waktu']),
|
|
};
|
|
}).toList();
|
|
|
|
// Replace with server data (server is source of truth for range)
|
|
historyGlobal = serverData;
|
|
await _saveHistoryLocal();
|
|
|
|
// Save current range
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString(_keyCurrentRange, range);
|
|
|
|
lastSyncTime = DateTime.now();
|
|
await _saveLastSyncTime();
|
|
|
|
print(
|
|
'Synced from server: ${historyGlobal.length} records (range: $range)');
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print('Error syncing from server: $e, using cached data');
|
|
}
|
|
}
|
|
|
|
// Save to local SharedPreferences cache (PRIVATE)
|
|
static Future<void> _saveHistoryLocal() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
List<Map<String, dynamic>> toSave = historyGlobal.map((item) {
|
|
Map<String, dynamic> copy = Map.from(item);
|
|
if (copy['waktu'] is DateTime) {
|
|
copy['waktu'] = (copy['waktu'] as DateTime).toIso8601String();
|
|
}
|
|
return copy;
|
|
}).toList();
|
|
|
|
String historyString = jsonEncode(toSave);
|
|
await prefs.setString(_keyHistory, historyString);
|
|
print('Local cache saved: ${historyGlobal.length} records');
|
|
}
|
|
|
|
// PUBLIC: Save to local SharedPreferences cache (for external use)
|
|
static Future<void> saveHistoryLocal() async {
|
|
await _saveHistoryLocal();
|
|
}
|
|
|
|
// Save last sync time
|
|
static Future<void> _saveLastSyncTime() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
if (lastSyncTime != null) {
|
|
await prefs.setString(_keyLastSync, lastSyncTime!.toIso8601String());
|
|
}
|
|
}
|
|
|
|
// Save single data to MySQL and local cache
|
|
static Future<void> addData(Map<String, dynamic> newData) async {
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse('$BASE_URL/save_history.php'),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({
|
|
'suhu': newData['suhu'],
|
|
'kelembapan': newData['kelembapan'],
|
|
'berat': newData['berat'],
|
|
}),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final result = jsonDecode(response.body);
|
|
if (result['status'] == 'success') {
|
|
// Add to local cache
|
|
Map<String, dynamic> dataWithTime = {
|
|
...newData,
|
|
'waktu': DateTime.now(),
|
|
};
|
|
historyGlobal.insert(0, dataWithTime);
|
|
|
|
// Limit to 1000 data
|
|
if (historyGlobal.length > 1000) {
|
|
historyGlobal.removeLast();
|
|
}
|
|
|
|
await _saveHistoryLocal();
|
|
print('Data saved to MySQL and local cache');
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print('Error saving to MySQL: $e');
|
|
}
|
|
}
|
|
|
|
// Delete selected data by index from display list
|
|
static Future<void> deleteSelectedData(List<int> indices) async {
|
|
if (indices.isEmpty) return;
|
|
|
|
// Get display data (reversed from historyGlobal)
|
|
List<Map<String, dynamic>> displayData = List.from(historyGlobal.reversed);
|
|
|
|
List<Map<String, dynamic>> toDelete = [];
|
|
for (var index in indices) {
|
|
if (index < displayData.length) {
|
|
toDelete.add(displayData[index]);
|
|
}
|
|
}
|
|
|
|
if (toDelete.isEmpty) return;
|
|
|
|
// Remove from historyGlobal
|
|
historyGlobal.removeWhere((item) {
|
|
return toDelete.any((deleteItem) =>
|
|
deleteItem['waktu'].toString() == item['waktu'].toString());
|
|
});
|
|
|
|
// Save to local cache
|
|
await _saveHistoryLocal();
|
|
|
|
// Also sync deletion to server (optional - you may need to implement server-side deletion)
|
|
// For now, we'll just update local and server will sync on next refresh
|
|
|
|
print('${toDelete.length} data deleted locally');
|
|
}
|
|
|
|
// Delete single data by its id or index
|
|
static Future<void> deleteSingleData(
|
|
Map<String, dynamic> dataToDelete) async {
|
|
historyGlobal.removeWhere((item) {
|
|
return item['waktu'].toString() == dataToDelete['waktu'].toString();
|
|
});
|
|
await _saveHistoryLocal();
|
|
print('Single data deleted');
|
|
}
|
|
|
|
// Clear all history from MySQL and local cache
|
|
static Future<void> clearHistory() async {
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse('$BASE_URL/clear_history.php'),
|
|
);
|
|
if (response.statusCode == 200) {
|
|
historyGlobal.clear();
|
|
await _saveHistoryLocal();
|
|
print('History cleared from MySQL and local cache');
|
|
}
|
|
} catch (e) {
|
|
print('Error clearing history: $e');
|
|
}
|
|
}
|
|
|
|
// Update interval settings
|
|
static Future<void> updateInterval(int value, String unit) async {
|
|
intervalValue = value;
|
|
intervalUnit = unit;
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setInt(_keyIntervalValue, value);
|
|
await prefs.setString(_keyIntervalUnit, unit);
|
|
|
|
print('Interval updated: every $value $unit');
|
|
}
|
|
|
|
// Force refresh from server
|
|
static Future<void> forceRefresh(
|
|
{String range = 'semua', int limit = 1000}) async {
|
|
await _syncFromServer(range, limit);
|
|
}
|
|
}
|