Skip to content

Commit 69476b6

Browse files
authored
feat: added guide for robotic arm (#2801)
1 parent 3124fca commit 69476b6

File tree

5 files changed

+155
-95
lines changed

5 files changed

+155
-95
lines changed

assets/images/robotic_arm_guide.png

320 KB
Loading

lib/l10n/app_en.arb

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -311,40 +311,42 @@
311311
"baroMeterBulletPoint2": "If you want to use the sensor BMP-180, connect the sensor to PSLab device as shown in the figure.",
312312
"baroMeterBulletPoint3": "The above pin configuration has to be same except for the pin GND. GND is meant for Ground and any of the PSLab device GND pins can be used since they are common.",
313313
"baroMeterBulletPoint4": "Select the sensor by going to the Configure tab from the bottom navigation bar and choose BMP-180 in the drop down menu under Select Sensor.",
314-
"sharingMessage" : "Sharing PSLab Data",
315-
"delete" : "Delete",
314+
"sharingMessage": "Sharing PSLab Data",
315+
"delete": "Delete",
316316
"deleteHint": "Are you sure you want to delete this file?",
317-
"deleteFile" : "Delete File",
318-
"deleteAllData" : "Delete All Data",
319-
"deleteCautionMessage" : "Are you sure you want to delete all logged data for this instrument?",
320-
"deleteAll" : "Delete All",
321-
"noLoggedData" : "No logged data found.",
322-
"importLog" : "Import Log",
323-
"failedToSave" : "Failed to save file. No data was recorded.",
324-
"fileSaved" : "File saved",
325-
"save" : "Save",
326-
"enterFileName" : "Enter filename (leave empty for auto-generated name)",
327-
"fileName" : "Filename",
328-
"saveRecording" : "Save Recording",
329-
"recordingStarted" : "Recording started",
330-
"noValidData" : "No valid data to display.",
331-
"csvPickingError" : "Error picking or reading CSV file",
332-
"csvReadingError" : "Error reading CSV from file",
333-
"sharingError" : "Error sharing file",
334-
"csvGettingError" : "Error getting saved files",
335-
"unsupportedPlatform" : "Unsupported platform",
336-
"noDataRecorded" : "No data recorded to save for",
337-
"csvFileSaved" : "CSV file saved at",
338-
"csvSavingError" : "Error saving CSV file",
339-
"csvDeletingError" : "Error deleting file",
340-
"fileDeleted" : "File deleted",
341-
"soundmeterConfig" : "Soundmeter Configurations",
342-
"barometerConfig" : "Barometer Configurations",
343-
"baroUpdatePeriodHint" : "Please provide time interval at which data will be updated (100 ms to 2000 ms)",
344-
"barometerHighLimitHint" : "Please provide the maximum limit of lux value to be recorded (0 atm to 1.10 atm)",
345-
"gyroscopeConfigurations" : "Gyroscope Configurations",
346-
"gyroscopeHighLimitHint" : "Please provide the maximum limit of lux value to be recorded (0 rad/s to 1000 rad/s)",
347-
"accelerometerConfigurations" : "Accelerometer Configurations",
348-
"accelerometerUpdatePeriodHint" : "Please provide time interval at which data will be updated",
349-
"accelerometerHighLimitHint" : "Please provide the maximum limit of lux value to be recorded"
317+
"deleteFile": "Delete File",
318+
"deleteAllData": "Delete All Data",
319+
"deleteCautionMessage": "Are you sure you want to delete all logged data for this instrument?",
320+
"deleteAll": "Delete All",
321+
"noLoggedData": "No logged data found.",
322+
"importLog": "Import Log",
323+
"failedToSave": "Failed to save file. No data was recorded.",
324+
"fileSaved": "File saved",
325+
"save": "Save",
326+
"enterFileName": "Enter filename (leave empty for auto-generated name)",
327+
"fileName": "Filename",
328+
"saveRecording": "Save Recording",
329+
"recordingStarted": "Recording started",
330+
"noValidData": "No valid data to display.",
331+
"csvPickingError": "Error picking or reading CSV file",
332+
"csvReadingError": "Error reading CSV from file",
333+
"sharingError": "Error sharing file",
334+
"csvGettingError": "Error getting saved files",
335+
"unsupportedPlatform": "Unsupported platform",
336+
"noDataRecorded": "No data recorded to save for",
337+
"csvFileSaved": "CSV file saved at",
338+
"csvSavingError": "Error saving CSV file",
339+
"csvDeletingError": "Error deleting file",
340+
"fileDeleted": "File deleted",
341+
"soundmeterConfig": "Soundmeter Configurations",
342+
"barometerConfig": "Barometer Configurations",
343+
"baroUpdatePeriodHint": "Please provide time interval at which data will be updated (100 ms to 2000 ms)",
344+
"barometerHighLimitHint": "Please provide the maximum limit of lux value to be recorded (0 atm to 1.10 atm)",
345+
"gyroscopeConfigurations": "Gyroscope Configurations",
346+
"gyroscopeHighLimitHint": "Please provide the maximum limit of lux value to be recorded (0 rad/s to 1000 rad/s)",
347+
"accelerometerConfigurations": "Accelerometer Configurations",
348+
"accelerometerUpdatePeriodHint": "Please provide time interval at which data will be updated",
349+
"accelerometerHighLimitHint": "Please provide the maximum limit of lux value to be recorded",
350+
"roboticArmIntro": "• A robotic arm is a programmable mechanical device that mimics the movement of a human arm.\n• It uses servo motors to control its motion, and these motors are operated using PWM signals.\n• The PSLab provides four PWM square wave generators (SQ1, SQ2, SQ3, SQ4), allowing control of up to four servo motors and enabling a robotic arm with up to four degrees of freedom.",
351+
"roboticArmConnection": "• In the above figure, SQ1 is connected to the signal pin of the first servo motor. The servo's GND pin is connected to both the PSLab’s GND and the external power supply GND, while the VCC pin is connected to the external power supply VCC.\n• Similarly, connect the remaining servos to SQ2, SQ3, and SQ4 along with their respective GND and power supply connections.\n• Once connected, each servo can be controlled using either circular sliders for manual control or a timeline-based sequence for automated movement."
350352
}

lib/l10n/app_localizations.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,18 @@ abstract class AppLocalizations {
21752175
/// In en, this message translates to:
21762176
/// **'Please provide the maximum limit of lux value to be recorded'**
21772177
String get accelerometerHighLimitHint;
2178+
2179+
/// No description provided for @roboticArmIntro.
2180+
///
2181+
/// In en, this message translates to:
2182+
/// **'• A robotic arm is a programmable mechanical device that mimics the movement of a human arm.\n• It uses servo motors to control its motion, and these motors are operated using PWM signals.\n• The PSLab provides four PWM square wave generators (SQ1, SQ2, SQ3, SQ4), allowing control of up to four servo motors and enabling a robotic arm with up to four degrees of freedom.'**
2183+
String get roboticArmIntro;
2184+
2185+
/// No description provided for @roboticArmConnection.
2186+
///
2187+
/// In en, this message translates to:
2188+
/// **'• In the above figure, SQ1 is connected to the signal pin of the first servo motor. The servo\'s GND pin is connected to both the PSLab’s GND and the external power supply GND, while the VCC pin is connected to the external power supply VCC.\n• Similarly, connect the remaining servos to SQ2, SQ3, and SQ4 along with their respective GND and power supply connections.\n• Once connected, each servo can be controlled using either circular sliders for manual control or a timeline-based sequence for automated movement.'**
2189+
String get roboticArmConnection;
21782190
}
21792191

21802192
class _AppLocalizationsDelegate

lib/l10n/app_localizations_en.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,4 +1115,12 @@ class AppLocalizationsEn extends AppLocalizations {
11151115
@override
11161116
String get accelerometerHighLimitHint =>
11171117
'Please provide the maximum limit of lux value to be recorded';
1118+
1119+
@override
1120+
String get roboticArmIntro =>
1121+
'• A robotic arm is a programmable mechanical device that mimics the movement of a human arm.\n• It uses servo motors to control its motion, and these motors are operated using PWM signals.\n• The PSLab provides four PWM square wave generators (SQ1, SQ2, SQ3, SQ4), allowing control of up to four servo motors and enabling a robotic arm with up to four degrees of freedom.';
1122+
1123+
@override
1124+
String get roboticArmConnection =>
1125+
'• In the above figure, SQ1 is connected to the signal pin of the first servo motor. The servo\'s GND pin is connected to both the PSLab’s GND and the external power supply GND, while the VCC pin is connected to the external power supply VCC.\n• Similarly, connect the remaining servos to SQ2, SQ3, and SQ4 along with their respective GND and power supply connections.\n• Once connected, each servo can be controlled using either circular sliders for manual control or a timeline-based sequence for automated movement.';
11181126
}

lib/view/robotic_arm_screen.dart

Lines changed: 98 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:pslab/view/widgets/robotic_arm_dialog.dart';
88
import 'package:pslab/view/widgets/robotic_arm_summary.dart';
99
import 'package:pslab/view/widgets/robotic_arm_timeline.dart';
1010
import '../providers/robotic_arm_state_provider.dart';
11+
import 'package:pslab/view/widgets/guide_widget.dart';
1112
import 'widgets/servo_card.dart';
1213

1314
class RoboticArmScreen extends StatefulWidget {
@@ -20,6 +21,8 @@ class RoboticArmScreen extends StatefulWidget {
2021
class _RoboticArmScreenState extends State<RoboticArmScreen> {
2122
late RoboticArmStateProvider provider;
2223
late List<String> servoLabels;
24+
bool _showGuide = false;
25+
static const imagePath = 'assets/images/robotic_arm_guide.png';
2326
AppLocalizations appLocalizations = getIt.get<AppLocalizations>();
2427
@override
2528
void initState() {
@@ -46,6 +49,26 @@ class _RoboticArmScreenState extends State<RoboticArmScreen> {
4649
};
4750
}
4851

52+
void _hideInstrumentGuide() {
53+
setState(() {
54+
_showGuide = false;
55+
});
56+
}
57+
58+
List<Widget> _getRoboticArmContent() {
59+
return [
60+
InstrumentIntroText(
61+
text: appLocalizations.roboticArmIntro,
62+
),
63+
const InstrumentImage(
64+
imagePath: imagePath,
65+
),
66+
InstrumentIntroText(
67+
text: appLocalizations.roboticArmConnection,
68+
),
69+
];
70+
}
71+
4972
void _showAngleInputDialog(BuildContext context, int index) {
5073
final currentValue = provider.servoValues[index];
5174

@@ -83,7 +106,7 @@ class _RoboticArmScreenState extends State<RoboticArmScreen> {
83106
final screenWidth = MediaQuery.of(context).size.width;
84107
final scrollAmount = (screenWidth / 6);
85108
return CommonScaffold(
86-
title: appLocalizations.roboticArm,
109+
title: appLocalizations.roboticArmTitle,
87110
actions: [
88111
IconButton(
89112
icon: Icon(
@@ -160,12 +183,16 @@ class _RoboticArmScreenState extends State<RoboticArmScreen> {
160183
IconButton(
161184
icon: const Icon(Icons.save, color: Colors.white),
162185
tooltip: appLocalizations.saveData,
163-
onPressed: () {}, //TODO
186+
onPressed: () {}, // TODO
164187
),
165188
IconButton(
166189
icon: const Icon(Icons.info, color: Colors.white),
167190
tooltip: appLocalizations.showGuide,
168-
onPressed: () {}, //TODO
191+
onPressed: () {
192+
setState(() {
193+
_showGuide = !_showGuide;
194+
});
195+
},
169196
),
170197
PopupMenuButton<String>(
171198
icon: const Icon(Icons.more_vert, color: Colors.white),
@@ -182,67 +209,78 @@ class _RoboticArmScreenState extends State<RoboticArmScreen> {
182209
],
183210
),
184211
],
185-
body: SafeArea(
186-
child: Padding(
187-
padding: const EdgeInsets.all(4.0),
188-
child: Column(
189-
crossAxisAlignment: CrossAxisAlignment.start,
190-
children: [
191-
SizedBox(
192-
height: servoHeight,
193-
child: Row(
194-
mainAxisAlignment: MainAxisAlignment.spaceBetween,
195-
children: List.generate(4, (index) {
196-
return Expanded(
197-
child: Padding(
198-
padding:
199-
const EdgeInsets.symmetric(horizontal: 1),
200-
child: SizedBox(
201-
height: servoHeight,
202-
child: ServoCard(
203-
value: provider.servoValues[index],
204-
label: servoLabels[index],
205-
servoId: index,
206-
onChanged: (val) {
207-
setState(() {
208-
provider.updateServoValue(index, val);
209-
});
210-
},
211-
onTap: () =>
212-
_showAngleInputDialog(context, index),
213-
cardHeight: servoHeight,
212+
body: Stack(
213+
children: [
214+
SafeArea(
215+
child: Padding(
216+
padding: const EdgeInsets.all(4.0),
217+
child: Column(
218+
crossAxisAlignment: CrossAxisAlignment.start,
219+
children: [
220+
SizedBox(
221+
height: servoHeight,
222+
child: Row(
223+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
224+
children: List.generate(4, (index) {
225+
return Expanded(
226+
child: Padding(
227+
padding:
228+
const EdgeInsets.symmetric(horizontal: 1),
229+
child: SizedBox(
230+
height: servoHeight,
231+
child: ServoCard(
232+
value: provider.servoValues[index],
233+
label: servoLabels[index],
234+
servoId: index,
235+
onChanged: (val) {
236+
setState(() {
237+
provider.updateServoValue(index, val);
238+
});
239+
},
240+
onTap: () =>
241+
_showAngleInputDialog(context, index),
242+
cardHeight: servoHeight,
243+
),
244+
),
214245
),
215-
),
246+
);
247+
}),
248+
),
249+
),
250+
const SizedBox(height: 2),
251+
Expanded(
252+
child: Scrollbar(
253+
controller: provider.timelineScrollController,
254+
thumbVisibility: true,
255+
thickness: screenHeight * 0.006,
256+
radius: const Radius.circular(4),
257+
child: TimelineScrollView(
258+
totalTimelineItems: provider.totalTimelineItems,
259+
screenHeight: screenHeight,
260+
timelinePosition: provider.timelinePosition,
261+
timelineDegrees: provider.timelineDegrees,
262+
scrollController:
263+
provider.timelineScrollController,
264+
onUpdate: (index, servo, value) {
265+
setState(() {
266+
provider.updateTimelineDegree(
267+
index, servo, value);
268+
});
269+
},
216270
),
217-
);
218-
}),
219-
),
271+
),
272+
)
273+
],
220274
),
221-
const SizedBox(height: 2),
222-
Expanded(
223-
child: Scrollbar(
224-
controller: provider.timelineScrollController,
225-
thumbVisibility: true,
226-
thickness: screenHeight * 0.006,
227-
radius: const Radius.circular(4),
228-
child: TimelineScrollView(
229-
totalTimelineItems: provider.totalTimelineItems,
230-
screenHeight: screenHeight,
231-
timelinePosition: provider.timelinePosition,
232-
timelineDegrees: provider.timelineDegrees,
233-
scrollController: provider.timelineScrollController,
234-
onUpdate: (index, servo, value) {
235-
setState(() {
236-
provider.updateTimelineDegree(
237-
index, servo, value);
238-
});
239-
},
240-
),
241-
),
242-
)
243-
],
275+
),
244276
),
245-
),
277+
if (_showGuide)
278+
InstrumentOverviewDrawer(
279+
instrumentName: appLocalizations.roboticArmTitle,
280+
content: _getRoboticArmContent(),
281+
onHide: _hideInstrumentGuide,
282+
),
283+
],
246284
),
247285
);
248286
},

0 commit comments

Comments
 (0)