New Widgets

This commit is contained in:
Tyler Wilcox 2026-03-21 10:12:53 -04:00
parent 7971bf8201
commit 5db20aa088
7 changed files with 203 additions and 63 deletions

View File

@ -54,17 +54,21 @@ class NTUtils {
} }
} }
NT4Subscription subscribe(String topic) { NT4Subscription subscribe(
NT4Subscription subscrip = _ntClient!.subscribe( String topic, {
topic, NT4SubscriptionOptions opts = const NT4SubscriptionOptions(),
NT4SubscriptionOptions(), }) {
); NT4Subscription subscrip = _ntClient!.subscribe(topic, opts);
return subscrip; return subscrip;
} }
void unsubscribe(NT4Subscription subscription) {
_ntClient!.unSubscribe(subscription);
}
final Map<String, NT4Topic> _nt4Topics = {}; final Map<String, NT4Topic> _nt4Topics = {};
NT4Topic getPublicTopic(String topic, String type) { NT4Topic getPublishTopic(String topic, String type) {
if (!_nt4Topics.containsKey(topic)) { if (!_nt4Topics.containsKey(topic)) {
NT4Topic top = _ntClient!.publishNewTopic(topic, type); NT4Topic top = _ntClient!.publishNewTopic(topic, type);
_nt4Topics[topic] = top; _nt4Topics[topic] = top;
@ -73,5 +77,9 @@ class NTUtils {
return _nt4Topics[topic]!; return _nt4Topics[topic]!;
} }
void publishSample(NT4Topic topic, dynamic sample) {
_ntClient!.addSample(topic, sample);
}
NTUtils._internal(); NTUtils._internal();
} }

View File

@ -3,6 +3,7 @@ import 'package:helm/ntutils.dart';
import 'package:helm/widgets/base_card.dart'; import 'package:helm/widgets/base_card.dart';
import 'package:helm/widgets/posty/posty_results_card.dart'; import 'package:helm/widgets/posty/posty_results_card.dart';
import 'package:helm/widgets/posty/posty_summary_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 'package:responsive_grid/responsive_grid.dart';
import '../widgets/text_card.dart'; import '../widgets/text_card.dart';
@ -36,7 +37,7 @@ class _DashboardState extends State<Dashboard> {
// fast, so that you can just rebuild anything that needs updating rather // fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets. // than having to individually change instances of widgets.
return DefaultTabController( return DefaultTabController(
length: 1, length: 2,
initialIndex: 0, initialIndex: 0,
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
@ -47,7 +48,12 @@ class _DashboardState extends State<Dashboard> {
// Here we take the value from the MyHomePage object that was created by // 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. // the App.build method, and use it to set our appbar title.
title: Text("Helm - $connectionState"), title: Text("Helm - $connectionState"),
bottom: TabBar(tabs: <Widget>[Tab(icon: Icon(Icons.home))]), bottom: TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.home)),
Tab(icon: Icon(Icons.checklist)),
],
),
), ),
body: TabBarView( body: TabBarView(
children: <Widget>[ children: <Widget>[
@ -88,6 +94,11 @@ class _DashboardState extends State<Dashboard> {
child: TextCard(cardTitle: "Blah"), child: TextCard(cardTitle: "Blah"),
), ),
ResponsiveGridCol(child: PostySummaryCard()), ResponsiveGridCol(child: PostySummaryCard()),
ResponsiveGridCol(
child: SendableChooserCard(
chooserName: "SendableChooser[0]",
),
),
], ],
), ),
), ),
@ -96,6 +107,7 @@ class _DashboardState extends State<Dashboard> {
), ),
], ],
), ),
Text("CheckList"),
], ],
), ),
), ),

View File

@ -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<String, dynamic> json)
: testName = json['testName'] as String,
subsystemName = json['subsystemName'] as String,
result = json['result'] as String,
failReason = json['failReason'] as String;
}

View File

@ -1,5 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:helm/widgets/posty/models/posty_results_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:helm/ntutils.dart'; import 'package:helm/ntutils.dart';
import 'package:helm/widgets/base_card.dart'; import 'package:helm/widgets/base_card.dart';
@ -14,15 +16,31 @@ class PostyResultsCard extends StatefulWidget {
class _PostyResultsCardState extends State<PostyResultsCard> { class _PostyResultsCardState extends State<PostyResultsCard> {
final String _ntTopic = "/Posty/results/post/resultsJson"; final String _ntTopic = "/Posty/results/post/resultsJson";
NT4Subscription? sub;
List<PostyResultModel> resultsData = []; List<PostyResultModel> resultsData = [];
_PostyResultsCardState() { @override
NT4Subscription sub = NTUtils().subscribe(_ntTopic); void dispose() {
sub.listen((data) { // 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(() { setState(() {
List<dynamic> tmp = jsonDecode(data.toString()); List<dynamic> tmp = jsonDecode(data.toString());
resultsData.clear();
for (dynamic json in tmp) { for (dynamic json in tmp) {
resultsData.add(PostyResultModel.fromJson(json)); resultsData.add(PostyResultModel.fromJson(json));
} }
@ -32,6 +50,7 @@ class _PostyResultsCardState extends State<PostyResultsCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print("Build");
List<Widget> children = []; List<Widget> children = [];
resultsData.sort((a, b) => a.subsystemName.compareTo(b.subsystemName)); resultsData.sort((a, b) => a.subsystemName.compareTo(b.subsystemName));
@ -59,23 +78,3 @@ class _PostyResultsCardState extends State<PostyResultsCard> {
); );
} }
} }
class PostyResultModel {
String testName = "";
String subsystemName = "";
String result = "";
String failReason = "";
PostyResultModel(
this.testName,
this.subsystemName,
this.result,
this.failReason,
);
PostyResultModel.fromJson(Map<String, dynamic> json)
: testName = json['testName'] as String,
subsystemName = json['subsystemName'] as String,
result = json['result'] as String,
failReason = json['failReason'] as String;
}

View File

@ -1,12 +1,16 @@
import 'dart:convert'; import 'dart:convert';
import 'package:helm/widgets/posty/models/posty_results_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:helm/ntutils.dart'; import 'package:helm/ntutils.dart';
import 'package:helm/widgets/base_card.dart'; import 'package:helm/widgets/base_card.dart';
import 'package:nt4/nt4.dart'; import 'package:nt4/nt4.dart';
class PostySummaryCard extends StatefulWidget { class PostySummaryCard extends StatefulWidget {
const PostySummaryCard({super.key}); const PostySummaryCard({super.key, this.showAlertDialog = false});
final bool showAlertDialog;
@override @override
State<StatefulWidget> createState() => _PostySummaryCardState(); State<StatefulWidget> createState() => _PostySummaryCardState();
@ -14,30 +18,37 @@ class PostySummaryCard extends StatefulWidget {
class _PostySummaryCardState extends State<PostySummaryCard> { class _PostySummaryCardState extends State<PostySummaryCard> {
final String _ntTopic = "/Posty/results/post/resultsJson"; final String _ntTopic = "/Posty/results/post/resultsJson";
NT4Subscription? sub;
List<PostyResultModel> resultsData = []; List<PostyResultModel> resultsData = [];
_PostySummaryCardState() {}
@override @override
Widget build(BuildContext context) { void initState() {
NT4Subscription sub = NTUtils().subscribe(_ntTopic); super.initState();
sub.listen((data) { sub = NTUtils().subscribe(_ntTopic);
sub!.listen((data) {
setState(() { setState(() {
List<dynamic> tmp = jsonDecode(data.toString()); List<dynamic> tmp = jsonDecode(data.toString());
resultsData.clear();
for (dynamic json in tmp) { for (dynamic json in tmp) {
resultsData.add(PostyResultModel.fromJson(json)); resultsData.add(PostyResultModel.fromJson(json));
} }
}); });
}); });
}
bool isFailedResult() {
bool systemNotReady = false; bool systemNotReady = false;
for (PostyResultModel result in resultsData) { for (PostyResultModel result in resultsData) {
systemNotReady |= result.result == "FAILED"; systemNotReady |= result.result == "FAILED";
} }
return systemNotReady;
}
@override
Widget build(BuildContext context) {
List<Widget> children = [ List<Widget> children = [
Center( Center(
child: Container( child: Container(
@ -49,13 +60,39 @@ class _PostySummaryCardState extends State<PostySummaryCard> {
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, 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<String>(
context: context,
builder:
(BuildContext context) => AlertDialog(
title: const Text("POST Test failed"),
content: Text("Hello World"),
actions: <Widget>[
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( return BaseCard(
width: 1, width: 1,
height: 1, height: 1,
@ -64,23 +101,3 @@ class _PostySummaryCardState extends State<PostySummaryCard> {
); );
} }
} }
class PostyResultModel {
String testName = "";
String subsystemName = "";
String result = "";
String failReason = "";
PostyResultModel(
this.testName,
this.subsystemName,
this.result,
this.failReason,
);
PostyResultModel.fromJson(Map<String, dynamic> json)
: testName = json['testName'] as String,
subsystemName = json['subsystemName'] as String,
result = json['result'] as String,
failReason = json['failReason'] as String;
}

View File

@ -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<StatefulWidget> createState() => _SendableChooserCard();
}
class _SendableChooserCard extends State<SendableChooserCard> {
List<String> 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<Object?>;
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<String>(
groupValue: activeChoice,
onChanged:
(value) => setState(() {
activeChoice = value;
NTUtils().publishSample(activePub!, value);
}),
child: Column(
children:
choices.map((choice) {
return ListTile(
title: Text(choice),
leading: Radio<String>(value: choice),
);
}).toList(),
),
),
],
);
}
}

View File

@ -24,7 +24,13 @@ class TextCard extends StatefulWidget {
class _TextCardState extends State<TextCard> { class _TextCardState extends State<TextCard> {
String _val = ""; 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) { void setVal(String newVal) {
setState(() { setState(() {
@ -34,8 +40,6 @@ class _TextCardState extends State<TextCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
NT4Subscription sub = NTUtils().subscribe(widget.ntTopic);
sub.listen((data) => setVal(data.toString()));
return BaseCard( return BaseCard(
cardTitle: widget.cardTitle, cardTitle: widget.cardTitle,
width: widget.width, width: widget.width,