mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
new: detail status page
This commit is contained in:
@@ -199,7 +199,7 @@ enum StatusCmdType {
|
|||||||
tempType,
|
tempType,
|
||||||
tempVal,
|
tempVal,
|
||||||
host,
|
host,
|
||||||
;
|
diskio;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cmds for linux server
|
/// Cmds for linux server
|
||||||
@@ -216,6 +216,7 @@ const _statusCmds = [
|
|||||||
'cat /sys/class/thermal/thermal_zone*/type',
|
'cat /sys/class/thermal/thermal_zone*/type',
|
||||||
'cat /sys/class/thermal/thermal_zone*/temp',
|
'cat /sys/class/thermal/thermal_zone*/temp',
|
||||||
'hostname',
|
'hostname',
|
||||||
|
'cat /proc/diskstats',
|
||||||
];
|
];
|
||||||
|
|
||||||
enum DockerCmdType {
|
enum DockerCmdType {
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
|
import 'package:toolbox/core/extension/numx.dart';
|
||||||
|
import 'package:toolbox/data/model/server/time_seq.dart';
|
||||||
|
|
||||||
import '../../res/misc.dart';
|
import '../../res/misc.dart';
|
||||||
|
|
||||||
class Disk {
|
class Disk {
|
||||||
final String path;
|
final String dev;
|
||||||
final String loc;
|
final String mount;
|
||||||
final int usedPercent;
|
final int usedPercent;
|
||||||
final String used;
|
final String used;
|
||||||
final String size;
|
final String size;
|
||||||
final String avail;
|
final String avail;
|
||||||
|
|
||||||
const Disk({
|
const Disk({
|
||||||
required this.path,
|
required this.dev,
|
||||||
required this.loc,
|
required this.mount,
|
||||||
required this.usedPercent,
|
required this.usedPercent,
|
||||||
required this.used,
|
required this.used,
|
||||||
required this.size,
|
required this.size,
|
||||||
@@ -18,6 +21,80 @@ class Disk {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DiskIO extends TimeSeq<DiskIOPiece> {
|
||||||
|
DiskIO(super.pre, super.now);
|
||||||
|
|
||||||
|
(String?, String?) getReadSpeed(String dev) {
|
||||||
|
final pres = this.pre.where(
|
||||||
|
(element) => element.dev == dev.replaceFirst('/dev/', ''),
|
||||||
|
);
|
||||||
|
final nows = this.now.where(
|
||||||
|
(element) => element.dev == dev.replaceFirst('/dev/', ''),
|
||||||
|
);
|
||||||
|
if (pres.isEmpty || nows.isEmpty) return (null, null);
|
||||||
|
final pre = pres.first;
|
||||||
|
final now = nows.first;
|
||||||
|
final sectorsRead = now.sectorsRead - pre.sectorsRead;
|
||||||
|
final sectorsWrite = now.sectorsWrite - pre.sectorsWrite;
|
||||||
|
final time = now.time - pre.time;
|
||||||
|
final read = '${(sectorsRead / time * 512).convertBytes}/s';
|
||||||
|
final write = '${(sectorsWrite / time * 512).convertBytes}/s';
|
||||||
|
return (read, write);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw:
|
||||||
|
// 254 0 vda 584193 186416 40419294 845790 5024458 2028159 92899586 6997559 0 5728372 8143590 0 0 0 0 2006112 300240
|
||||||
|
// 254 1 vda1 584029 186416 40412734 845668 5024453 2028159 92899586 6997558 0 5728264 7843226 0 0 0 0 0 0
|
||||||
|
// 11 0 sr0 36 0 280 49 0 0 0 0 0 56 49 0 0 0 0 0 0
|
||||||
|
// 7 0 loop0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 1 loop1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 2 loop2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 3 loop3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 4 loop4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 5 loop5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 6 loop6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
// 7 7 loop7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
static List<DiskIOPiece> parse(String raw, int time) {
|
||||||
|
final lines = raw.split('\n');
|
||||||
|
if (lines.isEmpty) return [];
|
||||||
|
final items = <DiskIOPiece>[];
|
||||||
|
for (var item in lines) {
|
||||||
|
item = item.trim();
|
||||||
|
if (item.isEmpty) continue;
|
||||||
|
final vals = item.split(Miscs.blankReg);
|
||||||
|
if (vals.length < 10) continue;
|
||||||
|
try {
|
||||||
|
items.add(DiskIOPiece(
|
||||||
|
dev: vals[2],
|
||||||
|
sectorsRead: int.parse(vals[5]),
|
||||||
|
sectorsWrite: int.parse(vals[9]),
|
||||||
|
time: time,
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DiskIOPiece extends TimeSeqIface<DiskIOPiece> {
|
||||||
|
final String dev;
|
||||||
|
final int sectorsRead;
|
||||||
|
final int sectorsWrite;
|
||||||
|
final int time;
|
||||||
|
|
||||||
|
DiskIOPiece({
|
||||||
|
required this.dev,
|
||||||
|
required this.sectorsRead,
|
||||||
|
required this.sectorsWrite,
|
||||||
|
required this.time,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool same(DiskIOPiece other) => dev == other.dev;
|
||||||
|
}
|
||||||
|
|
||||||
List<Disk> parseDisk(String raw) {
|
List<Disk> parseDisk(String raw) {
|
||||||
final list = <Disk>[];
|
final list = <Disk>[];
|
||||||
final items = raw.split('\n');
|
final items = raw.split('\n');
|
||||||
@@ -38,8 +115,8 @@ List<Disk> parseDisk(String raw) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
list.add(Disk(
|
list.add(Disk(
|
||||||
path: vals[0],
|
dev: vals[0],
|
||||||
loc: vals[5],
|
mount: vals[5],
|
||||||
usedPercent: int.parse(vals[4].replaceFirst('%', '')),
|
usedPercent: int.parse(vals[4].replaceFirst('%', '')),
|
||||||
used: vals[2],
|
used: vals[2],
|
||||||
size: vals[1],
|
size: vals[1],
|
||||||
@@ -62,9 +139,9 @@ List<Disk> parseDisk(String raw) {
|
|||||||
/// the fps may lower than 60.
|
/// the fps may lower than 60.
|
||||||
Disk? findRootDisk(List<Disk> disks) {
|
Disk? findRootDisk(List<Disk> disks) {
|
||||||
if (disks.isEmpty) return null;
|
if (disks.isEmpty) return null;
|
||||||
final roots = disks.where((element) => element.loc == '/');
|
final roots = disks.where((element) => element.mount == '/');
|
||||||
if (roots.isEmpty) {
|
if (roots.isEmpty) {
|
||||||
final sysRoots = disks.where((element) => element.loc == '/sysroot');
|
final sysRoots = disks.where((element) => element.mount == '/sysroot');
|
||||||
if (sysRoots.isEmpty) {
|
if (sysRoots.isEmpty) {
|
||||||
return disks.first;
|
return disks.first;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class ServerStatus {
|
|||||||
Temperatures temps;
|
Temperatures temps;
|
||||||
SystemType system;
|
SystemType system;
|
||||||
String? failedInfo;
|
String? failedInfo;
|
||||||
|
DiskIO diskIO;
|
||||||
|
|
||||||
ServerStatus({
|
ServerStatus({
|
||||||
required this.cpu,
|
required this.cpu,
|
||||||
@@ -31,6 +32,7 @@ class ServerStatus {
|
|||||||
required this.swap,
|
required this.swap,
|
||||||
required this.temps,
|
required this.temps,
|
||||||
required this.system,
|
required this.system,
|
||||||
|
required this.diskIO,
|
||||||
this.failedInfo,
|
this.failedInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,10 @@ Future<ServerStatus> getStatus(ServerStatusUpdateReq req) async {
|
|||||||
Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
||||||
final segments = req.segments;
|
final segments = req.segments;
|
||||||
|
|
||||||
|
final time = int.tryParse(StatusCmdType.time.find(segments)) ??
|
||||||
|
DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final time = int.parse(StatusCmdType.time.find(segments));
|
|
||||||
final net = parseNetSpeed(StatusCmdType.net.find(segments), time);
|
final net = parseNetSpeed(StatusCmdType.net.find(segments), time);
|
||||||
req.ss.netSpeed.update(net);
|
req.ss.netSpeed.update(net);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
@@ -101,6 +103,13 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.parse.warning(e, s);
|
Loggers.parse.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final diskio = DiskIO.parse(StatusCmdType.diskio.find(segments), time);
|
||||||
|
req.ss.diskIO.update(diskio);
|
||||||
|
} catch (e, s) {
|
||||||
|
Loggers.parse.warning(e, s);
|
||||||
|
}
|
||||||
return req.ss;
|
return req.ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class BuildData {
|
|||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 618;
|
static const int build = 618;
|
||||||
static const String engine = "3.13.8";
|
static const String engine = "3.13.8";
|
||||||
static const String buildAt = "2023-10-30 17:18:16";
|
static const String buildAt = "2023-10-31 15:59:28";
|
||||||
static const int modifications = 5;
|
static const int modifications = 7;
|
||||||
static const int script = 23;
|
static const int script = 23;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ class Miscs {
|
|||||||
/// RegExp for number
|
/// RegExp for number
|
||||||
static final numReg = RegExp(r'\s{1,}');
|
static final numReg = RegExp(r'\s{1,}');
|
||||||
|
|
||||||
|
static final blankReg = RegExp(r'\s+');
|
||||||
|
|
||||||
/// RegExp for password request
|
/// RegExp for password request
|
||||||
static final pwdRequestWithUserReg = RegExp(r'\[sudo\] password for (.+):');
|
static final pwdRequestWithUserReg = RegExp(r'\[sudo\] password for (.+):');
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class InitStatus {
|
|||||||
uptime: '',
|
uptime: '',
|
||||||
disk: [
|
disk: [
|
||||||
const Disk(
|
const Disk(
|
||||||
path: '/',
|
dev: '/',
|
||||||
loc: '/',
|
mount: '/',
|
||||||
usedPercent: 0,
|
usedPercent: 0,
|
||||||
used: '0',
|
used: '0',
|
||||||
size: '0',
|
size: '0',
|
||||||
@@ -63,5 +63,6 @@ class InitStatus {
|
|||||||
),
|
),
|
||||||
system: SystemType.linux,
|
system: SystemType.linux,
|
||||||
temps: Temperatures(),
|
temps: Temperatures(),
|
||||||
|
diskIO: DiskIO([], []),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import 'package:toolbox/core/extension/context/common.dart';
|
|||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:toolbox/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/core/extension/order.dart';
|
import 'package:toolbox/core/extension/order.dart';
|
||||||
import 'package:toolbox/data/model/server/cpu.dart';
|
import 'package:toolbox/data/model/server/cpu.dart';
|
||||||
|
import 'package:toolbox/data/model/server/disk.dart';
|
||||||
import 'package:toolbox/data/model/server/net_speed.dart';
|
import 'package:toolbox/data/model/server/net_speed.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/model/server/system.dart';
|
import 'package:toolbox/data/model/server/system.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:toolbox/data/res/store.dart';
|
||||||
|
import 'package:toolbox/view/widget/expand_tile.dart';
|
||||||
import 'package:toolbox/view/widget/server_func_btns.dart';
|
import 'package:toolbox/view/widget/server_func_btns.dart';
|
||||||
import 'package:toolbox/view/widget/value_notifier.dart';
|
import 'package:toolbox/view/widget/value_notifier.dart';
|
||||||
|
|
||||||
@@ -306,64 +308,55 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDiskView(ServerStatus ss) {
|
Widget _buildDiskView(ServerStatus ss) {
|
||||||
final disk = ss.disk;
|
final disks = ss.disk;
|
||||||
disk.removeWhere((e) {
|
disks.removeWhere((e) {
|
||||||
for (final ingorePath in Stores.setting.diskIgnorePath.fetch()) {
|
for (final ingorePath in Stores.setting.diskIgnorePath.fetch()) {
|
||||||
if (e.path.startsWith(ingorePath)) return true;
|
if (e.dev.startsWith(ingorePath)) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
final children = disk
|
final children =
|
||||||
.map((disk) => Padding(
|
List.generate(disks.length, (idx) => _buildDiskItem(disks[idx], ss));
|
||||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'${disk.usedPercent}% of ${disk.size}',
|
|
||||||
style: UIs.textSize11,
|
|
||||||
textScaleFactor: _textFactor,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
disk.path,
|
|
||||||
style: UIs.textSize11,
|
|
||||||
textScaleFactor: _textFactor,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
_buildProgress(disk.usedPercent.toDouble())
|
|
||||||
],
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
return CardX(
|
return CardX(
|
||||||
Padding(
|
ExpandTile(
|
||||||
padding: UIs.roundRectCardPadding,
|
title: Text('Disk'),
|
||||||
child: Column(
|
leading: Icon(Icons.storage, size: 17),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
initiallyExpanded: children.length <= 7,
|
||||||
children: children,
|
children: children,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDiskItem(Disk disk, ServerStatus ss) {
|
||||||
|
final (read, write) = ss.diskIO.getReadSpeed(disk.dev);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(
|
||||||
|
disk.dev,
|
||||||
|
style: UIs.textSize13Bold,
|
||||||
|
textScaleFactor: _textFactor,
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 3, horizontal: 17),
|
||||||
|
subtitle: Text(
|
||||||
|
'${disk.usedPercent}% of ${disk.size}\n↑ $read | ↓ $write',
|
||||||
|
style: UIs.textSize11,
|
||||||
|
textScaleFactor: _textFactor,
|
||||||
|
),
|
||||||
|
trailing: SizedBox(
|
||||||
|
height: 37,
|
||||||
|
width: 37,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
value: disk.usedPercent / 100,
|
||||||
|
strokeWidth: 7,
|
||||||
|
backgroundColor: DynamicColors.progress.resolve(context),
|
||||||
|
valueColor: AlwaysStoppedAnimation(primaryColor),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNetView(ServerStatus ss) {
|
Widget _buildNetView(ServerStatus ss) {
|
||||||
return CardX(
|
|
||||||
Padding(
|
|
||||||
padding: UIs.roundRectCardPadding,
|
|
||||||
child: ValueBuilder(
|
|
||||||
listenable: _netSortType,
|
|
||||||
build: () {
|
|
||||||
final ns = ss.netSpeed;
|
final ns = ss.netSpeed;
|
||||||
final children = <Widget>[
|
final children = <Widget>[];
|
||||||
_buildNetSpeedTop(),
|
|
||||||
const Divider(
|
|
||||||
height: 7,
|
|
||||||
)
|
|
||||||
];
|
|
||||||
if (ns.devices.isEmpty) {
|
if (ns.devices.isEmpty) {
|
||||||
children.add(Center(
|
children.add(Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -376,141 +369,68 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
devices.sort(_netSortType.value.getSortFunc(ns));
|
devices.sort(_netSortType.value.getSortFunc(ns));
|
||||||
children.addAll(devices.map((e) => _buildNetSpeedItem(ns, e)));
|
children.addAll(devices.map((e) => _buildNetSpeedItem(ns, e)));
|
||||||
}
|
}
|
||||||
return Column(
|
return ValueBuilder(
|
||||||
|
listenable: _netSortType,
|
||||||
|
build: () {
|
||||||
|
return CardX(
|
||||||
|
ExpandTile(
|
||||||
|
title: Text('Net'),
|
||||||
|
leading: Icon(Icons.device_hub, size: 17),
|
||||||
|
initiallyExpanded: children.length <= 7,
|
||||||
children: children,
|
children: children,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildNetSpeedTop() {
|
|
||||||
const icon = Icon(Icons.arrow_downward, size: 13);
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 3),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
child: _netSortType.value.isDevice
|
|
||||||
? const Row(
|
|
||||||
children: [
|
|
||||||
Text('Iface'),
|
|
||||||
icon,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: const Text('Iface'),
|
|
||||||
onTap: () => _netSortType.value = _NetSortType.device,
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
child: _netSortType.value.isIn
|
|
||||||
? const Row(
|
|
||||||
children: [
|
|
||||||
Text('Recv'),
|
|
||||||
icon,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: const Text('Recv'),
|
|
||||||
onTap: () => _netSortType.value = _NetSortType.recv,
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
child: _netSortType.value.isOut
|
|
||||||
? const Row(
|
|
||||||
children: [
|
|
||||||
Text('Trans'),
|
|
||||||
icon,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: const Text('Trans'),
|
|
||||||
onTap: () => _netSortType.value = _NetSortType.trans,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNetSpeedItem(NetSpeed ns, String device) {
|
Widget _buildNetSpeedItem(NetSpeed ns, String device) {
|
||||||
final width = (_media.size.width - 34 - 34) / 3;
|
return ListTile(
|
||||||
return Padding(
|
title: Text(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: width,
|
|
||||||
child: Text(
|
|
||||||
device,
|
device,
|
||||||
style: UIs.textSize11,
|
style: UIs.textSize13Bold,
|
||||||
textScaleFactor: _textFactor,
|
textScaleFactor: _textFactor,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
subtitle: Text(
|
||||||
SizedBox(
|
'${ns.sizeIn(device: device)} | ${ns.sizeOut(device: device)}',
|
||||||
width: width,
|
|
||||||
child: Text(
|
|
||||||
'${ns.speedIn(device: device)} | ${ns.sizeIn(device: device)}',
|
|
||||||
style: UIs.textSize11,
|
style: UIs.textSize11,
|
||||||
textAlign: TextAlign.center,
|
textScaleFactor: _textFactor,
|
||||||
textScaleFactor: 0.87 * _textFactor,
|
|
||||||
),
|
),
|
||||||
),
|
trailing: SizedBox(
|
||||||
SizedBox(
|
width: 170,
|
||||||
width: width,
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'${ns.speedOut(device: device)} | ${ns.sizeOut(device: device)}',
|
'↑ ${ns.speedOut(device: device)}\n↓ ${ns.speedIn(device: device)}',
|
||||||
style: UIs.textSize11,
|
textAlign: TextAlign.end,
|
||||||
textAlign: TextAlign.right,
|
|
||||||
textScaleFactor: 0.87 * _textFactor,
|
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTemperature(ServerStatus ss) {
|
Widget _buildTemperature(ServerStatus ss) {
|
||||||
final temps = ss.temps;
|
if (ss.temps.isEmpty) {
|
||||||
if (temps.isEmpty) {
|
|
||||||
return UIs.placeholder;
|
return UIs.placeholder;
|
||||||
}
|
}
|
||||||
final List<Widget> children = [
|
|
||||||
const Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.device_hub, size: 17),
|
|
||||||
Icon(Icons.ac_unit, size: 17),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 3),
|
|
||||||
child: Divider(height: 7),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
children.addAll(temps.devices.map((key) => Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
key,
|
|
||||||
style: UIs.textSize11,
|
|
||||||
textScaleFactor: _textFactor,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${temps.get(key)}°C',
|
|
||||||
style: UIs.textSize11,
|
|
||||||
textScaleFactor: _textFactor,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)));
|
|
||||||
return CardX(
|
return CardX(
|
||||||
Padding(
|
ExpandTile(
|
||||||
padding: UIs.roundRectCardPadding,
|
title: Text('Temperature'),
|
||||||
child: Column(children: children),
|
leading: const Icon(Icons.ac_unit, size: 17),
|
||||||
|
initiallyExpanded: ss.temps.devices.length <= 7,
|
||||||
|
children: ss.temps.devices
|
||||||
|
.map((key) => _buildTemperatureItem(key, ss.temps.get(key)))
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildTemperatureItem(String key, double? val) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(key, style: UIs.textSize13Bold),
|
||||||
|
trailing: Text('${val?.toStringAsFixed(1)}°C'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildAnimatedText(Key key, String text, TextStyle style) {
|
Widget _buildAnimatedText(Key key, String text, TextStyle style) {
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 277),
|
duration: const Duration(milliseconds: 277),
|
||||||
|
|||||||
@@ -82,6 +82,6 @@ Overlay 3.0T 1.4t 1.6T 48%/Share/CacheDev1_data/Container/Container-SATA/LIB/DOC
|
|||||||
3.0T 1.4T 1.6T 48% /mnt/snapshot/1/10016
|
3.0T 1.4T 1.6T 48% /mnt/snapshot/1/10016
|
||||||
''';
|
''';
|
||||||
final disks = parseDisk(raw);
|
final disks = parseDisk(raw);
|
||||||
print(disks.map((e) => '${e.loc} ${e.used}').join('\n'));
|
print(disks.map((e) => '${e.mount} ${e.used}').join('\n'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user