Adds flight,battery models and image file managment

This commit is contained in:
2025-07-04 23:39:34 +02:00
parent 72a8d25318
commit 3925421428
13 changed files with 485 additions and 36 deletions

View File

@ -3,10 +3,13 @@ import 'dart:developer';
import 'dart:io';
import 'package:path/path.dart';
import 'package:logging/logging.dart';
import 'package:rtime/models/battery.dart';
import 'package:rtime/models/flight.dart';
import '../models/drone.dart';
import 'package:path_provider/path_provider.dart';import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DbHelper
{
@ -47,15 +50,42 @@ class DbHelper
CREATE TABLE drones(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
image TEXT
image_uuid TEXT
)
''');
await db.execute('''
CREATE TABLE batteries(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL,
volatege REAL NOT NULL,
image_uuid TEXT
)
''');
await db.execute('''
CREATE TABLE flights(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
start_timestamp INTEGER NOT NULL,
end_timestamp INTEGER NOT NULL,
drone_id INTEGER NOT NULL,
battery_id INTEGER NOT NULL,
location_lat REAL,
location_long REAL,
FOREIGN KEY(drone_id) REFERENCES drones(id),
FOREIGN KEY(battery_id) REFERENCES batteries(id)
)
''');
}
Future<int> insertDrone(Drone drone) async
// Drone related helpers
Future<Drone> insertDrone(Drone drone) async
{
final db = await database;
return await db.insert("drones", drone.toMap());
drone.id = await db.insert("drones", drone.toMap());
return drone;
}
Future<List<Drone>> getDrones() async
@ -65,10 +95,54 @@ class DbHelper
return maps.map((e) => Drone.fromMap(e)).toList();
}
Future<int> deleteItem(int drone_id) async
Future<int> deleteDrone(int droneId) async
{
// TODO: Delete image
final db = await database;
return await db.delete("drones", where: "id = ?", whereArgs: [droneId]);
}
// Battery related helpers
Future<Battery> insertBattery(Battery battery) async
{
final db = await database;
return await db.delete("drones", where: "id = ?", whereArgs: [drone_id]);
battery.id = await db.insert("batteries", battery.toMap());
return battery;
}
Future<List<Battery>> getBatteries() async
{
final db = await database;
final maps = await db.query("batteries");
return maps.map((e) => Battery.fromMap(e)).toList();
}
Future<int> deleteBattery(int batteryId) async
{
// TODO: Delete image
final db = await database;
return await db.delete("batteries", where: "id = ?", whereArgs: [batteryId]);
}
// Flight related helpers
Future<Flight> insertFlight(Flight flight) async
{
final db = await database;
flight.id = await db.insert("flights", flight.toMap());
return flight;
}
Future<List<Flight>> getFlights() async
{
final db = await database;
final maps = await db.query("flights");
return maps.map((e) => Flight.fromMap(e)).toList();
}
Future<int> deleteFlight(int flightId) async
{
final db = await database;
return await db.delete("flights", where: "id = ?", whereArgs: [flightId]);
}
Future closeDb() async

79
lib/images_manager.dart Normal file
View File

@ -0,0 +1,79 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:uuid/uuid.dart';
import 'package:image/image.dart' as img;
import 'package:uuid/v5.dart';
class ImagesManager
{
static final ImagesManager instance = ImagesManager._internal();
ImagesManager._internal();
static final Logger log = Logger("ImagesManager");
static Uri? _imagesDirectory;
Future<Uri> get imageDirectory async
{
if(_imagesDirectory != null) return _imagesDirectory!;
await _initImagesDirectory();
return _imagesDirectory!;
}
Future _initImagesDirectory() async
{
final directoryLoc = await getApplicationDocumentsDirectory();
final directoryName = "images";
final directoryPath = path.join(directoryLoc.path, directoryName);
final directoryUri = Uri.directory(directoryPath);
final directory = Directory.fromUri(directoryUri);
if(!await directory.exists())
{
log.info("Image directory does not yet extists. Creating it.");
}
directory.create(recursive: false);
log.info("Image directory set up at '$directory'");
_imagesDirectory = directoryUri;
}
Future<String?> createImage(ImageSource source) async
{
// Get image from camera or not
final XFile? ximage = await ImagePicker().pickImage(source: source);
if(ximage == null) return null;
final uuid = Uuid().v6();
final imageDir = await imageDirectory;
ximage.saveTo(path.join(imageDir.path, uuid + path.extension(ximage.name)));
return uuid;
}
Future<Image?> loadImage(String imageUuid) async
{
final imageDir = await imageDirectory;
if(!Uuid.isValidUUID(fromString: imageUuid))
{
log.warning("Tried to load an image with an invalid UUID : '$imageUuid'.");
return null;
}
final imagePath = path.join(imageDir.path, "$imageUuid.jpg");
final file = File(imagePath);
if(!await file.exists())
{
log.warning("Tried to load an image that does not extist: '$imagePath'.");
return null;
}
return Image.file(file);
}
}

View File

@ -1,10 +1,14 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:logging/logging.dart';
import 'package:rtime/db/db_helper.dart';
import 'package:rtime/images_manager.dart';
import 'package:rtime/models/drone.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:image_picker/image_picker.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
void main() {
if(Platform.isWindows || Platform.isLinux)
@ -74,7 +78,16 @@ class MyHomePage extends StatefulWidget {
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
Future _incrementCounter() async {
DbHelper.instance.insertDrone(Drone
(
name: "Image test",
imageUuid: await ImagesManager.instance.createImage(ImageSource.camera)
));
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
@ -82,7 +95,6 @@ class _MyHomePageState extends State<MyHomePage> {
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
DbHelper.instance.insertDrone(Drone(name: "skibidi drone"));
});
}
@ -94,6 +106,43 @@ class _MyHomePageState extends State<MyHomePage> {
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
var children =
<Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
FutureBuilder(
future: Future<Widget>(() async
{
final drones = await DbHelper.instance.getDrones();
if(drones.isEmpty)
{
return Icon(Icons.question_mark);
}
final image = await ImagesManager.instance.loadImage(drones.first.imageUuid!);
if(image == null)
{
return Icon(Icons.error);
}
return image;
}),
builder: (BuildContext ctx, AsyncSnapshot<Widget> img)
{
if(!img.hasData)
{
return Center(child: CircularProgressIndicator(),);
}
return img.data!;
}
),
Icon(Icons.build)];
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
@ -122,14 +171,8 @@ class _MyHomePageState extends State<MyHomePage> {
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
children: children,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
@ -139,3 +182,4 @@ class _MyHomePageState extends State<MyHomePage> {
);
}
}

33
lib/models/battery.dart Normal file
View File

@ -0,0 +1,33 @@
class Battery {
int? id;
String name;
String type;
double voltage;
String? imageUuid;
Battery({
this.id,
required this.name,
required this.type,
required this.voltage,
this.imageUuid,
});
factory Battery.fromMap(Map<String, dynamic> map) => Battery(
id: map["id"],
name: map["name"],
type: map["type"],
voltage: map["voltage"],
imageUuid: map["image_uuid"],
);
Map<String, dynamic> toMap() {
return {
"id": id,
"name": name,
"type": type,
"voltage": voltage,
"image_uuid": imageUuid,
};
}
}

View File

@ -1,24 +1,14 @@
import 'package:image/image.dart' as img;
class Drone {
int? id;
String name;
String? imageUuid;
class Drone
{
final int? id;
final String name;
//final img.Image image;
Drone({this.id, required this.name, this.imageUuid});
Drone ({this.id, required this.name}); //required this.image});
factory Drone.fromMap(Map<String, dynamic> map) =>
Drone(id: map["id"], name: map["name"], imageUuid: map["image_uuid"]);
factory Drone.fromMap(Map<String, dynamic> map) => Drone
(
id: map["id"],
name: map["name"],
//image: img.decodeJpg(map["image"])!,
);
Map<String, dynamic> toMap() =>
{
"id": id,
"name": name,
//"image": img.encodeJpg(image),
};
Map<String, dynamic> toMap() {
return {"id": id, "name": name, "image_uuid": imageUuid};
}
}

51
lib/models/flight.dart Normal file
View File

@ -0,0 +1,51 @@
class Flight {
int? id;
String name;
// Timestamp in seconds since epoch
int startTimestamp;
int endTimestamp;
// Foreign keys
int droneId;
int batteryId;
// Location
double? locationLat;
double? locationLong;
Flight({
this.id,
required this.name,
required this.startTimestamp,
required this.endTimestamp,
required this.droneId,
required this.batteryId,
this.locationLat,
this.locationLong,
});
factory Flight.fromMap(Map<String, dynamic> map) => Flight(
id: map["id"],
name: map["name"],
startTimestamp: map["start_timestamp"],
endTimestamp: map["end_timestamp"],
droneId: map["drone_id"],
batteryId: map["batteryId"],
locationLat: map["location_lat"],
locationLong: map["location_long"],
);
Map<String, dynamic> toMap() {
return {
"id": id,
"name": name,
"start_timestamp": startTimestamp,
"end_timestamp": endTimestamp,
"drone_id": droneId,
"battery_id": batteryId,
"location_lat": locationLat,
"location_long": locationLong,
};
}
}