DeviceInfo.dart 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import 'dart:convert';
  2. import 'dart:html';
  3. import 'dart:typed_data';
  4. import 'package:web_socket_channel/web_socket_channel.dart';
  5. import 'package:fluttertoast/fluttertoast.dart';
  6. import 'package:colorize_logger/colorize_logger.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:get_it/get_it.dart';
  10. import 'package:http/http.dart' as http;
  11. import 'package:sprintf/sprintf.dart';
  12. import 'package:ustest/Services/DeviceService.dart';
  13. class DeviceInfoView extends StatefulWidget {
  14. DeviceInfoView();
  15. @override
  16. _DeviceInfoViewState createState() => _DeviceInfoViewState();
  17. }
  18. class _DeviceInfoViewState extends State<DeviceInfoView> {
  19. late List<File> _files;
  20. String _uniqueCode = "xxxxxx";
  21. String _token = "xxxxxx";
  22. String _examCode = "";
  23. final _filesController = TextEditingController();
  24. final _connectTextController = TextEditingController(
  25. text: '{"jsonrpc": "2.0",'
  26. '"method": "ConnectAsync",'
  27. '"params": [{'
  28. '"DeviceUniqueCode": "4914352a75f645279f6b4ad6764cc288",'
  29. '"Password": "4914352a75f645279f6b4ad6764cc288",'
  30. '"DeviceModel": "E30",'
  31. '"DeviceType": "US",'
  32. '"SoftwareVersion": "1.11.0",'
  33. '"SystemVersion": "2.2.27.5",'
  34. '"CPUModel": "I5-10400",'
  35. '"Description": "自测设备",'
  36. '"Name": "ZCSB",'
  37. '"Organ": "Organization_20220608060137EVEo3p",'
  38. '"SystemLanguage": "Chinese"'
  39. '}],'
  40. '"id": 1 }');
  41. final _channel = WebSocketChannel.connect(
  42. Uri.parse('ws://192.168.6.20:9301?token=8557855B64474684B45294B75B1EDBA1'),
  43. );
  44. @override
  45. void initState() {
  46. getDeviceInfo();
  47. super.initState();
  48. }
  49. void getDeviceInfo() async {
  50. try {
  51. var request = _connectTextController.text;
  52. var service = GetIt.instance.get<DeviceService>();
  53. var device = await service.getDeviceDetail(request);
  54. setState(() {
  55. _uniqueCode = device.uniqueCode;
  56. _token = device.token;
  57. });
  58. } catch (ex) {
  59. print("initState ex" + ex.toString());
  60. }
  61. }
  62. @override
  63. Widget build(BuildContext context) {
  64. return Scaffold(
  65. body: Center(
  66. child: Container(
  67. padding: const EdgeInsets.fromLTRB(20, 30, 20, 20),
  68. child: Column(children: [
  69. Padding(
  70. padding: EdgeInsets.all(8.0),
  71. child: Row(
  72. children: [
  73. Expanded(
  74. child: TextField(
  75. controller: _connectTextController,
  76. keyboardType: TextInputType.multiline,
  77. textInputAction: TextInputAction.newline,
  78. decoration: InputDecoration(
  79. border: const OutlineInputBorder(),
  80. labelText: "请求数据"),
  81. minLines: 1,
  82. maxLines: 5,
  83. )),
  84. TextButton(
  85. onPressed: onConnect, child: const Text("Connect"))
  86. ],
  87. ),
  88. ),
  89. Padding(
  90. padding: EdgeInsets.all(8.0),
  91. child: Row(
  92. children: [Text("超声机设备Id"), Text("Device12345678")],
  93. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  94. )),
  95. Padding(
  96. padding: EdgeInsets.all(8.0),
  97. child: Row(
  98. children: [Text("超声机唯一码"), Text(_uniqueCode)],
  99. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  100. )),
  101. Padding(
  102. padding: EdgeInsets.all(8.0),
  103. child: Row(
  104. children: [
  105. Text("Token"),
  106. Expanded(
  107. child: Text(
  108. _token,
  109. ))
  110. ],
  111. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  112. )),
  113. Padding(
  114. padding: EdgeInsets.all(8.0),
  115. child: Row(
  116. children: [
  117. Text("创建检查"),
  118. Text(_examCode),
  119. ],
  120. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  121. )),
  122. Padding(
  123. padding: EdgeInsets.all(8.0),
  124. child: Row(
  125. children: [
  126. Expanded(
  127. child: TextField(
  128. restorationId: "file",
  129. keyboardType: TextInputType.text,
  130. decoration: InputDecoration(
  131. border: const OutlineInputBorder(),
  132. labelText: "请选择一个vid和一个对应缩略图jpg文件"),
  133. maxLength: 1,
  134. controller: _filesController,
  135. ),
  136. ),
  137. TextButton(
  138. onPressed: _onBrowse,
  139. child: Text("Browse"),
  140. )
  141. ],
  142. )),
  143. Row(
  144. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  145. children: [
  146. TextButton.icon(
  147. onPressed: onCreateExamPressed,
  148. icon: Icon(Icons.add),
  149. label: Text("开始检查")),
  150. TextButton.icon(
  151. onPressed: onUploadPressed,
  152. icon: Icon(Icons.upload_file),
  153. label: Text("上传图像")),
  154. TextButton.icon(
  155. onPressed: onFinshExanPressed,
  156. icon: Icon(Icons.done_all),
  157. label: Text("结束检查"))
  158. ],
  159. ),
  160. StreamBuilder(
  161. stream: _channel.stream,
  162. builder: (context, snapshot) {
  163. var message = "";
  164. // var message =
  165. // snapshot.hasData ? '${snapshot.data}' : 'no message';
  166. if (snapshot.hasData) {
  167. var uint8Array =
  168. Uint8List.fromList(snapshot.data as List<int>);
  169. var byteData = uint8Array.buffer.asByteData();
  170. var byteDataLength = byteData.lengthInBytes;
  171. var messageConentList =
  172. Uint8List.view(uint8Array.buffer, 0, byteDataLength);
  173. var messageConentLength = messageConentList.length;
  174. var messageConent = Uint8List.fromList(messageConentList);
  175. var messageText = Utf8Decoder().convert(messageConent);
  176. Map<String, dynamic> messageObject =
  177. jsonDecode(messageText);
  178. //通知类型区分
  179. if (messageObject["NotificationType"] as int ==
  180. NotificationTypeEnum.ConnectionNotification.index) {
  181. var connectionNotification =
  182. ConnectionNotification.fromJson(messageObject);
  183. print(
  184. "connectionNotification.NotificationType:${connectionNotification.notificationType}");
  185. } else if (messageObject["NotificationType"] as int ==
  186. NotificationTypeEnum.DisconnectNotification.index) {
  187. var disconnectNotification =
  188. DisconnectNotification.fromJson(messageObject);
  189. print(
  190. "disconnectNotification.NotificationType:${disconnectNotification.notificationType}");
  191. }
  192. message = messageText;
  193. }
  194. return new Padding(
  195. padding: const EdgeInsets.symmetric(vertical: 24.0),
  196. child: new Text(message),
  197. );
  198. },
  199. )
  200. ]))),
  201. );
  202. }
  203. @override
  204. void dispose() {
  205. //_channel.sink.close();
  206. super.dispose();
  207. }
  208. void onConnect() async {
  209. try {
  210. var request = _connectTextController.text;
  211. var service = GetIt.instance.get<DeviceService>();
  212. var device = await service.connectAsync(request);
  213. setState(() {
  214. _uniqueCode = device.uniqueCode;
  215. _token = device.token;
  216. });
  217. } catch (ex) {
  218. print("_token ex" + ex.toString());
  219. }
  220. }
  221. void _onBrowse() {
  222. var uploadInput = new FileUploadInputElement();
  223. uploadInput.multiple = true;
  224. //uploadInput.accept = '*.vid';
  225. uploadInput.click();
  226. uploadInput.onChange.listen(
  227. (changeEvent) {
  228. _files = uploadInput.files!;
  229. for (var i = 0; i < _files.length; i++) {
  230. _filesController.text += _files[i].name + ';';
  231. }
  232. },
  233. );
  234. }
  235. void _onProgress(ProgressEvent e) {
  236. print("upload progress is" + e.total.toString());
  237. }
  238. Future<String> uploadFileToSever(File file) async {
  239. var url = '';
  240. try {
  241. print('File Name:' + file.name);
  242. var client = http.Client();
  243. var body = sprintf(
  244. '{"jsonrpc": "2.0", "method": "GetAuthorizationAsync", "params": [{"Token": "%s", "FileName": "%s", "ApplicantTypeEnum": 1 }], "id": 1 }',
  245. [_token, file.name]);
  246. final response = await client.post(
  247. Uri.parse('http://192.168.6.20:8303/IStorageService'),
  248. body: body);
  249. print('GetAuthorizationAsync response.body' + response.body);
  250. final parsed = jsonDecode(response.body);
  251. var authorization = parsed['result']['Authorization'].toString();
  252. url = parsed['result']['StorageUrl'].toString();
  253. print('GetAuthorizationAsync result' + authorization);
  254. final headers = {"Authorization": authorization};
  255. var formData = new FormData();
  256. formData.appendBlob('file', file);
  257. await HttpRequest.request(url,
  258. method: 'PUT',
  259. sendData: formData,
  260. requestHeaders: headers,
  261. onProgress: _onProgress)
  262. .then((value) {
  263. var data = value;
  264. var responseText = data.responseText!;
  265. final parsed = jsonDecode(responseText);
  266. var reuslt = parsed['IsSuccess'];
  267. Logger.info(
  268. "status:" + data.status.toString() + ",isSuccess:" + reuslt);
  269. }).catchError((onError) {
  270. Logger.error(onError);
  271. });
  272. } catch (ex) {
  273. print(ex);
  274. }
  275. return url;
  276. }
  277. void onUploadPressed() async {
  278. if (_examCode == "") {
  279. showToast("请先开始一个检查");
  280. return;
  281. }
  282. var privewUrl = '';
  283. var vidUrl = '';
  284. for (var i = 0; i < _files.length; i++) {
  285. var name = _files[i].name;
  286. if (name.endsWith(".VID")) {
  287. vidUrl = await uploadFileToSever(_files[i]);
  288. Logger.info("Vid File upload to " + vidUrl);
  289. } else {
  290. privewUrl = await uploadFileToSever(_files[i]);
  291. Logger.info("Preview File upload to " + privewUrl);
  292. }
  293. }
  294. var client = http.Client();
  295. //update data
  296. var body = sprintf(
  297. '{"jsonrpc": "2.0", "method": "UploadExamDataAsync", "params": [{"Token": "%s", "ExamCode": "%s", "PreviewFileToken": "%s", "FileToken": "%s","Application": "腹部", "PatientScanType": "Routine" }], "id": 1 }',
  298. [_token, _examCode, privewUrl, vidUrl]);
  299. print('UploadExamData http.Client()' + body);
  300. final response2 = await client.post(
  301. Uri.parse('http://192.168.6.20:8303/IRemedicalService'),
  302. body: body);
  303. print('UploadExamData response.body' + response2.body);
  304. final parsed2 = jsonDecode(response2.body);
  305. var resutlt = parsed2['result'].toString();
  306. print('UploadExamData result ' + resutlt);
  307. }
  308. void onCreateExamPressed() async {
  309. var client = http.Client();
  310. var body = sprintf(
  311. '{"jsonrpc": "2.0", "method": "CreateExamInfoAsync", "params": [{"Token": "%s", "PatientType": "Person", "PatientInfo": [{"Name":"張三","IdentityCard":"320525198806110078","Sex":"2","Age":"34"}] }], "id": 1 }',
  312. [_token]);
  313. print('CreateExamInfo http.Client()' + body);
  314. final response = await client.post(
  315. Uri.parse('http://192.168.6.20:8303/IRemedicalService'),
  316. body: body);
  317. print('CreateExamInfo response.body' + response.body);
  318. final parsed = jsonDecode(response.body);
  319. var examCode = parsed['result']['ExamCode'].toString();
  320. print('CreateExamInfo result examCode' + examCode);
  321. setState(() {
  322. _examCode = examCode;
  323. });
  324. //client.close();
  325. }
  326. void onFinshExanPressed() async {
  327. if (_examCode == "") {
  328. showToast("No exam to finish");
  329. return;
  330. }
  331. var client = http.Client();
  332. try {
  333. var body = sprintf(
  334. '{"jsonrpc": "2.0", "method": "DeviceFinishExamAsync", "params": [{"Token": "%s", "Records":["%s"]}], "id": 1 }',
  335. [_token, _examCode]);
  336. print('FinishExam http.Client()' + body);
  337. final response = await client.post(
  338. Uri.parse('http://192.168.6.20:8303/IRemedicalService'),
  339. body: body);
  340. print('FinishExam response.body' + response.body);
  341. final parsed = jsonDecode(response.body);
  342. var result = parsed['result'] as bool;
  343. print('FinishExam result examCode' + result.toString());
  344. setState(() {
  345. _examCode = "";
  346. });
  347. //client.close();
  348. } catch (ex) {
  349. print('FinishExam.to ex' + ex.toString());
  350. }
  351. }
  352. void showToast(String msg) {
  353. Fluttertoast.showToast(
  354. msg: msg,
  355. toastLength: Toast.LENGTH_SHORT,
  356. gravity: ToastGravity.CENTER,
  357. timeInSecForIosWeb: 1,
  358. backgroundColor: Colors.red[500],
  359. textColor: Colors.white,
  360. fontSize: 16.0);
  361. }
  362. }
  363. enum NotificationTypeEnum {
  364. /// <summary>
  365. /// Unknown|0| 未知
  366. /// </summary>
  367. Unknown,
  368. /// <summary>
  369. /// ChatMsgNotification|1| 聊天通知
  370. /// </summary>
  371. ChatMsgNotification,
  372. /// <summary>
  373. /// TokenReplacedNotification|2| 账号被替换登出通知
  374. /// </summary>
  375. TokenReplacedNotification,
  376. /// <summary>
  377. /// DisconnectNotification| 3|与服务器断开连接通知
  378. /// </summary>
  379. DisconnectNotification,
  380. /// <summary>
  381. /// ConnectionNotification| 4| 与服务器已连接通知
  382. /// </summary>
  383. ConnectionNotification,
  384. /// <summary>
  385. /// ExamRecordsFinishedNotification| 5 | 完成检查通知
  386. /// </summary>
  387. ExamRecordsFinishedNotification,
  388. /// <summary>
  389. /// RejectApplyConsultationNotification| 6 | 拒绝预约申请的通知
  390. /// </summary>
  391. RejectApplyConsultationNotification,
  392. /// <summary>
  393. /// CancelInvitingInLiveConsultationNotification| 7 | 取消会诊过程中邀请其他成员的通知
  394. /// </summary>
  395. CancelInvitingInLiveConsultationNotification,
  396. /// <summary>
  397. /// InviteInLiveConsultationNotification| 8 | 会诊过程中邀请其他成员的通知
  398. /// </summary>
  399. InviteInLiveConsultationNotification,
  400. /// <summary>
  401. /// InviteInLiveConsultationNotification| 9 | 会诊开始前提醒的通知
  402. /// </summary>
  403. ConsultationRemindNotification,
  404. /// <summary>
  405. /// PasswordExpiredWarningNotification| 10 | 用户密码过期预警通知
  406. /// </summary>
  407. PasswordExpiredWarningNotification,
  408. /// <summary>
  409. /// InviteLiveConsultationNotification| 11 | 开始会诊的通知
  410. /// </summary>
  411. InviteLiveConsultationNotification,
  412. /// <summary>
  413. /// AcceptLiveConsultationNotification| 12 | 接受会诊的通知
  414. /// </summary>
  415. AcceptLiveConsultationNotification,
  416. /// <summary>
  417. /// RejectLiveConsultationNotification| 13 | 拒绝会诊的通知
  418. /// </summary>
  419. RejectLiveConsultationNotification,
  420. /// <summary>
  421. /// InviteLiveConsultationToDeviceNotification| 14 | 开始会诊通知 to 设备端
  422. /// </summary>
  423. InviteLiveConsultationToDeviceNotification,
  424. /// <summary>
  425. /// CancelLiveConsultationNotification| 15 | 取消会诊通知
  426. /// </summary>
  427. CancelLiveConsultationNotification,
  428. /// <summary>
  429. /// CloseLiveConsultationNotification| 16 | 关闭会诊通知
  430. /// </summary>
  431. CloseLiveConsultationNotification,
  432. /// <summary>
  433. /// JoinLiveConsultationNotification| 17 | 进入会诊通知
  434. /// </summary>
  435. JoinLiveConsultationNotification,
  436. /// <summary>
  437. /// NetworkErrConsultationNotification| 18 | 网络质量不佳会诊通知
  438. /// </summary>
  439. NetworkErrConsultationNotification,
  440. /// <summary>
  441. /// LeaveConsultationNotification| 19 | 离开会诊通知
  442. /// </summary>
  443. LeaveConsultationNotification,
  444. /// <summary>
  445. /// JoinInLiveConsultationNotification| 20 | 会诊中加入房间
  446. /// </summary>
  447. JoinInLiveConsultationNotification,
  448. /// <summary>
  449. /// RejectLiveConsultationNotification| 21 | 拒绝会诊的通知
  450. /// </summary>
  451. RejectInviteLiveConsultationNotification,
  452. /// <summary>
  453. /// ApplyConsultationNotification| 22 | 会诊申请通知
  454. /// </summary>
  455. ApplyConsultationNotification,
  456. /// <summary>
  457. /// ApprovalApplyConsultationNotification| 23 | 批准申请会诊通知
  458. /// </summary>
  459. ApprovalApplyConsultationNotification,
  460. /// <summary>
  461. /// InviteeConsultationNotification| 24 | 会诊受邀请人通知
  462. /// </summary>
  463. InviteeConsultationNotification,
  464. /// <summary>
  465. /// InviteeApproveApplyConsultationNotification| 25 | 会诊受邀请参与人同意通知
  466. /// </summary>
  467. InviteeApproveApplyConsultationNotification,
  468. /// <summary>
  469. /// InviteeRejectApplyConsultationNotification| 26 | 会诊受邀请参与人拒绝通知
  470. /// </summary>
  471. InviteeRejectApplyConsultationNotification,
  472. /// <summary>
  473. /// MuteLiveConsultationNotification| 27 | 开启关闭静音
  474. /// </summary>
  475. MuteLiveConsultationNotification,
  476. /// <summary>
  477. /// SwitchLiveConsultationVideoNotification| 28 | 开启关闭视频
  478. /// </summary>
  479. SwitchLiveConsultationVideoNotification,
  480. /// <summary>
  481. /// HeartRateJoinConsultationNotification| 29 | 会诊心跳,进入房间
  482. /// </summary>
  483. HeartRateJoinConsultationNotification,
  484. /// <summary>
  485. /// HeartRateLeaveConsultationNotification| 30 | 会诊心跳,离开房间
  486. /// </summary>
  487. HeartRateLeaveConsultationNotification,
  488. /// <summary>
  489. /// CloseLiveConsultationToDeviceNotification| 31 | 关闭会诊通知 to 设备端
  490. /// </summary>
  491. CloseLiveConsultationToDeviceNotification,
  492. /// <summary>
  493. /// CancelLiveConsultationToDeviceNotification| 32 | 取消会诊通知 to 设备端
  494. /// </summary>
  495. CancelLiveConsultationToDeviceNotification
  496. }
  497. class ConnectionNotification {
  498. NotificationTypeEnum notificationType;
  499. ConnectionNotification({
  500. this.notificationType = NotificationTypeEnum.Unknown,
  501. });
  502. factory ConnectionNotification.fromJson(Map<String, dynamic> map) {
  503. return ConnectionNotification(
  504. notificationType: NotificationTypeEnum.values
  505. .firstWhere((e) => e.index == map['NotificationType']),
  506. );
  507. }
  508. }
  509. class DisconnectNotification {
  510. NotificationTypeEnum notificationType;
  511. DisconnectNotification({
  512. this.notificationType = NotificationTypeEnum.Unknown,
  513. });
  514. factory DisconnectNotification.fromJson(Map<String, dynamic> map) {
  515. return DisconnectNotification(
  516. notificationType: NotificationTypeEnum.values
  517. .firstWhere((e) => e.index == map['NotificationType']),
  518. );
  519. }
  520. }