import 'dart:convert'; import 'dart:html'; import 'dart:typed_data'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:colorize_logger/colorize_logger.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; import 'package:http/http.dart' as http; import 'package:sprintf/sprintf.dart'; import 'package:ustest/Services/DeviceService.dart'; class DeviceInfoView extends StatefulWidget { DeviceInfoView(); @override _DeviceInfoViewState createState() => _DeviceInfoViewState(); } class _DeviceInfoViewState extends State<DeviceInfoView> { late List<File> _files; String _uniqueCode = "xxxxxx"; String _token = "xxxxxx"; String _examCode = ""; final _filesController = TextEditingController(); final _connectTextController = TextEditingController( text: '{"jsonrpc": "2.0",' '"method": "ConnectAsync",' '"params": [{' '"DeviceUniqueCode": "4914352a75f645279f6b4ad6764cc288",' '"Password": "4914352a75f645279f6b4ad6764cc288",' '"DeviceModel": "E30",' '"DeviceType": "US",' '"SoftwareVersion": "1.11.0",' '"SystemVersion": "2.2.27.5",' '"CPUModel": "I5-10400",' '"Description": "自测设备",' '"Name": "ZCSB",' '"Organ": "Organization_20220608060137EVEo3p",' '"SystemLanguage": "Chinese"' '}],' '"id": 1 }'); final _channel = WebSocketChannel.connect( Uri.parse('ws://192.168.6.20:9301?token=8557855B64474684B45294B75B1EDBA1'), ); @override void initState() { getDeviceInfo(); super.initState(); } void getDeviceInfo() async { try { var request = _connectTextController.text; var service = GetIt.instance.get<DeviceService>(); var device = await service.getDeviceDetail(request); setState(() { _uniqueCode = device.uniqueCode; _token = device.token; }); } catch (ex) { print("initState ex" + ex.toString()); } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Container( padding: const EdgeInsets.fromLTRB(20, 30, 20, 20), child: Column(children: [ Padding( padding: EdgeInsets.all(8.0), child: Row( children: [ Expanded( child: TextField( controller: _connectTextController, keyboardType: TextInputType.multiline, textInputAction: TextInputAction.newline, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: "请求数据"), minLines: 1, maxLines: 5, )), TextButton( onPressed: onConnect, child: const Text("Connect")) ], ), ), Padding( padding: EdgeInsets.all(8.0), child: Row( children: [Text("超声机设备Id"), Text("Device12345678")], mainAxisAlignment: MainAxisAlignment.spaceBetween, )), Padding( padding: EdgeInsets.all(8.0), child: Row( children: [Text("超声机唯一码"), Text(_uniqueCode)], mainAxisAlignment: MainAxisAlignment.spaceBetween, )), Padding( padding: EdgeInsets.all(8.0), child: Row( children: [ Text("Token"), Expanded( child: Text( _token, )) ], mainAxisAlignment: MainAxisAlignment.spaceBetween, )), Padding( padding: EdgeInsets.all(8.0), child: Row( children: [ Text("创建检查"), Text(_examCode), ], mainAxisAlignment: MainAxisAlignment.spaceBetween, )), Padding( padding: EdgeInsets.all(8.0), child: Row( children: [ Expanded( child: TextField( restorationId: "file", keyboardType: TextInputType.text, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: "请选择一个vid和一个对应缩略图jpg文件"), maxLength: 1, controller: _filesController, ), ), TextButton( onPressed: _onBrowse, child: Text("Browse"), ) ], )), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton.icon( onPressed: onCreateExamPressed, icon: Icon(Icons.add), label: Text("开始检查")), TextButton.icon( onPressed: onUploadPressed, icon: Icon(Icons.upload_file), label: Text("上传图像")), TextButton.icon( onPressed: onFinshExanPressed, icon: Icon(Icons.done_all), label: Text("结束检查")) ], ), StreamBuilder( stream: _channel.stream, builder: (context, snapshot) { var message = ""; // var message = // snapshot.hasData ? '${snapshot.data}' : 'no message'; if (snapshot.hasData) { var uint8Array = Uint8List.fromList(snapshot.data as List<int>); var byteData = uint8Array.buffer.asByteData(); var byteDataLength = byteData.lengthInBytes; var messageConentList = Uint8List.view(uint8Array.buffer, 0, byteDataLength); var messageConentLength = messageConentList.length; var messageConent = Uint8List.fromList(messageConentList); var messageText = Utf8Decoder().convert(messageConent); Map<String, dynamic> messageObject = jsonDecode(messageText); //通知类型区分 if (messageObject["NotificationType"] as int == NotificationTypeEnum.ConnectionNotification.index) { var connectionNotification = ConnectionNotification.fromJson(messageObject); print( "connectionNotification.NotificationType:${connectionNotification.notificationType}"); } else if (messageObject["NotificationType"] as int == NotificationTypeEnum.DisconnectNotification.index) { var disconnectNotification = DisconnectNotification.fromJson(messageObject); print( "disconnectNotification.NotificationType:${disconnectNotification.notificationType}"); } message = messageText; } return new Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: new Text(message), ); }, ) ]))), ); } @override void dispose() { //_channel.sink.close(); super.dispose(); } void onConnect() async { try { var request = _connectTextController.text; var service = GetIt.instance.get<DeviceService>(); var device = await service.connectAsync(request); setState(() { _uniqueCode = device.uniqueCode; _token = device.token; }); } catch (ex) { print("_token ex" + ex.toString()); } } void _onBrowse() { var uploadInput = new FileUploadInputElement(); uploadInput.multiple = true; //uploadInput.accept = '*.vid'; uploadInput.click(); uploadInput.onChange.listen( (changeEvent) { _files = uploadInput.files!; for (var i = 0; i < _files.length; i++) { _filesController.text += _files[i].name + ';'; } }, ); } void _onProgress(ProgressEvent e) { print("upload progress is" + e.total.toString()); } Future<String> uploadFileToSever(File file) async { var url = ''; try { print('File Name:' + file.name); var client = http.Client(); var body = sprintf( '{"jsonrpc": "2.0", "method": "GetAuthorizationAsync", "params": [{"Token": "%s", "FileName": "%s", "ApplicantTypeEnum": 1 }], "id": 1 }', [_token, file.name]); final response = await client.post( Uri.parse('http://192.168.6.20:8303/IStorageService'), body: body); print('GetAuthorizationAsync response.body' + response.body); final parsed = jsonDecode(response.body); var authorization = parsed['result']['Authorization'].toString(); url = parsed['result']['StorageUrl'].toString(); print('GetAuthorizationAsync result' + authorization); final headers = {"Authorization": authorization}; var formData = new FormData(); formData.appendBlob('file', file); await HttpRequest.request(url, method: 'PUT', sendData: formData, requestHeaders: headers, onProgress: _onProgress) .then((value) { var data = value; var responseText = data.responseText!; final parsed = jsonDecode(responseText); var reuslt = parsed['IsSuccess']; Logger.info( "status:" + data.status.toString() + ",isSuccess:" + reuslt); }).catchError((onError) { Logger.error(onError); }); } catch (ex) { print(ex); } return url; } void onUploadPressed() async { if (_examCode == "") { showToast("请先开始一个检查"); return; } var privewUrl = ''; var vidUrl = ''; for (var i = 0; i < _files.length; i++) { var name = _files[i].name; if (name.endsWith(".VID")) { vidUrl = await uploadFileToSever(_files[i]); Logger.info("Vid File upload to " + vidUrl); } else { privewUrl = await uploadFileToSever(_files[i]); Logger.info("Preview File upload to " + privewUrl); } } var client = http.Client(); //update data var body = sprintf( '{"jsonrpc": "2.0", "method": "UploadExamDataAsync", "params": [{"Token": "%s", "ExamCode": "%s", "PreviewFileToken": "%s", "FileToken": "%s","Application": "腹部", "PatientScanType": "Routine" }], "id": 1 }', [_token, _examCode, privewUrl, vidUrl]); print('UploadExamData http.Client()' + body); final response2 = await client.post( Uri.parse('http://192.168.6.20:8303/IRemedicalService'), body: body); print('UploadExamData response.body' + response2.body); final parsed2 = jsonDecode(response2.body); var resutlt = parsed2['result'].toString(); print('UploadExamData result ' + resutlt); } void onCreateExamPressed() async { var client = http.Client(); var body = sprintf( '{"jsonrpc": "2.0", "method": "CreateExamInfoAsync", "params": [{"Token": "%s", "PatientType": "Person", "PatientInfo": [{"Name":"張三","IdentityCard":"320525198806110078","Sex":"2","Age":"34"}] }], "id": 1 }', [_token]); print('CreateExamInfo http.Client()' + body); final response = await client.post( Uri.parse('http://192.168.6.20:8303/IRemedicalService'), body: body); print('CreateExamInfo response.body' + response.body); final parsed = jsonDecode(response.body); var examCode = parsed['result']['ExamCode'].toString(); print('CreateExamInfo result examCode' + examCode); setState(() { _examCode = examCode; }); //client.close(); } void onFinshExanPressed() async { if (_examCode == "") { showToast("No exam to finish"); return; } var client = http.Client(); try { var body = sprintf( '{"jsonrpc": "2.0", "method": "DeviceFinishExamAsync", "params": [{"Token": "%s", "Records":["%s"]}], "id": 1 }', [_token, _examCode]); print('FinishExam http.Client()' + body); final response = await client.post( Uri.parse('http://192.168.6.20:8303/IRemedicalService'), body: body); print('FinishExam response.body' + response.body); final parsed = jsonDecode(response.body); var result = parsed['result'] as bool; print('FinishExam result examCode' + result.toString()); setState(() { _examCode = ""; }); //client.close(); } catch (ex) { print('FinishExam.to ex' + ex.toString()); } } void showToast(String msg) { Fluttertoast.showToast( msg: msg, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, timeInSecForIosWeb: 1, backgroundColor: Colors.red[500], textColor: Colors.white, fontSize: 16.0); } } enum NotificationTypeEnum { /// <summary> /// Unknown|0| 未知 /// </summary> Unknown, /// <summary> /// ChatMsgNotification|1| 聊天通知 /// </summary> ChatMsgNotification, /// <summary> /// TokenReplacedNotification|2| 账号被替换登出通知 /// </summary> TokenReplacedNotification, /// <summary> /// DisconnectNotification| 3|与服务器断开连接通知 /// </summary> DisconnectNotification, /// <summary> /// ConnectionNotification| 4| 与服务器已连接通知 /// </summary> ConnectionNotification, /// <summary> /// ExamRecordsFinishedNotification| 5 | 完成检查通知 /// </summary> ExamRecordsFinishedNotification, /// <summary> /// RejectApplyConsultationNotification| 6 | 拒绝预约申请的通知 /// </summary> RejectApplyConsultationNotification, /// <summary> /// CancelInvitingInLiveConsultationNotification| 7 | 取消会诊过程中邀请其他成员的通知 /// </summary> CancelInvitingInLiveConsultationNotification, /// <summary> /// InviteInLiveConsultationNotification| 8 | 会诊过程中邀请其他成员的通知 /// </summary> InviteInLiveConsultationNotification, /// <summary> /// InviteInLiveConsultationNotification| 9 | 会诊开始前提醒的通知 /// </summary> ConsultationRemindNotification, /// <summary> /// PasswordExpiredWarningNotification| 10 | 用户密码过期预警通知 /// </summary> PasswordExpiredWarningNotification, /// <summary> /// InviteLiveConsultationNotification| 11 | 开始会诊的通知 /// </summary> InviteLiveConsultationNotification, /// <summary> /// AcceptLiveConsultationNotification| 12 | 接受会诊的通知 /// </summary> AcceptLiveConsultationNotification, /// <summary> /// RejectLiveConsultationNotification| 13 | 拒绝会诊的通知 /// </summary> RejectLiveConsultationNotification, /// <summary> /// InviteLiveConsultationToDeviceNotification| 14 | 开始会诊通知 to 设备端 /// </summary> InviteLiveConsultationToDeviceNotification, /// <summary> /// CancelLiveConsultationNotification| 15 | 取消会诊通知 /// </summary> CancelLiveConsultationNotification, /// <summary> /// CloseLiveConsultationNotification| 16 | 关闭会诊通知 /// </summary> CloseLiveConsultationNotification, /// <summary> /// JoinLiveConsultationNotification| 17 | 进入会诊通知 /// </summary> JoinLiveConsultationNotification, /// <summary> /// NetworkErrConsultationNotification| 18 | 网络质量不佳会诊通知 /// </summary> NetworkErrConsultationNotification, /// <summary> /// LeaveConsultationNotification| 19 | 离开会诊通知 /// </summary> LeaveConsultationNotification, /// <summary> /// JoinInLiveConsultationNotification| 20 | 会诊中加入房间 /// </summary> JoinInLiveConsultationNotification, /// <summary> /// RejectLiveConsultationNotification| 21 | 拒绝会诊的通知 /// </summary> RejectInviteLiveConsultationNotification, /// <summary> /// ApplyConsultationNotification| 22 | 会诊申请通知 /// </summary> ApplyConsultationNotification, /// <summary> /// ApprovalApplyConsultationNotification| 23 | 批准申请会诊通知 /// </summary> ApprovalApplyConsultationNotification, /// <summary> /// InviteeConsultationNotification| 24 | 会诊受邀请人通知 /// </summary> InviteeConsultationNotification, /// <summary> /// InviteeApproveApplyConsultationNotification| 25 | 会诊受邀请参与人同意通知 /// </summary> InviteeApproveApplyConsultationNotification, /// <summary> /// InviteeRejectApplyConsultationNotification| 26 | 会诊受邀请参与人拒绝通知 /// </summary> InviteeRejectApplyConsultationNotification, /// <summary> /// MuteLiveConsultationNotification| 27 | 开启关闭静音 /// </summary> MuteLiveConsultationNotification, /// <summary> /// SwitchLiveConsultationVideoNotification| 28 | 开启关闭视频 /// </summary> SwitchLiveConsultationVideoNotification, /// <summary> /// HeartRateJoinConsultationNotification| 29 | 会诊心跳,进入房间 /// </summary> HeartRateJoinConsultationNotification, /// <summary> /// HeartRateLeaveConsultationNotification| 30 | 会诊心跳,离开房间 /// </summary> HeartRateLeaveConsultationNotification, /// <summary> /// CloseLiveConsultationToDeviceNotification| 31 | 关闭会诊通知 to 设备端 /// </summary> CloseLiveConsultationToDeviceNotification, /// <summary> /// CancelLiveConsultationToDeviceNotification| 32 | 取消会诊通知 to 设备端 /// </summary> CancelLiveConsultationToDeviceNotification } class ConnectionNotification { NotificationTypeEnum notificationType; ConnectionNotification({ this.notificationType = NotificationTypeEnum.Unknown, }); factory ConnectionNotification.fromJson(Map<String, dynamic> map) { return ConnectionNotification( notificationType: NotificationTypeEnum.values .firstWhere((e) => e.index == map['NotificationType']), ); } } class DisconnectNotification { NotificationTypeEnum notificationType; DisconnectNotification({ this.notificationType = NotificationTypeEnum.Unknown, }); factory DisconnectNotification.fromJson(Map<String, dynamic> map) { return DisconnectNotification( notificationType: NotificationTypeEnum.values .firstWhere((e) => e.index == map['NotificationType']), ); } }