Skip to content

Commit

Permalink
Added option to resize to driver station's height
Browse files Browse the repository at this point in the history
  • Loading branch information
Gold872 committed Dec 3, 2023
1 parent e65dab0 commit 3991ff6
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 54 deletions.
25 changes: 7 additions & 18 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:io';
import 'dart:ui';

import 'package:elastic_dashboard/pages/dashboard_page.dart';
import 'package:elastic_dashboard/services/field_images.dart';
Expand All @@ -10,6 +9,7 @@ import 'package:elastic_dashboard/services/nt4_connection.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:screen_retriever/screen_retriever.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:window_manager/window_manager.dart';

Expand Down Expand Up @@ -53,30 +53,19 @@ void main() async {
Globals.showGrid = preferences.getBool(PrefKeys.showGrid) ?? Globals.showGrid;
Globals.cornerRadius =
preferences.getDouble(PrefKeys.cornerRadius) ?? Globals.cornerRadius;
Globals.autoResizeToDS =
preferences.getBool(PrefKeys.autoResizeToDS) ?? Globals.autoResizeToDS;

nt4Connection
.nt4Connect(preferences.getString(PrefKeys.ipAddress) ?? '127.0.0.1');

nt4Connection.dsClientConnect((ip) async {
if (Globals.ipAddressMode != IPAddressMode.driverStation) {
return;
}

if (preferences.getString(PrefKeys.ipAddress) != ip) {
await preferences.setString(PrefKeys.ipAddress, ip);
} else {
return;
}

nt4Connection.changeIPAddress(ip);
});

await FieldImages.loadFields('assets/fields/');

FlutterView screenView = PlatformDispatcher.instance.views.first;
Size screenSize = screenView.physicalSize * screenView.devicePixelRatio;
Display primaryDisplay = await screenRetriever.getPrimaryDisplay();
Size screenSize =
primaryDisplay.size * (primaryDisplay.scaleFactor?.toDouble() ?? 1.0);

await windowManager.setMinimumSize(screenSize * 0.55);
await windowManager.setMinimumSize(screenSize * 0.60);
await windowManager.setTitleBarStyle(TitleBarStyle.hidden,
windowButtonVisibility: false);

Expand Down
68 changes: 66 additions & 2 deletions lib/pages/dashboard_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:screen_retriever/screen_retriever.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:window_manager/window_manager.dart';
Expand Down Expand Up @@ -73,6 +74,29 @@ class _DashboardPageState extends State<DashboardPage> with WindowListener {

setupShortcuts();

nt4Connection.dsClientConnect(
onIPAnnounced: (ip) async {
if (Globals.ipAddressMode != IPAddressMode.driverStation) {
return;
}

if (_preferences.getString(PrefKeys.ipAddress) != ip) {
await _preferences.setString(PrefKeys.ipAddress, ip);
} else {
return;
}

nt4Connection.changeIPAddress(ip);
},
onDriverStationDockChanged: (docked) {
if (Globals.autoResizeToDS && docked) {
_onDriverStationDocked();
} else {
_onDriverStationUndocked();
}
},
);

nt4Connection.addConnectedListener(() {
setState(() {
for (DashboardGrid grid in grids) {
Expand Down Expand Up @@ -766,9 +790,11 @@ class _DashboardPageState extends State<DashboardPage> with WindowListener {
case IPAddressMode.driverStation:
String? lastAnnouncedIP = nt4Connection.dsClient.lastAnnouncedIP;

if (lastAnnouncedIP != null) {
_updateIPAddress(lastAnnouncedIP);
if (lastAnnouncedIP == null) {
break;
}

_updateIPAddress(lastAnnouncedIP);
break;
case IPAddressMode.roboRIOmDNS:
_updateIPAddress(
Expand Down Expand Up @@ -836,6 +862,19 @@ class _DashboardPageState extends State<DashboardPage> with WindowListener {

await _preferences.setDouble(PrefKeys.cornerRadius, newRadius);
},
onResizeToDSChanged: (value) async {
setState(() {
Globals.autoResizeToDS = value;

if (value && nt4Connection.dsClient.driverStationDocked) {
_onDriverStationDocked();
} else {
_onDriverStationUndocked();
}
});

await _preferences.setBool(PrefKeys.autoResizeToDS, value);
},
onColorChanged: widget.onColorChanged,
),
);
Expand All @@ -849,6 +888,31 @@ class _DashboardPageState extends State<DashboardPage> with WindowListener {
});
}

void _onDriverStationDocked() async {
Display primaryDisplay = await screenRetriever.getPrimaryDisplay();
double pixelRatio = primaryDisplay.scaleFactor?.toDouble() ?? 1.0;
Size screenSize = primaryDisplay.size * pixelRatio;

await windowManager.unmaximize();

Size newScreenSize =
Size(screenSize.width + 16, (screenSize.height + 8) - 250) / pixelRatio;

await windowManager.setSize(newScreenSize);

await windowManager.setAlignment(Alignment.topCenter);

Globals.isWindowMaximizable = false;
Globals.isWindowDraggable = false;
await windowManager.setResizable(false);
}

void _onDriverStationUndocked() async {
Globals.isWindowMaximizable = true;
Globals.isWindowDraggable = true;
await windowManager.setResizable(true);
}

void showWindowCloseConfirmation(BuildContext context) {
showDialog(
barrierDismissible: false,
Expand Down
92 changes: 76 additions & 16 deletions lib/services/ds_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';

import 'package:elastic_dashboard/services/ip_address_util.dart';
import 'package:elastic_dashboard/services/log.dart';
import 'package:flutter/foundation.dart';

class DSInteropClient {
Expand All @@ -12,22 +13,35 @@ class DSInteropClient {
Function()? onDisconnect;

Function(String ip)? onNewIPAnnounced;
Function(bool isDocked)? onDriverStationDockChanged;

String? lastAnnouncedIP;
Socket? _socket;
ServerSocket? _dbModeServer;

DSInteropClient({this.onNewIPAnnounced, this.onConnect, this.onDisconnect}) {
List<int> _tcpBuffer = [];

String? _lastAnnouncedIP;
bool _driverStationDocked = false;

String? get lastAnnouncedIP => _lastAnnouncedIP;
bool get driverStationDocked => _driverStationDocked;

DSInteropClient({
this.onNewIPAnnounced,
this.onDriverStationDockChanged,
this.onConnect,
this.onDisconnect,
}) {
_connect();
}

void _connect() async {
try {
_socket = await Socket.connect(serverBaseAddress, 1742);
_dbModeServer = await ServerSocket.bind(serverBaseAddress, 1741);
} catch (e) {
if (kDebugMode) {
print(
'[DS INTEROP] Failed to connect, attempting to reconnect in 5 seconds.');
}
logger.debug(
'[DS INTEROP] Failed to connect, attempting to reconnect in 5 seconds.');
Future.delayed(const Duration(seconds: 5), _connect);
return;
}
Expand All @@ -38,45 +52,91 @@ class DSInteropClient {
_serverConnectionActive = true;
onConnect?.call();
}
_socketOnMessage(utf8.decode(data));
_tcpSocketOnMessage(utf8.decode(data));
},
onDone: _socketClose,
);

_dbModeServer!.listen(
(socket) {
socket.listen(
(data) {
_tcpServerOnMessage(data);
},
);
},
onDone: _socketClose,
);
}

void _socketOnMessage(String data) {
void _tcpSocketOnMessage(String data) {
var jsonData = jsonDecode(data.toString());

if (jsonData is! Map) {
if (kDebugMode) {
print('[DS INTEROP] Ignoring text message, not a Json Object');
}
logger.warning('[DS INTEROP] Ignoring text message, not a Json Object');
return;
}

var rawIP = jsonData['robotIP'];

if (rawIP == null || rawIP == 0) {
if (kDebugMode) {
print('[DS INTEROP] Ignoring Json message, robot IP is not valid');
}
logger
.warning('[DS INTEROP] Ignoring Json message, robot IP is not valid');
return;
}

String ipAddress = IPAddressUtil.getIpFromInt32Value(rawIP);

if (lastAnnouncedIP != ipAddress) {
if (_lastAnnouncedIP != ipAddress) {
onNewIPAnnounced?.call(ipAddress);
}
lastAnnouncedIP = ipAddress;
_lastAnnouncedIP = ipAddress;
}

void _tcpServerOnMessage(Uint8List data) {
_tcpBuffer.addAll(data);
Map<int, Uint8List> mappedData = {};

int sublistIndex = 0;

for (int i = 0; i < _tcpBuffer.length - 1;) {
int size = (_tcpBuffer[i] << 8) | _tcpBuffer[i + 1];

if (i >= _tcpBuffer.length - 1 - size || size == 0) {
break;
}

Uint8List sublist =
Uint8List.fromList(_tcpBuffer.sublist(i + 2, i + 2 + size));
int tagID = sublist[0];
mappedData[tagID] = sublist.sublist(1);

sublistIndex = i + size + 2;

i += size + 2;
}

_tcpBuffer = _tcpBuffer.sublist(sublistIndex);

if (mappedData.containsKey(0x09)) {
bool docked = (mappedData[0x09]![0] & 0x04 != 0);
_driverStationDocked = docked;

onDriverStationDockChanged?.call(docked);
}
}

void _socketClose() {
_socket?.close();
_socket = null;

_dbModeServer?.close();
_dbModeServer = null;

_serverConnectionActive = false;

_driverStationDocked = false;
onDriverStationDockChanged?.call(false);
onDisconnect?.call();

if (kDebugMode) {
Expand Down
7 changes: 7 additions & 0 deletions lib/services/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class Globals {
static int gridSize = 128;
static double cornerRadius = 15.0;
static bool showGrid = false;
static bool autoResizeToDS = false;

// window_manager doesn't support drag disable/maximize
// disable on some platforms, this is a dumb workaround for it
static bool isWindowDraggable = true;
static bool isWindowMaximizable = true;

static const double defaultPeriod = 0.1;
static const double defaultGraphPeriod = 0.033;
Expand All @@ -26,4 +32,5 @@ class PrefKeys {
static String gridSize = 'grid_size';
static String cornerRadius = 'corner_radius';
static String showGrid = 'show_grid';
static String autoResizeToDS = 'auto_resize_to_driver_station';
}
5 changes: 4 additions & 1 deletion lib/services/nt4_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ class NT4Connection {
allTopicsSubscription = _ntClient.subscribeTopicsOnly('/');
}

void dsClientConnect(Function(String ip)? onIPAnnounced) {
void dsClientConnect(
{Function(String ip)? onIPAnnounced,
Function(bool isDocked)? onDriverStationDockChanged}) {
_dsClient = DSInteropClient(
onNewIPAnnounced: onIPAnnounced,
onDriverStationDockChanged: onDriverStationDockChanged,
onConnect: () => _dsConnected = true,
onDisconnect: () => _dsConnected = false,
);
Expand Down
17 changes: 15 additions & 2 deletions lib/widgets/custom_appbar.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:elastic_dashboard/services/globals.dart';
import 'package:flutter/material.dart';
import 'package:titlebar_buttons/titlebar_buttons.dart';
import 'package:window_manager/window_manager.dart';
Expand Down Expand Up @@ -40,6 +41,10 @@ class CustomAppBar extends AppBar {
child: DecoratedMaximizeButton(
type: buttonType,
onPressed: () async {
if (!Globals.isWindowMaximizable) {
return;
}

if (await windowManager.isMaximized()) {
windowManager.unmaximize();
} else {
Expand Down Expand Up @@ -97,13 +102,21 @@ class _WindowDragArea extends StatelessWidget {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (details) {
if (!Globals.isWindowDraggable) {
return;
}

windowManager.startDragging();
},
onDoubleTap: () async {
if (!Globals.isWindowMaximizable) {
return;
}

if (await windowManager.isMaximized()) {
windowManager.unmaximize();
await windowManager.unmaximize();
} else {
windowManager.maximize();
await windowManager.maximize();
}
},
child: child ?? Container(),
Expand Down
Loading

0 comments on commit 3991ff6

Please sign in to comment.