urinalysis.dart 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. import 'dart:async';
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:get/get.dart';
  5. import 'package:vitalapp/architecture/app_parameters.dart';
  6. import 'package:vitalapp/architecture/utils/prompt_box.dart';
  7. import 'package:vitalapp/components/button.dart';
  8. import 'package:vitalapp/components/dialog_input.dart';
  9. import 'package:vitalapp/managers/device_controller_manager.dart';
  10. import 'package:vitalapp/pages/mappers/urine.dart';
  11. import 'package:vitalapp/pages/medical/widgets/device_status_position.dart';
  12. import 'package:vnote_device_plugin/consts/types.dart';
  13. import 'package:vitalapp/managers/interfaces/models/device.dart';
  14. import 'package:vitalapp/pages/medical/widgets/exam_card.dart';
  15. import 'package:vitalapp/pages/medical/controller.dart';
  16. import 'package:vitalapp/pages/medical/models/worker.dart';
  17. import 'package:vitalapp/pages/medical/widgets/device_status.dart';
  18. import 'package:vnote_device_plugin/devices/urine.dart';
  19. import 'package:vnote_device_plugin/models/exams/urine.dart';
  20. import 'package:fis_common/logger/logger.dart';
  21. /// 尿常规
  22. class Urinalysis extends StatefulWidget {
  23. const Urinalysis({
  24. super.key,
  25. });
  26. @override
  27. State<Urinalysis> createState() => _ExamUrinalysisState();
  28. }
  29. class _ExamUrinalysisState extends State<Urinalysis> {
  30. var controller = Get.find<MedicalController>();
  31. bool get isPureSoftwareMode => AppParameters.data.isPureSoftwareMode;
  32. DeviceControllerManager? urinaly;
  33. UrineDeviceWorker? worker;
  34. UrineExamData? urineExamData;
  35. bool isConnectFail = false;
  36. bool isAutoTesting = false;
  37. int errorCount = 0;
  38. String? get deviceType => MedicalController.typeConvertMap[DeviceTypes.URINE];
  39. WorkerStatus connectStatus = WorkerStatus.connecting;
  40. List<Map<String, String>> urinalysis = [
  41. {"name": '尿白细胞', "key": 'LEU'},
  42. {"name": '红细胞/潜血', "key": 'BLD'},
  43. {"name": '尿亚硝酸盐', "key": 'NIT'},
  44. {"name": '酮体', "key": 'KET'},
  45. {"name": '尿胆原', "key": 'UBG'},
  46. {"name": '胆红素', "key": 'BIL'},
  47. {"name": '尿蛋白', "key": 'PRO'},
  48. {"name": '葡萄糖', "key": 'GLU'},
  49. {"name": '酸碱度', "key": 'PH'},
  50. {"name": '尿比重', "key": 'SG'},
  51. {"name": '维生素C', "key": 'VC'},
  52. ];
  53. Map _value = {};
  54. @override
  55. void initState() {
  56. WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
  57. initUrinlysis();
  58. currentDevice();
  59. controller.changePatient.addListener(_onPatientChange);
  60. });
  61. super.initState();
  62. }
  63. Future<void> currentDevice() async {
  64. DeviceModel? device = await controller.getDevice(DeviceTypes.URINE);
  65. if (device == null) {
  66. connectStatus = WorkerStatus.unboundDevice;
  67. return;
  68. }
  69. urinaly =
  70. DeviceControllerManager(DeviceTypes.URINE, device.model, device.mac);
  71. worker = urinaly!.worker as UrineDeviceWorker;
  72. connectStatus = urinaly!.connectStatus;
  73. loadListeners();
  74. worker!.connect();
  75. }
  76. Future<void> initUrinlysis() async {
  77. await initData();
  78. }
  79. void loadListeners() {
  80. worker!.successEvent.addListener(_onSuccess);
  81. worker!.connectErrorEvent.addListener(_onConnectFail);
  82. worker!.connectedEvent.addListener(_onConnectSuccess);
  83. worker!.disconnectedEvent.addListener(_onDisconnected);
  84. // worker!.errorEvent.addListener(_onError);
  85. }
  86. Future<void> initData() async {
  87. if (controller.diagnosisDataValue.isNotEmpty &&
  88. controller.diagnosisDataValue.containsKey(deviceType)) {
  89. _value = controller.diagnosisDataValue[deviceType!];
  90. } else {
  91. _value = {};
  92. }
  93. setState(() {});
  94. }
  95. void releaseListeners() {
  96. if (worker != null) {
  97. worker!.connectErrorEvent.removeListener(_onConnectFail);
  98. worker!.connectedEvent.removeListener(_onConnectSuccess);
  99. worker!.successEvent.removeListener(_onSuccess);
  100. worker!.disconnectedEvent.removeListener(_onDisconnected);
  101. // worker!.errorEvent.removeListener(_onError);
  102. }
  103. }
  104. /// 尝试重连
  105. Future<void> tryReconnect() async {
  106. //await worker.disconnect();
  107. //5s后在执行,是为了防止重连与断开事件一直相互触发
  108. Future.delayed(const Duration(seconds: 5), () async {
  109. //如果5秒后仍然是未连接状态,则重试连接
  110. if (connectStatus == WorkerStatus.disconnected ||
  111. connectStatus == WorkerStatus.connectionFailed ||
  112. connectStatus == WorkerStatus.connecting) {
  113. await disconnect();
  114. await connect();
  115. }
  116. });
  117. }
  118. Future<void> disconnect() async {
  119. await worker?.disconnect();
  120. }
  121. Future<void> connect() async {
  122. await worker?.connect();
  123. }
  124. @override
  125. void dispose() async {
  126. controller.changePatient.removeListener(_onPatientChange);
  127. urinaly?.dispose();
  128. releaseListeners();
  129. disconnect();
  130. worker?.dispose();
  131. super.dispose();
  132. }
  133. void _onSuccess(_, UrineExamData e) {
  134. setState(() {
  135. isAutoTesting = false;
  136. urineExamData = e;
  137. controller.diagnosisDataValue[deviceType!] =
  138. UrineExamDataMapper.convertUrineExamDataToMap(urineExamData!);
  139. _value = controller.diagnosisDataValue[deviceType!];
  140. controller.saveCachedRecord();
  141. connectStatus = WorkerStatus.connected;
  142. });
  143. }
  144. void _onConnectFail(sender, e) {
  145. print('连接设备失败');
  146. logger.i("设备连接失败:${worker!.mac}");
  147. if (errorCount < 3) {
  148. errorCount++;
  149. tryReconnect();
  150. } else {
  151. isConnectFail = true;
  152. }
  153. connectStatus = WorkerStatus.connectionFailed;
  154. setState(() {});
  155. }
  156. void _onConnectSuccess(sender, e) {
  157. logger.i("设备连接成功:${worker!.mac}");
  158. isConnectFail = false;
  159. errorCount = 0;
  160. connectStatus = WorkerStatus.connected;
  161. setState(() {});
  162. }
  163. void _onError(sender, e) async {
  164. PromptBox.toast("测试失败,请确定放好试纸并且试纸已浸湿尿液");
  165. isAutoTesting = false;
  166. setState(() {});
  167. }
  168. void _onDisconnected(sender, e) {
  169. print('设备连接中断 errorCount:$errorCount');
  170. logger.i("设备连接中断:${worker!.mac} errorCount:$errorCount");
  171. if (errorCount < 3) {
  172. errorCount++;
  173. tryReconnect();
  174. } else {
  175. isConnectFail = true;
  176. }
  177. connectStatus = WorkerStatus.disconnected;
  178. setState(() {});
  179. }
  180. @override
  181. Widget build(BuildContext context) {
  182. return Stack(
  183. children: [
  184. _buildUrinalysis(),
  185. if (!isPureSoftwareMode) ...[
  186. if (!isConnectFail)
  187. DeviceStatusPosition(
  188. deviceStatus: DeviceStatus(connectStatus: connectStatus),
  189. )
  190. else
  191. _buildErrorButton(),
  192. ],
  193. ],
  194. );
  195. }
  196. Widget _buildErrorButton() {
  197. return DeviceStatusPosition(
  198. deviceStatus: Row(
  199. children: [
  200. const Text(
  201. '请确认设备是否启动',
  202. style: TextStyle(fontSize: 24, color: Colors.red),
  203. ),
  204. IconButton(
  205. onPressed: () {
  206. tryReconnect();
  207. setState(() {
  208. connectStatus = WorkerStatus.connecting;
  209. isConnectFail = false;
  210. });
  211. },
  212. icon: const Icon(Icons.refresh),
  213. iconSize: 32,
  214. ),
  215. ],
  216. ),
  217. );
  218. }
  219. Widget _buildUrinalysis() {
  220. return ExamCard(
  221. title: '尿常规',
  222. content: Container(
  223. alignment: Alignment.center,
  224. padding: const EdgeInsets.only(
  225. bottom: 20,
  226. right: 30,
  227. left: 40,
  228. ),
  229. constraints: const BoxConstraints(minHeight: 50),
  230. child: GridView.count(
  231. shrinkWrap: true,
  232. childAspectRatio: kIsWeb ? 4.5 : 4,
  233. crossAxisCount: 2, // 列数为2,即两列布局
  234. children: urinalysis.map((item) {
  235. return _buildUrineItem(item);
  236. }).toList(),
  237. ),
  238. ),
  239. );
  240. }
  241. Widget _buildUrineItem(Map<String, String> urine) {
  242. return InkWell(
  243. onTap: () {
  244. _input(urine['name']!, urine['key']!);
  245. },
  246. child: Row(
  247. mainAxisAlignment: MainAxisAlignment.start,
  248. crossAxisAlignment: CrossAxisAlignment.center,
  249. children: [
  250. Container(
  251. alignment: Alignment.centerLeft,
  252. width: 100,
  253. height: 55,
  254. child: Column(
  255. crossAxisAlignment: CrossAxisAlignment.start,
  256. children: [
  257. Text(
  258. urine['key']!,
  259. style: const TextStyle(
  260. fontSize: 22,
  261. color: Colors.black,
  262. ),
  263. ),
  264. Text(
  265. '(${urine['name']!})',
  266. // text: _value.isEmpty ? '--' : _value,
  267. textAlign: TextAlign.center,
  268. style: const TextStyle(
  269. fontSize: 16,
  270. ),
  271. ),
  272. ],
  273. ),
  274. ),
  275. Container(
  276. height: 55,
  277. width: 140,
  278. alignment: Alignment.centerLeft,
  279. child: Text(
  280. _value[urine['key']!] ?? '',
  281. // text: _value.isEmpty ? '--' : _value,
  282. maxLines: 2,
  283. style: const TextStyle(
  284. fontSize: 30,
  285. ),
  286. ),
  287. )
  288. ],
  289. ),
  290. );
  291. }
  292. Future<void> _input(String name, String key) async {
  293. String? result = await VDialogInput(
  294. title: name,
  295. initialValue: _value[key],
  296. ).show();
  297. if (result?.isNotEmpty ?? false) {
  298. _value[key] = result ?? '';
  299. }
  300. controller.diagnosisDataValue[deviceType!] = _value;
  301. controller.saveCachedRecord();
  302. // widget.urinalysis(_value);
  303. setState(() {});
  304. }
  305. void _onPatientChange(Object sender, String e) {
  306. setState(() {
  307. _value = {};
  308. controller.diagnosisDataValue[deviceType!] = {};
  309. });
  310. }
  311. }