From 5db20aa0885ddbc94e712f21bde5093ca211f955 Mon Sep 17 00:00:00 2001 From: Tyler Wilcox Date: Sat, 21 Mar 2026 10:12:53 -0400 Subject: [PATCH] New Widgets --- lib/ntutils.dart | 20 +++-- lib/pages/dashboard.dart | 16 +++- .../posty/models/posty_results_model.dart | 19 +++++ lib/widgets/posty/posty_results_card.dart | 47 ++++++----- lib/widgets/posty/posty_summary_card.dart | 73 ++++++++++------- .../sendable_chooser_card.dart | 81 +++++++++++++++++++ lib/widgets/text_card.dart | 10 ++- 7 files changed, 203 insertions(+), 63 deletions(-) create mode 100644 lib/widgets/posty/models/posty_results_model.dart create mode 100644 lib/widgets/smart_dashboard/sendable_chooser_card.dart diff --git a/lib/ntutils.dart b/lib/ntutils.dart index 43e9099..c23a5d2 100644 --- a/lib/ntutils.dart +++ b/lib/ntutils.dart @@ -54,17 +54,21 @@ class NTUtils { } } - NT4Subscription subscribe(String topic) { - NT4Subscription subscrip = _ntClient!.subscribe( - topic, - NT4SubscriptionOptions(), - ); + NT4Subscription subscribe( + String topic, { + NT4SubscriptionOptions opts = const NT4SubscriptionOptions(), + }) { + NT4Subscription subscrip = _ntClient!.subscribe(topic, opts); return subscrip; } + void unsubscribe(NT4Subscription subscription) { + _ntClient!.unSubscribe(subscription); + } + final Map _nt4Topics = {}; - NT4Topic getPublicTopic(String topic, String type) { + NT4Topic getPublishTopic(String topic, String type) { if (!_nt4Topics.containsKey(topic)) { NT4Topic top = _ntClient!.publishNewTopic(topic, type); _nt4Topics[topic] = top; @@ -73,5 +77,9 @@ class NTUtils { return _nt4Topics[topic]!; } + void publishSample(NT4Topic topic, dynamic sample) { + _ntClient!.addSample(topic, sample); + } + NTUtils._internal(); } diff --git a/lib/pages/dashboard.dart b/lib/pages/dashboard.dart index c619226..22d3caa 100644 --- a/lib/pages/dashboard.dart +++ b/lib/pages/dashboard.dart @@ -3,6 +3,7 @@ import 'package:helm/ntutils.dart'; import 'package:helm/widgets/base_card.dart'; import 'package:helm/widgets/posty/posty_results_card.dart'; import 'package:helm/widgets/posty/posty_summary_card.dart'; +import 'package:helm/widgets/smart_dashboard/sendable_chooser_card.dart'; import 'package:responsive_grid/responsive_grid.dart'; import '../widgets/text_card.dart'; @@ -36,7 +37,7 @@ class _DashboardState extends State { // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. return DefaultTabController( - length: 1, + length: 2, initialIndex: 0, child: Scaffold( appBar: AppBar( @@ -47,7 +48,12 @@ class _DashboardState extends State { // 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))]), + bottom: TabBar( + tabs: [ + Tab(icon: Icon(Icons.home)), + Tab(icon: Icon(Icons.checklist)), + ], + ), ), body: TabBarView( children: [ @@ -88,6 +94,11 @@ class _DashboardState extends State { child: TextCard(cardTitle: "Blah"), ), ResponsiveGridCol(child: PostySummaryCard()), + ResponsiveGridCol( + child: SendableChooserCard( + chooserName: "SendableChooser[0]", + ), + ), ], ), ), @@ -96,6 +107,7 @@ class _DashboardState extends State { ), ], ), + Text("CheckList"), ], ), ), diff --git a/lib/widgets/posty/models/posty_results_model.dart b/lib/widgets/posty/models/posty_results_model.dart new file mode 100644 index 0000000..a7e49f7 --- /dev/null +++ b/lib/widgets/posty/models/posty_results_model.dart @@ -0,0 +1,19 @@ +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_results_card.dart b/lib/widgets/posty/posty_results_card.dart index 1207b65..55a5c7d 100644 --- a/lib/widgets/posty/posty_results_card.dart +++ b/lib/widgets/posty/posty_results_card.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:helm/widgets/posty/models/posty_results_model.dart'; + import 'package:flutter/material.dart'; import 'package:helm/ntutils.dart'; import 'package:helm/widgets/base_card.dart'; @@ -14,15 +16,31 @@ class PostyResultsCard extends StatefulWidget { class _PostyResultsCardState extends State { final String _ntTopic = "/Posty/results/post/resultsJson"; - + NT4Subscription? sub; List resultsData = []; - _PostyResultsCardState() { - NT4Subscription sub = NTUtils().subscribe(_ntTopic); - sub.listen((data) { + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + print("Dispose"); + NTUtils().unsubscribe(sub!); + } + + @override + void initState() { + super.initState(); + print("It's been made"); + sub = NTUtils().subscribe( + _ntTopic, + opts: NT4SubscriptionOptions(all: true), + ); + sub!.listen((data) { + print("nt Update"); setState(() { List tmp = jsonDecode(data.toString()); + resultsData.clear(); for (dynamic json in tmp) { resultsData.add(PostyResultModel.fromJson(json)); } @@ -32,6 +50,7 @@ class _PostyResultsCardState extends State { @override Widget build(BuildContext context) { + print("Build"); List children = []; resultsData.sort((a, b) => a.subsystemName.compareTo(b.subsystemName)); @@ -59,23 +78,3 @@ class _PostyResultsCardState extends State { ); } } - -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 index e5ce2b2..ecae24c 100644 --- a/lib/widgets/posty/posty_summary_card.dart +++ b/lib/widgets/posty/posty_summary_card.dart @@ -1,12 +1,16 @@ import 'dart:convert'; +import 'package:helm/widgets/posty/models/posty_results_model.dart'; + 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}); + const PostySummaryCard({super.key, this.showAlertDialog = false}); + + final bool showAlertDialog; @override State createState() => _PostySummaryCardState(); @@ -14,30 +18,37 @@ class PostySummaryCard extends StatefulWidget { class _PostySummaryCardState extends State { final String _ntTopic = "/Posty/results/post/resultsJson"; - + NT4Subscription? sub; List resultsData = []; - _PostySummaryCardState() {} - @override - Widget build(BuildContext context) { - NT4Subscription sub = NTUtils().subscribe(_ntTopic); - sub.listen((data) { + void initState() { + super.initState(); + sub = NTUtils().subscribe(_ntTopic); + sub!.listen((data) { setState(() { List tmp = jsonDecode(data.toString()); + resultsData.clear(); for (dynamic json in tmp) { resultsData.add(PostyResultModel.fromJson(json)); } }); }); + } + bool isFailedResult() { bool systemNotReady = false; for (PostyResultModel result in resultsData) { systemNotReady |= result.result == "FAILED"; } + return systemNotReady; + } + + @override + Widget build(BuildContext context) { List children = [ Center( child: Container( @@ -49,13 +60,39 @@ class _PostySummaryCardState extends State { child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - color: !systemNotReady ? Colors.green : Colors.red, + color: !isFailedResult() ? Colors.green : Colors.red, ), ), ), ), ), ]; + + if (isFailedResult()) { + if (widget.showAlertDialog) { + WidgetsBinding.instance.addPostFrameCallback((dur) { + showDialog( + context: context, + builder: + (BuildContext context) => AlertDialog( + title: const Text("POST Test failed"), + content: Text("Hello World"), + actions: [ + TextButton( + onPressed: () => {Navigator.pop(context, 'Ignore')}, + child: const Text("Ignore"), + ), + TextButton( + onPressed: () => {Navigator.pop(context, 'Re-run test')}, + child: const Text("Re-run test"), + ), + ], + ), + ); + }); + } + } + return BaseCard( width: 1, height: 1, @@ -64,23 +101,3 @@ class _PostySummaryCardState extends State { ); } } - -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/smart_dashboard/sendable_chooser_card.dart b/lib/widgets/smart_dashboard/sendable_chooser_card.dart new file mode 100644 index 0000000..ed58fc8 --- /dev/null +++ b/lib/widgets/smart_dashboard/sendable_chooser_card.dart @@ -0,0 +1,81 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:helm/ntutils.dart'; +import 'package:helm/widgets/base_card.dart'; +import 'package:nt4/nt4.dart'; + +class SendableChooserCard extends StatefulWidget { + const SendableChooserCard({super.key, this.chooserName = ""}); + + final String chooserName; + + @override + State createState() => _SendableChooserCard(); +} + +class _SendableChooserCard extends State { + List choices = []; + String? activeChoice = ""; + + NT4Subscription? optionsSub; + NT4Subscription? activeSub; + NT4Topic? activePub; + + @override + void initState() { + super.initState(); + + final String ntTopic = "/SmartDashboard/${widget.chooserName}"; + final String ntOptionsTopic = "$ntTopic/options"; + final String ntActiveTopic = "$ntTopic/selected"; + + optionsSub = NTUtils().subscribe(ntOptionsTopic); + optionsSub!.listen((data) { + setState(() { + data as List; + choices.clear(); + for (var d in data) { + choices.add(d as String); + } + }); + }); + + activeSub = NTUtils().subscribe(ntActiveTopic); + activeSub!.listen((data) { + setState(() { + data as String; + activeChoice = data; + }); + }); + + activePub = NTUtils().getPublishTopic(ntActiveTopic, NT4TypeStr.typeStr); + } + + @override + Widget build(BuildContext context) { + return BaseCard( + width: 2, + height: 2, + cardTitle: "Chooser", + children: [ + RadioGroup( + groupValue: activeChoice, + onChanged: + (value) => setState(() { + activeChoice = value; + NTUtils().publishSample(activePub!, value); + }), + child: Column( + children: + choices.map((choice) { + return ListTile( + title: Text(choice), + leading: Radio(value: choice), + ); + }).toList(), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/text_card.dart b/lib/widgets/text_card.dart index 6c63bf2..4b34329 100644 --- a/lib/widgets/text_card.dart +++ b/lib/widgets/text_card.dart @@ -24,7 +24,13 @@ class TextCard extends StatefulWidget { class _TextCardState extends State { String _val = ""; - _TextCardState() {} + NT4Subscription? sub; + @override + void initState() { + super.initState(); + sub = NTUtils().subscribe(widget.ntTopic); + sub!.listen((data) => setVal(data.toString())); + } void setVal(String newVal) { setState(() { @@ -34,8 +40,6 @@ class _TextCardState extends State { @override Widget build(BuildContext context) { - NT4Subscription sub = NTUtils().subscribe(widget.ntTopic); - sub.listen((data) => setVal(data.toString())); return BaseCard( cardTitle: widget.cardTitle, width: widget.width,