diff --git a/lib/ntutils.dart b/lib/ntutils.dart index e18b2e7..43e9099 100644 --- a/lib/ntutils.dart +++ b/lib/ntutils.dart @@ -10,12 +10,9 @@ class NTUtils { } bool isConnected = false; - bool _isAttemptingToConnect = false; String _serverAddress = ""; - NT4Client? ntClient; - - final Map _nt4Subscriptions = {}; + NT4Client? _ntClient; final List connectCallbacks = []; final List disconnectCallbacks = []; @@ -30,12 +27,10 @@ class NTUtils { void connect(String serverAddress) { _serverAddress = serverAddress; - _isAttemptingToConnect = true; - ntClient = NT4Client( + _ntClient = NT4Client( serverBaseAddress: serverAddress, onConnect: () { isConnected = true; - _isAttemptingToConnect = false; for (VoidCallback cb in connectCallbacks) { cb(); @@ -52,33 +47,26 @@ class NTUtils { } String connectionStatus() { - if (_isAttemptingToConnect) { - return "Attempting to connect to $_serverAddress"; - } if (isConnected) { return "Connected"; } else { - return "Not Connected"; + return "Not Connected. Attempting to connect to $_serverAddress"; } } NT4Subscription subscribe(String topic) { - if (!_nt4Subscriptions.containsKey(topic)) { - NT4Subscription subscrip = ntClient!.subscribe( - topic, - NT4SubscriptionOptions(), - ); - _nt4Subscriptions[topic] = subscrip; - } - - return _nt4Subscriptions[topic]!; + NT4Subscription subscrip = _ntClient!.subscribe( + topic, + NT4SubscriptionOptions(), + ); + return subscrip; } final Map _nt4Topics = {}; NT4Topic getPublicTopic(String topic, String type) { if (!_nt4Topics.containsKey(topic)) { - NT4Topic top = ntClient!.publishNewTopic(topic, type); + NT4Topic top = _ntClient!.publishNewTopic(topic, type); _nt4Topics[topic] = top; } diff --git a/lib/pages/dashboard.dart b/lib/pages/dashboard.dart index 055ee00..c619226 100644 --- a/lib/pages/dashboard.dart +++ b/lib/pages/dashboard.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:helm/ntutils.dart'; import 'package:helm/widgets/base_card.dart'; -import 'package:helm/widgets/posty_results_card.dart'; +import 'package:helm/widgets/posty/posty_results_card.dart'; +import 'package:helm/widgets/posty/posty_summary_card.dart'; import 'package:responsive_grid/responsive_grid.dart'; import '../widgets/text_card.dart'; @@ -34,60 +35,69 @@ class _DashboardState extends State { // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text("Helm - $connectionState"), - ), - body: ResponsiveGridRow( - children: [ - ResponsiveGridCol( - md: 12, - child: ResponsiveGridRow( + return DefaultTabController( + length: 1, + initialIndex: 0, + child: Scaffold( + appBar: AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text("Helm - $connectionState"), + bottom: TabBar(tabs: [Tab(icon: Icon(Icons.home))]), + ), + body: TabBarView( + children: [ + ResponsiveGridRow( children: [ ResponsiveGridCol( - md: 6, - child: BaseCard(cardTitle: "Reef Picker", height: 4), - ), - ResponsiveGridCol( - md: 6, + md: 12, child: ResponsiveGridRow( children: [ ResponsiveGridCol( md: 6, - child: TextCard( - cardTitle: "Match State", - ntTopic: "/FMSInfo/StationNumber", + child: BaseCard(cardTitle: "Reef Picker", height: 4), + ), + ResponsiveGridCol( + md: 6, + child: ResponsiveGridRow( + children: [ + ResponsiveGridCol( + md: 6, + child: TextCard( + cardTitle: "Match State", + ntTopic: "/FMSInfo/StationNumber", + ), + ), + ResponsiveGridCol( + md: 6, + child: TextCard(cardTitle: "Match Time"), + ), + ResponsiveGridCol(md: 6, child: PostyResultsCard()), + ], ), ), ResponsiveGridCol( md: 6, - child: TextCard(cardTitle: "Match Time"), + child: ResponsiveGridRow( + children: [ + ResponsiveGridCol( + child: TextCard(cardTitle: "Blah"), + ), + ResponsiveGridCol(child: PostySummaryCard()), + ], + ), ), - ResponsiveGridCol( - md: 6, - child: TextCard(cardTitle: "Robot State"), - ), - ], - ), - ), - ResponsiveGridCol( - md: 6, - child: ResponsiveGridRow( - children: [ - ResponsiveGridCol(child: TextCard(cardTitle: "Blah")), ], ), ), ], ), - ), - ], + ], + ), ), ); } diff --git a/lib/widgets/posty/posty_results_card.dart b/lib/widgets/posty/posty_results_card.dart new file mode 100644 index 0000000..1207b65 --- /dev/null +++ b/lib/widgets/posty/posty_results_card.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:helm/ntutils.dart'; +import 'package:helm/widgets/base_card.dart'; +import 'package:nt4/nt4.dart'; + +class PostyResultsCard extends StatefulWidget { + const PostyResultsCard({super.key}); + + @override + State createState() => _PostyResultsCardState(); +} + +class _PostyResultsCardState extends State { + final String _ntTopic = "/Posty/results/post/resultsJson"; + + List resultsData = []; + + _PostyResultsCardState() { + NT4Subscription sub = NTUtils().subscribe(_ntTopic); + sub.listen((data) { + setState(() { + List tmp = jsonDecode(data.toString()); + + for (dynamic json in tmp) { + resultsData.add(PostyResultModel.fromJson(json)); + } + }); + }); + } + + @override + Widget build(BuildContext context) { + List children = []; + + resultsData.sort((a, b) => a.subsystemName.compareTo(b.subsystemName)); + + children = + resultsData.map((result) { + Color statusColor = Colors.green; + if (result.result == "FAILED") { + statusColor = Colors.red; + } else if (result.result == "SKIPPED") { + statusColor = Colors.grey; + } + + return Text( + "${result.testName} -- ${result.result}", + style: TextStyle(color: statusColor), + ); + }).toList(); + + return BaseCard( + width: 6, + height: 2, + cardTitle: "Self Test Results", + children: children, + ); + } +} + +class PostyResultModel { + String testName = ""; + String subsystemName = ""; + String result = ""; + String failReason = ""; + + PostyResultModel( + this.testName, + this.subsystemName, + this.result, + this.failReason, + ); + + PostyResultModel.fromJson(Map json) + : testName = json['testName'] as String, + subsystemName = json['subsystemName'] as String, + result = json['result'] as String, + failReason = json['failReason'] as String; +} diff --git a/lib/widgets/posty/posty_summary_card.dart b/lib/widgets/posty/posty_summary_card.dart new file mode 100644 index 0000000..e5ce2b2 --- /dev/null +++ b/lib/widgets/posty/posty_summary_card.dart @@ -0,0 +1,86 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:helm/ntutils.dart'; +import 'package:helm/widgets/base_card.dart'; +import 'package:nt4/nt4.dart'; + +class PostySummaryCard extends StatefulWidget { + const PostySummaryCard({super.key}); + + @override + State createState() => _PostySummaryCardState(); +} + +class _PostySummaryCardState extends State { + final String _ntTopic = "/Posty/results/post/resultsJson"; + + List resultsData = []; + + _PostySummaryCardState() {} + + @override + Widget build(BuildContext context) { + NT4Subscription sub = NTUtils().subscribe(_ntTopic); + sub.listen((data) { + setState(() { + List tmp = jsonDecode(data.toString()); + + for (dynamic json in tmp) { + resultsData.add(PostyResultModel.fromJson(json)); + } + }); + }); + + bool systemNotReady = false; + + for (PostyResultModel result in resultsData) { + systemNotReady |= result.result == "FAILED"; + } + + List children = [ + Center( + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration(color: Colors.grey, shape: BoxShape.circle), + child: Padding( + padding: EdgeInsets.all(2), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: !systemNotReady ? Colors.green : Colors.red, + ), + ), + ), + ), + ), + ]; + return BaseCard( + width: 1, + height: 1, + cardTitle: "System Status", + children: children, + ); + } +} + +class PostyResultModel { + String testName = ""; + String subsystemName = ""; + String result = ""; + String failReason = ""; + + PostyResultModel( + this.testName, + this.subsystemName, + this.result, + this.failReason, + ); + + PostyResultModel.fromJson(Map json) + : testName = json['testName'] as String, + subsystemName = json['subsystemName'] as String, + result = json['result'] as String, + failReason = json['failReason'] as String; +} diff --git a/lib/widgets/text_card.dart b/lib/widgets/text_card.dart index bc1651f..6c63bf2 100644 --- a/lib/widgets/text_card.dart +++ b/lib/widgets/text_card.dart @@ -4,16 +4,16 @@ import 'package:nt4/nt4.dart'; import 'package:helm/ntutils.dart'; class TextCard extends StatefulWidget { - const TextCard({ + TextCard({ super.key, required this.cardTitle, - this.ntTopic = '', + this.ntTopic = "", this.height = 1, this.width = 1, }); final String cardTitle; - final String? ntTopic; + String ntTopic = ""; final int width, height; @@ -24,6 +24,8 @@ class TextCard extends StatefulWidget { class _TextCardState extends State { String _val = ""; + _TextCardState() {} + void setVal(String newVal) { setState(() { _val = newVal; @@ -32,9 +34,8 @@ class _TextCardState extends State { @override Widget build(BuildContext context) { - NT4Subscription sub = NTUtils().subscribe(widget.ntTopic!); + NT4Subscription sub = NTUtils().subscribe(widget.ntTopic); sub.listen((data) => setVal(data.toString())); - return BaseCard( cardTitle: widget.cardTitle, width: widget.width, diff --git a/pubspec.lock b/pubspec.lock index 0982886..cd3b966 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -87,26 +87,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -143,10 +143,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" msgpack_dart: dependency: transitive description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.7" typed_data: dependency: transitive description: @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -273,5 +273,5 @@ packages: source: hosted version: "2.4.5" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54"