Skip to content

Commit

Permalink
feat: add save request to configurator side
Browse files Browse the repository at this point in the history
  • Loading branch information
ShapeLayer committed Oct 25, 2024
1 parent 4e8d9e3 commit 05b341b
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 19 deletions.
13 changes: 3 additions & 10 deletions configurator/lib/components/device_selector.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:configurator/utilities/event_snackbar.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:configurator/models/error_serial_device.dart';
import 'package:configurator/models/list_dropdown_button_item.dart';
Expand All @@ -10,7 +11,6 @@ import 'package:configurator/globals.dart';
import 'package:configurator/models/serial_descriptor.dart';
import 'package:configurator/widgets/list_dropdown_button.dart';
import 'package:configurator/utilities/selected_device_state.dart';
import 'package:configurator/utilities/event_notifier.dart';

class DeviceSelector extends StatefulWidget {
const DeviceSelector({super.key});
Expand Down Expand Up @@ -45,13 +45,6 @@ class _DeviceSelectorState extends State<DeviceSelector> {
_loadAvailableDevice();
}

void _showEventSnackBar(
BuildContext context, NotifyingEvents notifyingEvent) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
EventNotifier.eventNotifyingMessage(context, notifyingEvent))));
}

void _onSelected(IListDropdownButtonItem selected) {
// Phase 1: Invoke validation to check selected device is valid.
Globals.instance.currentSerialDevicePort = selected.toValue();
Expand All @@ -72,7 +65,7 @@ class _DeviceSelectorState extends State<DeviceSelector> {
}
}
if (resultState == SerialDeviceState.invalid && mounted) {
_showEventSnackBar(context,
showEventSnackBar(context,
NotifyingEvents.serialDeviceDoesNotResponseMayInvalidDevice);
}

Expand All @@ -85,7 +78,7 @@ class _DeviceSelectorState extends State<DeviceSelector> {
case StreamControllerNotInstantiatedWell _:
case TimeoutException _:
if (mounted) {
_showEventSnackBar(
showEventSnackBar(
context, NotifyingEvents.serialDeviceDoesNotResponse);
}
}
Expand Down
27 changes: 26 additions & 1 deletion configurator/lib/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,24 @@ class Globals {
return keyConfig;
}

Future<KeyConfig?> requestSaveKeyConfiguration() async {
SerialDevice current = currentSerialDevice;
SerialLoadSavedKeyConfigurationResult
serialLoadSavedKeyConfigurationResult =
await current.requestLoadSavedKeyConfiguration(
requestedSerialDevicePort: currentSerialDevicePort);

if (currentSerialDevicePort != serialLoadSavedKeyConfigurationResult.port) {
return null;
}
if (serialLoadSavedKeyConfigurationResult.data == null) {
return null;
}
keyConfig = serialLoadSavedKeyConfigurationResult.data!;
requestRefreshKeyConfigValueDisplayerWithKey();
return keyConfig;
}

/*
void requestGetCurrentSerialDeviceConfig() {
/// Load device's keyconfig data
Expand Down Expand Up @@ -279,15 +297,22 @@ class Globals {
?.updateValue(keyConfig.emoticon5);
}

void saveCurrentSerialDeviceConfig() async {
Future<bool> saveCurrentSerialDeviceConfig() async {
// await request..
keyConfig = KeyConfig.clone(updatedKeyConfig);
SerialDevice current = currentSerialDevice;
SerialLoadSavedKeyConfigurationResult result =
await current.requestSaveKeyConfiguration(
requestedSerialDevicePort: currentSerialDevicePort);
requestRefreshKeyConfigValueDisplayerWithKey();
return result.data != null;
}

void revertCurrentSerialDeviceConfig() async {
// await request..
updatedKeyConfig = KeyConfig.clone(keyConfig);
callOnKeyConfigChangeEventHandlers();
requestRefreshKeyConfigValueDisplayerWithKey();
}

void applyCurrentSerialDeviceKeyConfigToThis(KeyConfig keyConfig) {
Expand Down
4 changes: 4 additions & 0 deletions configurator/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
"keyNameEmoticon5": "Emoticon #5",
"notifyingEventsSerialDeviceDoesNotResponse": "Selected device doesn't response.",
"notifyingEventsSerialDeviceDoesNotResponse_mayInvalidDevice": "Selected device doesn't response. It seems to be not correct device or software installed.",
"notifyingEventsSerialDeviceConfigSaveComplete": "Configuration has been saved completely.",
"notifyingEventsSerialDeviceConfigSaveError": "An error occurred during the save process.",
"notifyingEventsSerialDeviceConfigSaveErrorDataReceivedIncompletely": "An error occurred during the save process. (Device returned: config data sent incompletely.)",
"notifyingEventsSerialDeviceConfigSaveErrorDataReturnedIncompletely": "An error occurred during the save process. (Program returned: config data has not been saved to device.)",
"keycodeTextLeftCtrl": "Left Control",
"keycodeTextLeftShift": "Left Shift",
"keycodeTextLeftAlt": "Left Alt",
Expand Down
25 changes: 25 additions & 0 deletions configurator/lib/models/error_serial_device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,28 @@ class SerialPortCommunicationDoneIncompleted implements Exception {

String typicalSerialPortCommunicationDoneIncompletedMessage =
'Serial commuication has been done incompletely.';

class SerialPortCommunicationIncompletelyDoneThrownFromDevice
implements Exception {
String cause;
SerialPortCommunicationIncompletelyDoneThrownFromDevice(this.cause);
}

String typicalSerialPortCommunicationIncompletelyDoneThrownFromDeviceMessage =
'Device throws error that serial port communication has been done.';

class SerialPortCommunicationResponseIsOutOfScenario implements Exception {
String cause;
SerialPortCommunicationResponseIsOutOfScenario(this.cause);
}

String typicalSerialPortCommunicationResponseIsOutOfScenarioMessage =
'Device\'s response cannot be like received. (out of scenario)';

class SerialPortCommunicationResultIsNotExpected implements Exception {
String cause;
SerialPortCommunicationResultIsNotExpected(this.cause);
}

String typicalSerialPortCommunicationResultIsNotExpectedMessage =
'SerialPort communication result is not expected value.';
35 changes: 33 additions & 2 deletions configurator/lib/models/key_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,39 @@ class KeyConfig implements KeyConfigStructure<EachKeyConfig> {
// @override
// int get hashCode

List<Keycode> toListKeyCode() {
return [
esc.keycode,
enter.keycode,
tab.keycode,
space.keycode,
speedUp.keycode,
speedDown.keycode,
rewind.keycode,
leftShift.keycode,
rightShift.keycode,
arrowUp.keycode,
arrowDown.keycode,
arrowLeft.keycode,
arrowRight.keycode,
tuneLeftSide.keycode,
tuneS.keycode,
tuneD.keycode,
tuneF.keycode,
tuneC.keycode,
tuneM.keycode,
tuneJ.keycode,
tuneK.keycode,
tuneL.keycode,
tuneRightSide.keycode,
emoticon1.keycode,
emoticon2.keycode,
emoticon3.keycode,
emoticon4.keycode,
emoticon5.keycode,
];
}

factory KeyConfig.fromUint8List(Uint8List list) {
if (list.length != keyConfigArrayLength) {
throw Uint8ListLengthNotOrTooEnough(
Expand All @@ -145,8 +178,6 @@ class KeyConfig implements KeyConfigStructure<EachKeyConfig> {
return Function.apply(
KeyConfig.new,
list.map<EachKeyConfig>((each) {
print('keycode: ${ArduinoKeycode.toKey(each)}');
print('keycode: ${each != Keycode.undefined}');
return EachKeyConfig(
keycode: ArduinoKeycode.toKey(each),
enabled: each != Keycode.undefined);
Expand Down
5 changes: 5 additions & 0 deletions configurator/lib/models/magic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ Uint8List setKeyConfigurationRequest =
// 2e 73 63 2e 72 65 73 2e 61
Uint8List setKeyConfigurationResponse =
Uint8List.fromList('.sc.res.a'.codeUnits);

// set key request served incompletely
// 2e 73 63 2e 72 65 71 2e 62
Uint8List setKeyConfigurationRequestIncomplete =
Uint8List.fromList('.sc.res.b'.codeUnits);
6 changes: 5 additions & 1 deletion configurator/lib/models/notifying_events.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
enum NotifyingEvents {
serialDeviceDoesNotResponse,
serialDeviceDoesNotResponseMayInvalidDevice
serialDeviceDoesNotResponseMayInvalidDevice,
serialDeviceConfigSaveComplete,
serialDeviceConfigSaveError,
serialDeviceConfigSaveErrorDataReceivedIncompletely,
serialDeviceConfigSaveErrorDataReturnedIncompletely,
}
86 changes: 86 additions & 0 deletions configurator/lib/models/serial_device.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import 'dart:async';
import 'dart:developer';
import 'dart:typed_data';

import 'package:configurator/globals.dart';
import 'package:configurator/models/each_key_config.dart';
import 'package:configurator/models/error_serial_device.dart';
import 'package:configurator/models/key_config.dart';
import 'package:configurator/models/keycode.dart';
import 'package:configurator/models/serial_communication_result.dart';
import 'package:configurator/utilities/keycode_utils.dart';
import 'package:configurator/utilities/selected_device_state.dart';
import 'package:configurator/utilities/serial_device_utils.dart';
import 'package:flutter_libserialport/flutter_libserialport.dart';
Expand Down Expand Up @@ -256,6 +259,89 @@ class SerialDevice {
KeyConfig.fromUint8List(SerialDeviceUtils.parseResponseBody(response)));
}

Future<SerialLoadSavedKeyConfigurationResult> requestSaveKeyConfiguration({
String requestedSerialDevicePort = '',
int retryLimit = BuildConfig.serialDefaultRetryLimit,
}) async {
if (serialPort == null) {
throw SerialPortNotInstantiatedWell(
typicalSerialPortCommunicationDoneIncompletedMessage);
}
beginCommunication();

serialPort!.write(magic.setKeyConfigurationRequest);
serialPort!.write(Uint8List.fromList(
Globals.instance.keyConfig.toListKeyCode().map((each) {
return KeycodeUtils.toArduinoKeycode(each);
}).toList()));

try {
Uint8List value = await streamController!.stream.first.timeout(
const Duration(seconds: BuildConfig.serialDefaultTimeout),
);
closeCommunication();
print(value);
return await onSaveKeyConfigurationResponse(
value,
retryLimit,
requestedSerialDevicePort: requestedSerialDevicePort,
);
} catch (error) {
closeCommunication();
if (error is TimeoutException) {
if (retryLimit > 0) {
return await requestLoadSavedKeyConfiguration(
requestedSerialDevicePort: requestedSerialDevicePort,
retryLimit: retryLimit - 1,
);
} else {
return SerialLoadSavedKeyConfigurationResult(
requestedSerialDevicePort, null);
}
} else {
rethrow;
}
}
}

Future<SerialLoadSavedKeyConfigurationResult> onSaveKeyConfigurationResponse(
Uint8List response, int retryLimit,
{String requestedSerialDevicePort = ''}) async {
onFailed({Exception? error}) async {
if (retryLimit > 0) {
return await requestSaveKeyConfiguration(
requestedSerialDevicePort: requestedSerialDevicePort,
retryLimit: retryLimit - 1,
);
} else {
if (error != null) throw error;
return SerialLoadSavedKeyConfigurationResult(
requestedSerialDevicePort, null);
}
}

Uint8List responseHead = SerialDeviceUtils.parseResponseHead(response);
if (responseHead.equals(magic.setKeyConfigurationRequestIncomplete)) {
log('Response from device throws configuration array sent incompletely.',
time: DateTime.now());
return onFailed(
error: SerialPortCommunicationIncompletelyDoneThrownFromDevice(
typicalSerialPortCommunicationIncompletelyDoneThrownFromDeviceMessage));
}
if (!responseHead.equals(magic.setKeyConfigurationResponse)) {
return onFailed(
error: SerialPortCommunicationResponseIsOutOfScenario(
typicalSerialPortCommunicationResponseIsOutOfScenarioMessage));
}

Uint8List responseBody = SerialDeviceUtils.parseResponseBody(response);
if (responseBody.length != keyConfigArrayLength) {
onFailed();
}
return SerialLoadSavedKeyConfigurationResult(requestedSerialDevicePort,
KeyConfig.fromUint8List(SerialDeviceUtils.parseResponseBody(response)));
}

/*
Future requestLoadKeyConfiguration(
{int retryLimit = BuildConfig.serialRetryLimit}) async {
Expand Down
31 changes: 29 additions & 2 deletions configurator/lib/screens/main_config_page.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'package:configurator/components/device_selector.dart';
import 'package:configurator/globals.dart';
import 'package:configurator/models/error_serial_device.dart';
import 'package:configurator/models/notifying_events.dart';
import 'package:configurator/utilities/event_snackbar.dart';
import 'package:configurator/widgets/section_title.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Expand All @@ -16,13 +19,37 @@ class _MainConfigPageState extends State<MainConfigPage> {
void _resetSaveAndRevertButton() {
_saveButtonOnPressed = null;
_revertButtonOnPressed = null;
Globals.instance.requestRefreshKeyConfigValueDisplayerWithKey();
}

void Function()? _saveButtonOnPressed;
void _saveButtonOnPressedHandler() {
Globals.instance.saveCurrentSerialDeviceConfig().then((isDone) {
if (mounted) {
if (isDone) {
showEventSnackBar(
context, NotifyingEvents.serialDeviceConfigSaveComplete);
} else {
showEventSnackBar(
context, NotifyingEvents.serialDeviceConfigSaveError);
}
}
}).catchError((error) {
if (mounted) {
switch (error.runtimeType) {
case SerialPortCommunicationDoneIncompleted _:
showEventSnackBar(
context,
NotifyingEvents
.serialDeviceConfigSaveErrorDataReceivedIncompletely);
return;
case SerialPortCommunicationResponseIsOutOfScenario _:
showEventSnackBar(
context, NotifyingEvents.serialDeviceConfigSaveError);
return;
}
}
});
setState(() {
Globals.instance.saveCurrentSerialDeviceConfig();
_resetSaveAndRevertButton();
});
}
Expand Down
12 changes: 12 additions & 0 deletions configurator/lib/utilities/event_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ class EventNotifier {
case NotifyingEvents.serialDeviceDoesNotResponseMayInvalidDevice:
return AppLocalizations.of(context)!
.notifyingEventsSerialDeviceDoesNotResponse_mayInvalidDevice;
case NotifyingEvents.serialDeviceConfigSaveComplete:
return AppLocalizations.of(context)!
.notifyingEventsSerialDeviceConfigSaveComplete;
case NotifyingEvents.serialDeviceConfigSaveError:
return AppLocalizations.of(context)!
.notifyingEventsSerialDeviceConfigSaveError;
case NotifyingEvents.serialDeviceConfigSaveErrorDataReceivedIncompletely:
return AppLocalizations.of(context)!
.notifyingEventsSerialDeviceConfigSaveErrorDataReceivedIncompletely;
case NotifyingEvents.serialDeviceConfigSaveErrorDataReturnedIncompletely:
return AppLocalizations.of(context)!
.notifyingEventsSerialDeviceConfigSaveErrorDataReturnedIncompletely;
}
}
}
9 changes: 9 additions & 0 deletions configurator/lib/utilities/event_snackbar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:configurator/models/notifying_events.dart';
import 'package:configurator/utilities/event_notifier.dart';
import 'package:flutter/material.dart';

void showEventSnackBar(BuildContext context, NotifyingEvents notifyingEvent) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text(EventNotifier.eventNotifyingMessage(context, notifyingEvent))));
}
Loading

0 comments on commit 05b341b

Please sign in to comment.