signiture, fixes, console (broken), states
This commit is contained in:
parent
9753b68167
commit
0e08b591b7
10 changed files with 512 additions and 68 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -42,3 +42,6 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
key.properties
|
||||||
|
/key
|
|
@ -51,11 +51,26 @@ android {
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
228
lib/boxes/console.dart
Normal file
228
lib/boxes/console.dart
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:ju_rc_app/lib/boxes.dart';
|
||||||
|
import 'package:ju_rc_app/lib/serial.dart';
|
||||||
|
|
||||||
|
class SerialBox extends JuBox {
|
||||||
|
const SerialBox({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _SerialBoxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SerialBoxState extends State<SerialBox> {
|
||||||
|
USerial serial = getSerial();
|
||||||
|
final ScrollController _controller = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
serial.listen(serialListen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
serial.removeListen(serialListen);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialListen(String line, SerialCommand? _) {
|
||||||
|
setState(() {
|
||||||
|
//change serial lines
|
||||||
|
_controller.jumpTo(_controller.position.maxScrollExtent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => const SerialDetailPage()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
controller: _controller,
|
||||||
|
shrinkWrap: false,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: serial.lines
|
||||||
|
.last(20)
|
||||||
|
.map(
|
||||||
|
(e) => Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(0.0),
|
||||||
|
child: Icon(e.$1 != SerialMessageType.received
|
||||||
|
? Icons.arrow_left
|
||||||
|
: Icons.arrow_right),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
e.$3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
class SerialDetailPage extends StatefulWidget {
|
||||||
|
const SerialDetailPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _SerialDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SerialDetailPageState extends State<SerialDetailPage> {
|
||||||
|
final ScrollController _controller = ScrollController();
|
||||||
|
USerial serial = getSerial();
|
||||||
|
bool jump = true;
|
||||||
|
final List<String> _messages = [];
|
||||||
|
int messageIndex = 0;
|
||||||
|
final TextEditingController _textController = TextEditingController();
|
||||||
|
FocusNode fc = FocusNode();
|
||||||
|
FocusNode _textFocus = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
serial.listen(serialListen);
|
||||||
|
fc.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
serial.removeListen(serialListen);
|
||||||
|
_textController.dispose();
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialListen(String line, SerialCommand? _) {
|
||||||
|
if (!jump) return;
|
||||||
|
setState(() {
|
||||||
|
//change serial lines
|
||||||
|
_controller.jumpTo(_controller.position.maxScrollExtent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void submit() {
|
||||||
|
if (_textController.text.isNotEmpty) {
|
||||||
|
setState(() {
|
||||||
|
serial.sprintln(_textController.text);
|
||||||
|
_messages.add(_textController.text);
|
||||||
|
messageIndex = _messages.length;
|
||||||
|
_textController.clear();
|
||||||
|
});
|
||||||
|
_textFocus.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleKeyEvent(RawKeyEvent event) {
|
||||||
|
if (event.runtimeType.toString() == 'RawKeyDownEvent') {
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||||||
|
if (messageIndex > 0) {
|
||||||
|
setState(() {
|
||||||
|
messageIndex--;
|
||||||
|
print("up: " + _messages[messageIndex]);
|
||||||
|
_textController.text = _messages[messageIndex];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
|
||||||
|
if (messageIndex >= _messages.length - 1) {
|
||||||
|
setState(() {
|
||||||
|
print("clear");
|
||||||
|
messageIndex = _messages.length;
|
||||||
|
_textController.clear();
|
||||||
|
_textFocus.requestFocus();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
messageIndex++;
|
||||||
|
print("down: " + _messages[messageIndex]);
|
||||||
|
_textController.text = _messages[messageIndex];
|
||||||
|
_textFocus.requestFocus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
|
title: const Text(
|
||||||
|
"Serial Console",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
toolbarHeight: 40,
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
controller: _controller,
|
||||||
|
children: serial.lines.items
|
||||||
|
.map(
|
||||||
|
(e) => Row(
|
||||||
|
children: [
|
||||||
|
Text(DateFormat('HH:mm:ss.S').format(e.$2)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(0.0),
|
||||||
|
child: Icon(e.$1 != SerialMessageType.received
|
||||||
|
? Icons.arrow_left
|
||||||
|
: Icons.arrow_right),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
e.$3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
)),
|
||||||
|
RawKeyboardListener(
|
||||||
|
focusNode: fc,
|
||||||
|
autofocus: true,
|
||||||
|
onKey: _handleKeyEvent,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 8.0),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
focusNode: _textFocus,
|
||||||
|
controller: _textController,
|
||||||
|
autofocus: true,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Enter a Command',
|
||||||
|
),
|
||||||
|
onSubmitted: (_) {
|
||||||
|
submit();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.send),
|
||||||
|
onPressed: submit,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
84
lib/boxes/state.dart
Normal file
84
lib/boxes/state.dart
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ju_rc_app/lib/boxes.dart';
|
||||||
|
import 'package:ju_rc_app/lib/serial.dart';
|
||||||
|
|
||||||
|
class ControllerState extends JuBox {
|
||||||
|
const ControllerState({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _ControllerStateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ControllerStateState extends State<ControllerState> {
|
||||||
|
USerial serial = getSerial();
|
||||||
|
late Timer timer;
|
||||||
|
|
||||||
|
int connected = 0;
|
||||||
|
bool mode = false; //true is ble
|
||||||
|
int arc = 0;
|
||||||
|
double akku = 0.1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
serial.listen(serialListen);
|
||||||
|
timer = Timer.periodic(const Duration(seconds: 1), (_) async {
|
||||||
|
serial.sprintln("<ping>", system: true);
|
||||||
|
connected--;
|
||||||
|
serial.sprintln("<getSendMode>", system: true);
|
||||||
|
serial.sprintln("<getVoltage>", system: true);
|
||||||
|
serial.sprintln("<getRfArc>", system: true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
serial.removeListen(serialListen);
|
||||||
|
timer.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void serialListen(String line, SerialCommand? cmdo) {
|
||||||
|
if (cmdo == null) return;
|
||||||
|
var cmd = cmdo;
|
||||||
|
setState(() {
|
||||||
|
if (cmd.command == "pong") connected = 2;
|
||||||
|
if (cmd.command == "sendMode") {
|
||||||
|
if (cmd.arg == "RF") {
|
||||||
|
mode = false;
|
||||||
|
} else if (cmd.arg == "BLE") {
|
||||||
|
mode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cmd.command == "rfArc"){
|
||||||
|
arc = int.tryParse(cmd.arg) ?? 15;
|
||||||
|
}
|
||||||
|
if(cmd.command == "voltage"){
|
||||||
|
akku = double.tryParse(cmd.arg) ?? 0.0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => Column(
|
||||||
|
children: [
|
||||||
|
const Text("Remote controll state"),
|
||||||
|
Text(
|
||||||
|
"Serial Connection: ${connected > 0 ? "connected" : "disconnected"}"),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text("RC Mode: "),
|
||||||
|
if (mode)
|
||||||
|
const Icon(Icons.bluetooth)
|
||||||
|
else
|
||||||
|
const Icon(Icons.settings_input_antenna)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text("ARC: $arc"),
|
||||||
|
Text("Akku: ${akku}V")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,33 +3,39 @@ import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:ju_rc_app/lib/serial.dart';
|
import 'package:ju_rc_app/lib/serial.dart';
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
class ConnectionBar extends StatefulWidget {
|
class ConnectionBar extends StatefulWidget {
|
||||||
const ConnectionBar({super.key});
|
USerial serial = getSerial();
|
||||||
|
ConnectionBar({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConnectionBarState createState() => ConnectionBarState();
|
// ignore: library_private_types_in_public_api
|
||||||
|
_ConnectionBarState createState() => _ConnectionBarState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectionBarState extends State<ConnectionBar> {
|
class _ConnectionBarState extends State<ConnectionBar> {
|
||||||
late USerial serial;
|
|
||||||
UPort? connectedTo;
|
|
||||||
List<UPort> _ports = [];
|
|
||||||
StateSetter? sheetsetstate;
|
|
||||||
late Timer timer;
|
late Timer timer;
|
||||||
String _status = "";
|
String _status = "";
|
||||||
|
List<UPort> _ports = [];
|
||||||
|
StateSetter? sheetsetstate;
|
||||||
|
UPort? connectedTo;
|
||||||
|
|
||||||
ConnectionBarState() {
|
_ConnectionBarState() {
|
||||||
serial = getSerial();
|
|
||||||
serial.listen((line) {});
|
|
||||||
timer = Timer.periodic(const Duration(seconds: 1), (_) async {
|
timer = Timer.periodic(const Duration(seconds: 1), (_) async {
|
||||||
if (sheetsetstate == null) return;
|
if (sheetsetstate == null) return;
|
||||||
var ports = await serial.getPorts();
|
var ports = await widget.serial.getPorts();
|
||||||
setState(() {
|
setState(() {
|
||||||
_ports = ports;
|
_ports = ports;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
timer.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setState(VoidCallback fn) {
|
void setState(VoidCallback fn) {
|
||||||
super.setState(fn);
|
super.setState(fn);
|
||||||
|
@ -40,14 +46,15 @@ class ConnectionBarState extends State<ConnectionBar> {
|
||||||
|
|
||||||
void showModal() async {
|
void showModal() async {
|
||||||
if (connectedTo != null && connectedTo!.connected) {
|
if (connectedTo != null && connectedTo!.connected) {
|
||||||
await serial.disconnect();
|
await widget.serial.disconnect();
|
||||||
setState(() {
|
setState(() {
|
||||||
connectedTo = null;
|
connectedTo = null;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var ports = await serial.getPorts();
|
var ports = await widget.serial.getPorts();
|
||||||
setState(() {
|
setState(() {
|
||||||
_ports = ports;
|
_ports = ports;
|
||||||
|
_status = "";
|
||||||
});
|
});
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
@ -56,6 +63,7 @@ class ConnectionBarState extends State<ConnectionBar> {
|
||||||
builder: (BuildContext context, StateSetter setStateM) {
|
builder: (BuildContext context, StateSetter setStateM) {
|
||||||
sheetsetstate = setStateM;
|
sheetsetstate = setStateM;
|
||||||
return BottomSheet(
|
return BottomSheet(
|
||||||
|
enableDrag: false,
|
||||||
builder: (context) => Column(children: [
|
builder: (context) => Column(children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
|
@ -80,13 +88,13 @@ class ConnectionBarState extends State<ConnectionBar> {
|
||||||
Text(port.connected ? "Disconnect" : "Connect"),
|
Text(port.connected ? "Disconnect" : "Connect"),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (port.connected) {
|
if (port.connected) {
|
||||||
await serial.disconnect();
|
await widget.serial.disconnect();
|
||||||
setState(() {
|
setState(() {
|
||||||
connectedTo = null;
|
connectedTo = null;
|
||||||
_status = "Successfully disconnected!";
|
_status = "Successfully disconnected!";
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (await serial.connect(port)) {
|
if (await widget.serial.connect(port)) {
|
||||||
setState(() {
|
setState(() {
|
||||||
connectedTo = port;
|
connectedTo = port;
|
||||||
_status = "Successfully connected!";
|
_status = "Successfully connected!";
|
||||||
|
@ -97,7 +105,7 @@ class ConnectionBarState extends State<ConnectionBar> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ports = await serial.getPorts();
|
var ports = await widget.serial.getPorts();
|
||||||
setState(() {
|
setState(() {
|
||||||
_ports = ports;
|
_ports = ports;
|
||||||
});
|
});
|
||||||
|
|
19
lib/lib/boxes.dart
Normal file
19
lib/lib/boxes.dart
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SerialCommand {
|
||||||
|
String command;
|
||||||
|
String arg;
|
||||||
|
SerialCommand(this.command, this.arg);
|
||||||
|
|
||||||
|
static SerialCommand? parse(String line) {
|
||||||
|
if (!line.startsWith("<")) return null;
|
||||||
|
int cend = line.indexOf(">");
|
||||||
|
if (cend < 0) return null;
|
||||||
|
return SerialCommand(
|
||||||
|
line.substring(1, cend), line.substring(cend + 1).replaceAll("\r", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class JuBox extends StatefulWidget {
|
||||||
|
const JuBox({super.key});
|
||||||
|
}
|
|
@ -1,11 +1,33 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter_libserialport/flutter_libserialport.dart';
|
import 'package:flutter_libserialport/flutter_libserialport.dart';
|
||||||
|
import 'package:ju_rc_app/lib/boxes.dart';
|
||||||
import 'package:usb_serial/transaction.dart';
|
import 'package:usb_serial/transaction.dart';
|
||||||
import 'package:usb_serial/usb_serial.dart';
|
import 'package:usb_serial/usb_serial.dart';
|
||||||
|
|
||||||
|
enum SerialMessageType { systemSend, userSend, received }
|
||||||
|
|
||||||
|
class LimitedList<T> {
|
||||||
|
final int maxLength;
|
||||||
|
final List<T> _items = [];
|
||||||
|
|
||||||
|
LimitedList(this.maxLength);
|
||||||
|
|
||||||
|
void add(T item) {
|
||||||
|
if (_items.length >= maxLength) {
|
||||||
|
_items.removeAt(0);
|
||||||
|
}
|
||||||
|
_items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T> last(int len) => _items.sublist(max(0, _items.length - len));
|
||||||
|
|
||||||
|
List<T> get items => _items;
|
||||||
|
}
|
||||||
|
|
||||||
abstract class UPort {
|
abstract class UPort {
|
||||||
String productName;
|
String productName;
|
||||||
String manufacturerName;
|
String manufacturerName;
|
||||||
|
@ -17,33 +39,48 @@ abstract class USerial {
|
||||||
Future<List<UPort>> getPorts();
|
Future<List<UPort>> getPorts();
|
||||||
Future<bool> connect(UPort port);
|
Future<bool> connect(UPort port);
|
||||||
Future<void> disconnect();
|
Future<void> disconnect();
|
||||||
Future<bool> sprint(String data);
|
Future<bool> _sprint(String data);
|
||||||
Future<bool> sprintln(String data) {
|
Future<bool> sprintln(String data, {bool system = false}) {
|
||||||
return sprint("$data\n");
|
lines.add((
|
||||||
|
system ? SerialMessageType.systemSend : SerialMessageType.userSend,
|
||||||
|
DateTime.now(),
|
||||||
|
data
|
||||||
|
));
|
||||||
|
return _sprint("$data\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> lines = [];
|
LimitedList<(SerialMessageType, DateTime, String)> lines =
|
||||||
Function(String)? _callback;
|
LimitedList(524288);
|
||||||
|
final List<Function(String, SerialCommand?)> _callback = [];
|
||||||
void _receive(String line) {
|
void _receive(String line) {
|
||||||
lines.add(line);
|
lines.add((SerialMessageType.received, DateTime.now(), line));
|
||||||
if (_callback != null) {
|
var parsed = SerialCommand.parse(line);
|
||||||
_callback!(line);
|
for (var f in _callback) {
|
||||||
|
f(line, parsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void listen(Function(String) callback) {
|
void listen(Function(String, SerialCommand?) callback) {
|
||||||
_callback = callback;
|
_callback.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeListen(Function(String, SerialCommand?) callback) {
|
||||||
|
_callback.remove(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
USerial? _serial;
|
||||||
|
|
||||||
USerial getSerial() {
|
USerial getSerial() {
|
||||||
|
if (_serial != null) return _serial as USerial;
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
return USerialAndroid();
|
_serial = USerialAndroid();
|
||||||
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
||||||
return USerialPC();
|
_serial = USerialPC();
|
||||||
} else {
|
} else {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
return _serial as USerial;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UPortAndroid extends UPort {
|
class UPortAndroid extends UPort {
|
||||||
|
@ -110,7 +147,7 @@ class USerialAndroid extends USerial {
|
||||||
115200, UsbPort.DATABITS_8, UsbPort.STOPBITS_1, UsbPort.PARITY_NONE);
|
115200, UsbPort.DATABITS_8, UsbPort.STOPBITS_1, UsbPort.PARITY_NONE);
|
||||||
|
|
||||||
_transaction = Transaction.stringTerminated(
|
_transaction = Transaction.stringTerminated(
|
||||||
_port!.inputStream as Stream<Uint8List>, Uint8List.fromList([13, 10]));
|
_port!.inputStream as Stream<Uint8List>, Uint8List.fromList([10]));
|
||||||
|
|
||||||
_subscription = _transaction!.stream.listen((String line) {
|
_subscription = _transaction!.stream.listen((String line) {
|
||||||
_receive(line);
|
_receive(line);
|
||||||
|
@ -140,7 +177,7 @@ class USerialAndroid extends USerial {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> sprint(String data) async {
|
Future<bool> _sprint(String data) async {
|
||||||
if (_port == null) return false;
|
if (_port == null) return false;
|
||||||
await _port!.write(Uint8List.fromList(data.codeUnits));
|
await _port!.write(Uint8List.fromList(data.codeUnits));
|
||||||
return true;
|
return true;
|
||||||
|
@ -205,7 +242,7 @@ class USerialPC extends USerial {
|
||||||
_reader = SerialPortReader(_port!);
|
_reader = SerialPortReader(_port!);
|
||||||
|
|
||||||
_transaction = Transaction.stringTerminated(
|
_transaction = Transaction.stringTerminated(
|
||||||
_reader!.stream, Uint8List.fromList([13, 10]));
|
_reader!.stream, Uint8List.fromList([10]));
|
||||||
|
|
||||||
_subscription = _transaction!.stream.listen((line) {
|
_subscription = _transaction!.stream.listen((line) {
|
||||||
_receive(line);
|
_receive(line);
|
||||||
|
@ -245,7 +282,7 @@ class USerialPC extends USerial {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> sprint(String data) async {
|
Future<bool> _sprint(String data) async {
|
||||||
try {
|
try {
|
||||||
if (_port == null) return false;
|
if (_port == null) return false;
|
||||||
_port!.write(Uint8List.fromList(data.codeUnits));
|
_port!.write(Uint8List.fromList(data.codeUnits));
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'dart:io';
|
||||||
import 'package:ju_rc_app/connector.dart';
|
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ju_rc_app/boxes/console.dart';
|
||||||
|
import 'package:ju_rc_app/boxes/state.dart';
|
||||||
|
import 'package:ju_rc_app/connector.dart';
|
||||||
|
import 'package:ju_rc_app/lib/boxes.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
|
@ -14,8 +18,15 @@ class MyApp extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'JuRcApp',
|
title: 'JuRcApp',
|
||||||
|
themeMode: ThemeMode.system,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
//colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||||
|
brightness: Brightness.light,
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
darkTheme: ThemeData(
|
||||||
|
//colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||||
|
brightness: Brightness.dark,
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: const MyHomePage(title: 'App for juRc'),
|
home: const MyHomePage(title: 'App for juRc'),
|
||||||
|
@ -32,37 +43,59 @@ class MyHomePage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
ConnectionBar conBar = const ConnectionBar();
|
ConnectionBar conBar = ConnectionBar();
|
||||||
|
|
||||||
|
static List<JuBox> boxes = [const SerialBox(), const ControllerState()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
setState(() {
|
setState(() {});
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
title: Text(widget.title),
|
title: Text(
|
||||||
|
widget.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
body: Center(
|
),
|
||||||
child: Column(
|
toolbarHeight: 40,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
),
|
||||||
children: <Widget>[
|
body: CustomScrollView(
|
||||||
conBar
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: conBar,
|
||||||
|
),
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
sliver: SliverGrid.extent(
|
||||||
|
maxCrossAxisExtent:
|
||||||
|
Platform.isAndroid || Platform.isIOS ? 300.0 : 600.0,
|
||||||
|
crossAxisSpacing: 10.0,
|
||||||
|
mainAxisSpacing: 10.0,
|
||||||
|
children: boxes
|
||||||
|
.map((b) => Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
width: 0,
|
||||||
|
color: const Color.fromARGB(0, 0, 0, 0)),
|
||||||
|
borderRadius: BorderRadius.circular(20.0),
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondaryContainer,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(0.3 * 2 * 20),
|
||||||
|
child: b,
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: () {},
|
|
||||||
tooltip: 'Reload',
|
|
||||||
child: const Icon(Icons.replay),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
pubspec.lock
42
pubspec.lock
|
@ -69,10 +69,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.2"
|
version: "1.18.0"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -192,6 +192,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.17"
|
version: "4.0.17"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.18.1"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -260,10 +268,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
|
sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "6.0.1"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -321,18 +329,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -353,10 +361,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -381,14 +389,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.4-beta"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xml
|
name: xml
|
||||||
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
|
sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.4.2"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -398,5 +414,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.0-163.1.beta <4.0.0"
|
dart: ">=3.1.0-185.0.dev <4.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
|
|
@ -38,6 +38,7 @@ dependencies:
|
||||||
flutter_libserialport: ^0.3.0
|
flutter_libserialport: ^0.3.0
|
||||||
usb_serial: ^0.5.1
|
usb_serial: ^0.5.1
|
||||||
rive: ^0.11.16
|
rive: ^0.11.16
|
||||||
|
intl: ^0.18.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue