diff --git a/assets/icons/ic_sin.png b/assets/icons/ic_sin.png new file mode 100644 index 000000000..a83232725 Binary files /dev/null and b/assets/icons/ic_sin.png differ diff --git a/assets/icons/ic_triangular.png b/assets/icons/ic_triangular.png new file mode 100644 index 000000000..72b55caf0 Binary files /dev/null and b/assets/icons/ic_triangular.png differ diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 4b36d9d80..4c28ec9d6 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -99,6 +99,18 @@ "pinPV2": "PV2", "pinPV3": "PV3", "pinPCS": "PCS", + "analog": "Analog", + "digital": "Digital", + "wave1": "Wave 1", + "wave2": "Wave 2", + "sqr1": "sqr1", + "sqr2": "sqr2", + "sqr3": "sqr3", + "sqr4": "sqr4", + "freq": "Freq", + "phase": "Phase", + "duty": "Duty", + "produceSound": "Produce Sound", "analyze": "Analyze", "settings": "Settings", "autoStart": "Auto Start", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 438081543..f5dc42539 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -688,6 +688,78 @@ abstract class AppLocalizations { /// **'PCS'** String get pinPCS; + /// No description provided for @analog. + /// + /// In en, this message translates to: + /// **'Analog'** + String get analog; + + /// No description provided for @digital. + /// + /// In en, this message translates to: + /// **'Digital'** + String get digital; + + /// No description provided for @wave1. + /// + /// In en, this message translates to: + /// **'Wave 1'** + String get wave1; + + /// No description provided for @wave2. + /// + /// In en, this message translates to: + /// **'Wave 2'** + String get wave2; + + /// No description provided for @sqr1. + /// + /// In en, this message translates to: + /// **'sqr1'** + String get sqr1; + + /// No description provided for @sqr2. + /// + /// In en, this message translates to: + /// **'sqr2'** + String get sqr2; + + /// No description provided for @sqr3. + /// + /// In en, this message translates to: + /// **'sqr3'** + String get sqr3; + + /// No description provided for @sqr4. + /// + /// In en, this message translates to: + /// **'sqr4'** + String get sqr4; + + /// No description provided for @freq. + /// + /// In en, this message translates to: + /// **'Freq'** + String get freq; + + /// No description provided for @phase. + /// + /// In en, this message translates to: + /// **'Phase'** + String get phase; + + /// No description provided for @duty. + /// + /// In en, this message translates to: + /// **'Duty'** + String get duty; + + /// No description provided for @produceSound. + /// + /// In en, this message translates to: + /// **'Produce Sound'** + String get produceSound; + /// No description provided for @analyze. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index c114ad842..22b69ef2c 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -316,6 +316,42 @@ class AppLocalizationsEn extends AppLocalizations { @override String get pinPCS => 'PCS'; + @override + String get analog => 'Analog'; + + @override + String get digital => 'Digital'; + + @override + String get wave1 => 'Wave 1'; + + @override + String get wave2 => 'Wave 2'; + + @override + String get sqr1 => 'sqr1'; + + @override + String get sqr2 => 'sqr2'; + + @override + String get sqr3 => 'sqr3'; + + @override + String get sqr4 => 'sqr4'; + + @override + String get freq => 'Freq'; + + @override + String get phase => 'Phase'; + + @override + String get duty => 'Duty'; + + @override + String get produceSound => 'Produce Sound'; + @override String get analyze => 'Analyze'; diff --git a/lib/main.dart b/lib/main.dart index 0b8cd90ae..f5e6e0a69 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:pslab/view/about_us_screen.dart'; import 'package:pslab/view/software_licenses_screen.dart'; import 'package:pslab/theme/app_theme.dart'; import 'package:pslab/view/soundmeter_screen.dart'; +import 'package:pslab/view/wave_generator_screen.dart'; import 'constants.dart'; void main() { @@ -60,6 +61,7 @@ class MyApp extends StatelessWidget { '/': (context) => const InstrumentsScreen(), '/oscilloscope': (context) => const OscilloscopeScreen(), '/multimeter': (context) => const MultimeterScreen(), + '/waveGenerator': (context) => const WaveGeneratorScreen(), '/logicAnalyzer': (context) => const LogicAnalyzerScreen(), '/powerSource': (context) => const PowerSourceScreen(), '/connectDevice': (context) => const ConnectDeviceScreen(), diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index 69725af82..fa27d6bde 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -141,6 +141,17 @@ class _InstrumentsScreenState extends State { ); } break; + case 4: + if (Navigator.canPop(context) && + ModalRoute.of(context)?.settings.name == '/waveGenerator') { + Navigator.popUntil(context, ModalRoute.withName('/waveGenerator')); + } else { + Navigator.pushNamedAndRemoveUntil( + context, + '/waveGenerator', + (route) => route.isFirst, + ); + } default: break; } diff --git a/lib/view/wave_generator_screen.dart b/lib/view/wave_generator_screen.dart new file mode 100644 index 000000000..eaa72f7f9 --- /dev/null +++ b/lib/view/wave_generator_screen.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:pslab/l10n/app_localizations.dart'; +import 'package:pslab/providers/locator.dart'; +import 'package:pslab/theme/colors.dart'; +import 'package:pslab/view/widgets/common_scaffold_widget.dart'; +import 'package:pslab/view/widgets/analog_waveform_controls.dart'; +import 'package:pslab/view/widgets/wave_generator_graph.dart'; +import 'package:pslab/view/widgets/wave_generator_main_controls.dart'; + +class WaveGeneratorScreen extends StatefulWidget { + const WaveGeneratorScreen({super.key}); + + @override + State createState() => _WaveGeneratorScreenState(); +} + +class _WaveGeneratorScreenState extends State { + AppLocalizations appLocalizations = getIt.get(); + @override + Widget build(BuildContext context) { + return CommonScaffold( + title: 'Wave Generator', + body: Container( + margin: const EdgeInsets.all(8.0), + child: Column( + children: [ + Expanded( + flex: 30, + child: Container( + color: chartBackgroundColor, + child: WaveGeneratorGraph(), + ), + ), + Expanded( + flex: 30, + child: Column( + children: [ + Expanded( + flex: 70, + child: AnalogWaveformControls(), + ), + Expanded( + flex: 30, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.produceSound, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.analog, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.digital, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + ], + ), + ), + ], + ), + ), + Expanded( + flex: 40, + child: WaveGeneratorMainControls(), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/widgets/analog_waveform_controls.dart b/lib/view/widgets/analog_waveform_controls.dart new file mode 100644 index 000000000..629c1b543 --- /dev/null +++ b/lib/view/widgets/analog_waveform_controls.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; +import 'package:pslab/l10n/app_localizations.dart'; +import 'package:pslab/providers/locator.dart'; +import 'package:pslab/theme/colors.dart'; + +class AnalogWaveformControls extends StatefulWidget { + const AnalogWaveformControls({super.key}); + + @override + State createState() => _AnalogWaveformControlsState(); +} + +class _AnalogWaveformControlsState extends State { + AppLocalizations appLocalizations = getIt.get(); + String iconSin = "assets/icons/ic_sin.png"; + String iconTriangular = "assets/icons/ic_triangular.png"; + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Container( + margin: const EdgeInsets.only(top: 8, bottom: 5), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + border: Border.all(width: 3, color: primaryRed), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.wave1, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.wave2, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + ], + ), + IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + flex: 35, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.freq, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + flex: 35, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.phase, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + flex: 15, + child: IconButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + icon: Image.asset( + iconSin, + color: Colors.white, + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + flex: 15, + child: IconButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + icon: Image.asset( + iconTriangular, + color: Colors.white, + ), + onPressed: () => {}, + ), + ), + ], + ), + ), + ], + ), + ), + Positioned( + left: 0, + right: 0, + top: 0, + child: Align( + alignment: Alignment.center, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 2), + decoration: BoxDecoration(color: oscilloscopeOptionTitleBoxColor), + child: Text( + appLocalizations.analog, + style: TextStyle( + color: oscilloscopeOptionTitleColor, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.bold, + fontSize: 13, + ), + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/view/widgets/digital_waveform_controls.dart b/lib/view/widgets/digital_waveform_controls.dart new file mode 100644 index 000000000..e16cb77a7 --- /dev/null +++ b/lib/view/widgets/digital_waveform_controls.dart @@ -0,0 +1,200 @@ +import 'package:flutter/material.dart'; +import 'package:pslab/l10n/app_localizations.dart'; +import 'package:pslab/providers/locator.dart'; +import 'package:pslab/theme/colors.dart'; + +class DigitalWaveformControls extends StatefulWidget { + const DigitalWaveformControls({super.key}); + + @override + State createState() => _DigitalWaveformControlsState(); +} + +class _DigitalWaveformControlsState extends State { + AppLocalizations appLocalizations = getIt.get(); + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Container( + margin: const EdgeInsets.only(top: 8, bottom: 5), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + border: Border.all(width: 3, color: primaryRed), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.sqr1.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.sqr2.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.sqr3.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.sqr4.toUpperCase(), + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + ], + ), + IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + flex: 35, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.freq, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + flex: 35, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.phase, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + const SizedBox(width: 4), + Expanded( + flex: 35, + child: TextButton( + style: TextButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: Text( + appLocalizations.duty, + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + onPressed: () => {}, + ), + ), + ], + ), + ), + ], + ), + ), + Positioned( + left: 0, + right: 0, + top: 0, + child: Align( + alignment: Alignment.center, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 2), + decoration: BoxDecoration(color: oscilloscopeOptionTitleBoxColor), + child: Text( + appLocalizations.digital, + style: TextStyle( + color: oscilloscopeOptionTitleColor, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.bold, + fontSize: 13, + ), + ), + ), + ), + ) + ], + ); + } +} diff --git a/lib/view/widgets/wave_generator_graph.dart b/lib/view/widgets/wave_generator_graph.dart new file mode 100644 index 000000000..3f8672416 --- /dev/null +++ b/lib/view/widgets/wave_generator_graph.dart @@ -0,0 +1,92 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:pslab/theme/colors.dart'; + +class WaveGeneratorGraph extends StatefulWidget { + const WaveGeneratorGraph({super.key}); + @override + State createState() => _WaveGeneratorGraphState(); +} + +class _WaveGeneratorGraphState extends State { + Widget topTitleWidgets(double value, TitleMeta meta) { + final style = TextStyle( + color: chartTextColor, + fontSize: 9, + ); + return SideTitleWidget( + meta: meta, + child: Text( + maxLines: 1, + meta.formattedValue, + style: style, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), + child: LineChart( + LineChartData( + backgroundColor: chartBackgroundColor, + titlesData: FlTitlesData( + show: true, + topTitles: AxisTitles( + sideTitles: SideTitles( + interval: 1000.0, + reservedSize: 20, + showTitles: true, + getTitlesWidget: topTitleWidgets, + ), + ), + bottomTitles: const AxisTitles( + sideTitles: SideTitles( + showTitles: false, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: false, + ), + ), + rightTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: false, + ), + ), + ), + gridData: FlGridData( + show: true, + horizontalInterval: 1.0, + drawHorizontalLine: true, + drawVerticalLine: true, + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide( + color: chartBorderColor, + ), + left: BorderSide( + color: chartBorderColor, + ), + top: BorderSide( + color: chartBorderColor, + ), + right: BorderSide( + color: chartBorderColor, + ), + ), + ), + clipData: const FlClipData.all(), + maxY: 5.0, + minY: -5.0, + maxX: 5000.0, + minX: 0.0, + ), + ), + ); + } +} diff --git a/lib/view/widgets/wave_generator_main_controls.dart b/lib/view/widgets/wave_generator_main_controls.dart new file mode 100644 index 000000000..f3f3179f3 --- /dev/null +++ b/lib/view/widgets/wave_generator_main_controls.dart @@ -0,0 +1,174 @@ +import 'package:flutter/material.dart'; +import 'package:pslab/theme/colors.dart'; + +class WaveGeneratorMainControls extends StatefulWidget { + const WaveGeneratorMainControls({super.key}); + + @override + State createState() => _WaveGeneratorMainControlsState(); +} + +class _WaveGeneratorMainControlsState extends State { + String iconSin = "assets/icons/ic_sin.png"; + String iconTriangular = "assets/icons/ic_triangular.png"; + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + flex: 75, + child: Container( + margin: const EdgeInsets.only(top: 4), + color: Colors.black, + child: Column( + children: [ + Expanded( + flex: 80, + child: IntrinsicHeight( + child: Row( + children: [ + Expanded( + flex: 20, + child: Container( + margin: const EdgeInsets.only(left: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + iconSin, + height: 40, + width: 40, + ), + Text( + 'Sine', + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + const VerticalDivider(), + Expanded( + flex: 80, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Frequency:', + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'Phase:', + style: TextStyle( + color: Colors.white, + fontSize: 13, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + ), + ), + Transform.translate( + offset: const Offset(0, -8), + child: const Divider(), + ), + Expanded( + flex: 20, + child: Transform.translate( + offset: const Offset(0, -8), + child: Container( + margin: const EdgeInsets.only( + left: 32, + ), + alignment: Alignment.centerLeft, + child: Text( + 'Phase Offset:', + style: TextStyle( + color: Colors.deepOrange, + fontSize: 16, + ), + ), + ), + ), + ), + ], + ), + ), + ), + Expanded( + flex: 25, + child: Container( + margin: const EdgeInsets.only( + bottom: 16, + ), + child: Row( + children: [ + SizedBox( + height: 35, + width: 30, + child: IconButton.filled( + padding: EdgeInsets.zero, + icon: Icon(Icons.chevron_left), + onPressed: () async {}, + style: IconButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ), + Expanded( + child: SliderTheme( + data: SliderThemeData( + inactiveTrackColor: sliderInActiveColor, + trackHeight: 1, + thumbShape: + const RoundSliderThumbShape(enabledThumbRadius: 6), + ), + child: Slider( + activeColor: primaryRed, + min: 0, + max: 5000, + value: 0, + onChanged: (value) {}, + ), + ), + ), + SizedBox( + height: 35, + width: 30, + child: IconButton.filled( + padding: EdgeInsets.zero, + icon: Icon(Icons.chevron_right), + onPressed: () async {}, + style: IconButton.styleFrom( + backgroundColor: primaryRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +}