environment_monitoring_app/lib/services/air_sampling_service.dart

208 lines
7.3 KiB
Dart

// lib/services/air_sampling_service.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:image/image.dart' as img;
import 'package:intl/intl.dart';
// Import your actual data models
import '../models/air_installation_data.dart';
import '../models/air_collection_data.dart';
// Import a local storage service (you would create this)
// import 'local_storage_service.dart';
// A placeholder for your actual API service
class AirApiService {
Future<Map<String, dynamic>> submitInstallation({
required Map<String, dynamic> installationJson,
required List<File> imageFiles,
}) async {
// In a real app, you would build an http.MultipartRequest here
// to send the JSON data and image files to your server.
print("Submitting Installation to API...");
print("Data: $installationJson");
print("Image count: ${imageFiles.length}");
// Simulate a network call
await Future.delayed(const Duration(seconds: 1));
// Simulate a successful response
return {
'status': 'S1', // 'S1' for Server Success (Installation Pending Collection)
'message': 'Installation data successfully submitted to the server.',
'refID': installationJson['refID'],
};
}
Future<Map<String, dynamic>> submitCollection({
required Map<String, dynamic> collectionJson,
required List<File> imageFiles,
}) async {
// In a real app, this would update the existing record linked by 'installationRefID'
print("Submitting Collection to API...");
print("Data: $collectionJson");
print("Image count: ${imageFiles.length}");
// Simulate a network call
await Future.delayed(const Duration(seconds: 1));
// Simulate a successful response
return {
'status': 'S3', // 'S3' for Server Success (Completed)
'message': 'Collection data successfully linked and submitted.',
};
}
}
/// A dedicated service to handle all business logic for the Air Manual Sampling feature.
class AirSamplingService {
final AirApiService _apiService = AirApiService();
// final LocalStorageService _localStorageService = LocalStorageService();
/// Picks an image from the specified source, adds a timestamp watermark,
/// and saves it to a temporary directory with a standardized name.
Future<File?> pickAndProcessImage(
ImageSource source, {
required String stationCode,
required String imageInfo, // e.g., "INSTALLATION_LEFT", "COLLECTION_CHART"
}) async {
final picker = ImagePicker();
final XFile? photo = await picker.pickImage(
source: source, imageQuality: 85, maxWidth: 1024);
if (photo == null) return null;
final bytes = await photo.readAsBytes();
img.Image? originalImage = img.decodeImage(bytes);
if (originalImage == null) return null;
// Prepare watermark text
final String watermarkTimestamp =
DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now());
final font = img.arial24;
// Add a white background for better text visibility
final textWidth = watermarkTimestamp.length * 12; // Estimate width
img.fillRect(originalImage,
x1: 5,
y1: 5,
x2: textWidth + 15,
y2: 35,
color: img.ColorRgb8(255, 255, 255));
img.drawString(originalImage, watermarkTimestamp,
font: font, x: 10, y: 10, color: img.ColorRgb8(0, 0, 0));
// Create a standardized file name
final tempDir = await getTemporaryDirectory();
final fileTimestamp = watermarkTimestamp.replaceAll(':', '-').replaceAll(' ', '_');
final newFileName =
"${stationCode}_${fileTimestamp}_${imageInfo.replaceAll(' ', '')}.jpg";
final filePath = path.join(tempDir.path, newFileName);
// Save the processed image and return the file
return File(filePath)..writeAsBytesSync(img.encodeJpg(originalImage));
}
/// Submits only the installation data.
Future<Map<String, dynamic>> submitInstallation(AirInstallationData data) async {
try {
// Prepare image files for upload
final List<File> images = [];
if (data.image1 != null) images.add(data.image1!);
if (data.image2 != null) images.add(data.image2!);
if (data.image3 != null) images.add(data.image3!);
if (data.image4 != null) images.add(data.image4!);
// In a real app, you would check connectivity here before attempting the API call
final result = await _apiService.submitInstallation(
installationJson: data.toJson(),
imageFiles: images,
);
return result;
} catch (e) {
print("API submission failed: $e. Saving installation locally.");
// --- Fallback to Local Storage ---
// TODO: Implement local DB save for installation data
data.status = 'L1'; // Mark as saved locally, pending collection
// await _localStorageService.saveAirInstallationData(data);
return {
'status': 'L1',
'message': 'Installation data saved locally.',
};
}
}
/// Submits only the collection data, linked to a previous installation.
Future<Map<String, dynamic>> submitCollection(AirCollectionData data) async {
try {
// Prepare image files for upload
final List<File> images = [];
if (data.imageSiteLeft != null) images.add(data.imageSiteLeft!);
if (data.imageSiteRight != null) images.add(data.imageSiteRight!);
// ... add all other collection images
// In a real app, you would check connectivity here
final result = await _apiService.submitCollection(
collectionJson: data.toJson(),
imageFiles: images,
);
return result;
} catch (e) {
print("API submission failed: $e. Saving collection locally.");
// --- Fallback to Local Storage ---
// TODO: Implement local DB save for collection data
data.status = 'L3'; // Mark as completed locally
// await _localStorageService.saveAirCollectionData(data);
return {
'status': 'L3',
'message': 'Collection data saved locally.',
};
}
}
/// Fetches installations that are pending collection.
Future<List<AirInstallationData>> getPendingInstallations() async {
// In a real app, this would query your local database or a server endpoint
// for records with status 'L1' (local, pending) or 'S1' (server, pending).
print("Fetching pending installations...");
await Future.delayed(const Duration(milliseconds: 500)); // Simulate network/DB delay
// Return placeholder data for demonstration
return [
AirInstallationData(
refID: 'ABC1234567',
stationID: 'SGR01',
locationName: 'Shah Alam',
samplingDate: '2025-08-10',
temp: '28.5',
status: 'L1', // Example of a locally saved installation
),
AirInstallationData(
refID: 'DEF8901234',
stationID: 'JHR02',
locationName: 'Muar',
samplingDate: '2025-08-09',
temp: '29.1',
status: 'S1', // Example of a server-saved installation
),
AirInstallationData(
refID: 'GHI5678901',
stationID: 'PRK01',
locationName: 'Ipoh',
samplingDate: '2025-08-11',
temp: '27.9',
status: 'S1',
),
];
}
void dispose() {
// Clean up any resources if necessary
}
}