using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using Vinno.IUS.Common.Log; using Vinno.IUS.Common.Network.Leaf; using Vinno.IUS.Common.Network.Tcp; using Vinno.IUS.Common.Network.Transfer; using Vinno.IUS.Common.Utilities; using Vinno.vCloud.Common.FIS.AfterSales; using Vinno.vCloud.Common.FIS.FLYINSONOLogin; using Vinno.vCloud.Common.FIS.LiveVideos; using Vinno.vCloud.Common.FIS.Remedicals; using Vinno.vCloud.Common.Storage; using Vinno.vCloud.FIS.CrossPlatform.Common; using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo; using Vinno.vCloud.Protocol.Infrastructures; using Vinno.vCloud.Protocol.Messages; using Vinno.vCloud.Protocol.Messages.Client; using Vinno.vCloud.Protocol.Messages.Client.AfterSales; using Vinno.vCloud.Protocol.Messages.Client.AssignTermianl; using Vinno.vCloud.Protocol.Messages.Client.AssignTerminal; using Vinno.vCloud.Protocol.Messages.Client.Live; using Vinno.vCloud.Protocol.Messages.Client.Share; using Vinno.vCloud.Protocol.Messages.Client.Storage; using Vinno.vCloud.Protocol.Messages.Login; using Vinno.vCloud.Protocol.Messages.Terminal; using Vinno.vCloud.Protocol.Messages.Terminal.Login; namespace Vinno.vCloud.Common.FIS { internal class vCloudTerminal : IvCloudTerminal { private readonly Dictionary _features = new Dictionary(); private readonly ConnectionInfo _connectionInfo; private readonly string _deviceName; private readonly string _password; private readonly bool _isUserDefined; private readonly LoginSource _loginSource; private readonly string _url; private readonly int _usScreenWidth; private readonly int _usScreenHeight; private readonly int _connectionCheckCycle; private readonly int _heartRateCycle; private readonly bool _isUseOldTerminalSdk; private string _clientId; private TerminalStatus _status; private ClientLeaf _leaf; private ConnectionChecker _connectionChecker; private HeartRateKeeper _heartRateKeeper; private bool _disposed; private string _id; private string _sessionId; private int _reconnectCounter; /// /// Gets the global remedical working folder. /// internal static string WorkingFolder { get; private set; } /// /// Raised when the status is changed. /// public event EventHandler StatusChanged; /// /// Gets the unique id. /// public string UniqueId { get; private set; } /// /// /// Gets the status of the Terminal /// /// /// public TerminalStatus Status { get => _status; private set { if (_status != value) { _status = value; OnStatusChanged(); } } } public string Id => _id; public string TerminalName => _deviceName; public string TerminalMode => _connectionInfo.DeviceMode; public vCloudTerminal(ConnectionInfo connectionInfo, bool isUsedOldSdk = false) { _isUseOldTerminalSdk = isUsedOldSdk; _usScreenHeight = connectionInfo.USScreenHeight; _usScreenWidth = connectionInfo.USScreenWidth; _connectionInfo = connectionInfo; _deviceName = connectionInfo.Account.Name; _password = connectionInfo.Account.Password; _isUserDefined = connectionInfo.Account.IsUserDefined; _url = connectionInfo.ServerUrl; _loginSource = CommonParameter.Instance.IsSonopost ? LoginSource.WindowSonopost : LoginSource.UltrasoundMachine; _clientId = IdHelper.Generate(); //Set the default value 10 seconds. _connectionCheckCycle = 10; //Set the default value 3 minutes. _heartRateCycle = 3; _status = TerminalStatus.Offline; WorkingFolder = connectionInfo.FISFolder; DirectoryHelper.CreateDirectory(WorkingFolder); if (!_isUseOldTerminalSdk) { CreateLeaf(); CreateConnectionChecker(); if (!connectionInfo.IsOnlyUsedToRegister) { CPCombineSmartPushConfiguration.Instance.Initialize(connectionInfo.FISFolder); UploadHelper.GetAuthorization = GetAuthorization; } } } /// /// /// Get the feature instance by feature enum. /// /// The interface of the feature /// The feature type /// The instance of the feature /// /// If the feature doesn't exist, this method will return null. /// public T GetFeature(TerminalFeatureType featureType) where T : IFeature { if (_features.ContainsKey(featureType)) { var feature = _features[featureType]; if (feature is T t) { return t; } } return default(T); } /// /// Connect to server. /// internal void Connect(string uniqueId = "", string terminalId = "") { try { if (!_isUseOldTerminalSdk) { var loginResult = NewLogin(); if (!_isUserDefined && loginResult.Status == LoginStatus.WrongAccount) { if (string.IsNullOrEmpty(_connectionInfo.Organization)) { Status = TerminalStatus.OrganizationMissing; return; } if (RegisterAccount()) { NewLogin(); } else { Status = TerminalStatus.LoginFailed; } } } else { UniqueId = uniqueId; _id = terminalId; ReleaseFeatures(); UpdateFeatures(_connectionInfo.EnabledFeatures); Status = TerminalStatus.Logon; } } catch (Exception e) { Logger.WriteLineError($"Terminal {_deviceName} login url:{_url} failed {e}"); Status = TerminalStatus.LoginFailed; } } public bool Register() { return RegisterAccount(); } /// /// Register Account /// /// private bool RegisterAccount() { using (var request = MessagePool.GetMessage()) { request.TerminalName = _deviceName; request.TerminalModel = _connectionInfo.DeviceMode; request.TerminalPassword = _password; request.OrganizationName = _connectionInfo.Organization; request.IsLive = true; var addTerminalAccountServerResult = _leaf?.Send(request); var addTerminalAccountResult = ResultMessage.Convert(addTerminalAccountServerResult); if (addTerminalAccountResult == CCR.OK) { return true; } else { return false; } } } public void Disconnect() { if (!_isUseOldTerminalSdk) { var status = TerminalStatus.Logoff; try { SendStopPushStreamResult(); using (var request = MessagePool.GetMessage()) { request.Name = _deviceName; request.Password = _password; request.Source = _loginSource; request.IsUserDefinedAccount = _isUserDefined; var result = _leaf?.Send(request); var logoffResult = LogoffResult.Convert(result); switch (logoffResult.Status) { case LogoffStatus.Success: status = TerminalStatus.Logoff; break; case LogoffStatus.WrongAccount: status = TerminalStatus.WrongAccount; break; case LogoffStatus.WrongPassword: status = TerminalStatus.WrongPassword; break; } } } catch (Exception e) { Logger.WriteLineError($"Disconnect terminal {_deviceName} error {e}"); } finally { Release(); FLYINSONOUserManager.Instance.Clear(); Status = status; } } else { try { Release(); FLYINSONOUserManager.Instance.Clear(); Status = TerminalStatus.Logoff; } catch (Exception e) { Logger.WriteLineError($"Disconnect terminal with old Sdk {_deviceName} error {e}"); } } } public IEnumerable GetOrganizations(string keyWord) { using (var request = MessagePool.GetMessage()) { request.KeyWord = keyWord; var result = _leaf?.Send(request); var findOrganizationInfosResult = FindOrganizationInfosResult.Convert(result); if (findOrganizationInfosResult?.OrganizationInfos != null) { return findOrganizationInfosResult.OrganizationInfos.Select(i => i.Name); } } return new List(); } public bool IsTerminalOrganizationMissging() { return Status == TerminalStatus.OrganizationMissing; } public bool UpdatelTerminalOrganizationName(string organizationName, bool createNewOrganization) { using (var request = MessagePool.GetMessage()) { request.TerminalName = _deviceName; request.OrganizationName = organizationName; request.IsCreateNewOrganization = createNewOrganization; var result = _leaf?.Send(request); var updateTerminalResult = ResultMessage.Convert(result); if (updateTerminalResult == CCR.OK) { Logger.WriteLineInfo($"vCloudTerminal UpdatelTerminalOrganizationName Success,TerminalName:{_deviceName},OrganizationName:{organizationName},CreateNewOrganization:{createNewOrganization}"); return true; } else { Logger.WriteLineError($"vCloudTerminal UpdatelTerminalOrganizationName Failed,TerminalName:{_deviceName},OrganizationName:{organizationName},CreateNewOrganization:{createNewOrganization}"); return false; //update organization name fail } } } public string GetOrganizationName() { var organizationName = string.Empty; try { using (var request = MessagePool.GetMessage()) { request.TerminalName = _deviceName; var result = _leaf?.Send(request); if (result != null) { var findTerminalByNameResult = FindTerminalByNameResult.Convert(result); if (findTerminalByNameResult != null) { organizationName = findTerminalByNameResult.Termianl?.OrganizationName; } } } } catch (Exception ex) { Logger.WriteLineError($"Vcloud terminal get organization name by terminal failed:{ex}"); } return organizationName; } public IEnumerable GetReportImagesByPatientId(string patientId) { try { using (var request = MessagePool.GetMessage()) { request.PatientId = patientId; var language = ""; var lcid = CultureInfo.CurrentCulture.LCID; if (lcid == 0x804 || lcid == 0xc04 || lcid == 0x1404 || lcid == 0x1004 || lcid == 0x404) { language = "Chinese"; } else { language = "English"; } request.Language = language; var result = _leaf?.Send(request); if (result != null) { var getReportImageByPatientIdResult = GetShareReportImageByPatientIdResult.Convert(result); if (getReportImageByPatientIdResult != null) { var reportImages = getReportImageByPatientIdResult.ReportImages; return reportImages.Select(v => v.GetBytes()).ToList(); } } } } catch (Exception e) { Logger.WriteLineError($"vCloudTerminal GetReportByPatientId failed error {e}"); } return new List(); } /// /// Update enabled feature types. /// /// The enabled feature types. public void UpdateFeatures(IEnumerable enabledFeatureTypes) { CommonParameter.Instance.IsFeatureReleased = false; if (!_isUseOldTerminalSdk) { var removedFeatures = _features.Keys.Where(f => !enabledFeatureTypes.Contains(f)).ToList(); foreach (var feature in removedFeatures) { var disposable = _features[feature] as IDisposable; disposable?.Dispose(); _features.Remove(feature); } foreach (var enabledFeature in enabledFeatureTypes) { if (!_features.ContainsKey(enabledFeature)) { var feature = GetFeature(enabledFeature); _features.Add(enabledFeature, feature); } } } else { var removedFeatures = _features.Keys.Where(f => !enabledFeatureTypes.Contains(f)).ToList(); foreach (var feature in removedFeatures) { if (feature == TerminalFeatureType.Consultation) { var disposable = _features[feature] as IDisposable; disposable?.Dispose(); _features.Remove(feature); } } foreach (var enabledFeature in enabledFeatureTypes) { if (enabledFeature == TerminalFeatureType.Consultation) { if (!_features.ContainsKey(enabledFeature)) { var feature = GetFeature(enabledFeature); _features.Add(enabledFeature, feature); } } } } } public void Dispose() { if (!_disposed) { Release(); _disposed = true; } GC.SuppressFinalize(this); } private void SendStopPushStreamResult() { if (!string.IsNullOrWhiteSpace(_id)) { using (var request = MessagePool.GetMessage()) { request.TerminalId = _id; var result = _leaf?.Send(request); var ok = ResultMessage.Convert(result); if (ok == null || ok.ResultCode != 0) throw new InvalidOperationException("Send reply to server error."); } } } private string GetAuthorization(string fileName) { try { var authorization = string.Empty; var getAuthorizationRequest = new GetAuthorizationRequest(); getAuthorizationRequest.FileName = fileName; var storageResult = _leaf?.Send(getAuthorizationRequest); var getAuthorizationResult = GetAuthorizationResult.Convert(storageResult); if (getAuthorizationResult != null) { authorization = getAuthorizationResult.Authorization; } return authorization; } catch (Exception e) { throw new Exception($"get authentication form server error:{e}"); } } private NewTerminalLoginResult4 NewLogin() { using (var request = MessagePool.GetMessage()) { request.Name = _deviceName; request.Password = _password; request.SupportLive = true; request.ClientId = _clientId; request.IsUserDefinedAccount = _isUserDefined; request.Source = _loginSource; request.TerminalVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); var result = _leaf?.Send(request); var loginResult = NewTerminalLoginResult4.Convert(result); if (loginResult != null) { UniqueId = loginResult.TerminalUniqueId; ////TODO(FISJuly) //StorageType = loginResult.StorageType; HandleLoginResult(loginResult.Status, loginResult.AccountId, loginResult.AccountSessionId); } else { throw new InvalidOperationException("Login failed."); } return loginResult; } } private void HandleLoginResult(LoginStatus loginStatus, string accountId, string accountSessionId) { if (loginStatus == LoginStatus.Success) { Status = TerminalStatus.Logoning; _sessionId = accountSessionId; _id = accountId; CreateHeartRateKeeper(); ReleaseFeatures(); UpdateFeatures(_connectionInfo.EnabledFeatures); UpdateTerminalInfo(); _reconnectCounter = 0; Status = TerminalStatus.Logon; Logger.WriteLineInfo($"{_deviceName} Login url:{_url} successed, UniqueId:{UniqueId}"); } else if (loginStatus == LoginStatus.WrongPassword) { Status = TerminalStatus.WrongPassword; } else if (_isUserDefined && loginStatus == LoginStatus.WrongAccount) { Status = TerminalStatus.WrongAccount; } else { Logger.WriteLineWarn($"Login to server error:{loginStatus}"); } } private void UpdateTerminalInfo() { try { Logger.WriteLineInfo($"begin ChangeTerminalDeviceInfoRequest SerialNumber:{_connectionInfo.SerialNumber} ,USMode:{_connectionInfo.DeviceMode}, USCPU:{_connectionInfo.USCPU}, USOS:{_connectionInfo.USOS}, USSoftware:{_connectionInfo.SoftwareVersion}, "); if (CommonParameter.Instance.IsSonopost) { using (var request = MessagePool.GetMessage()) { request.TerminalId = _id; request.SerialNumber = _connectionInfo.SerialNumber; request.TerminalMode = _connectionInfo.DeviceMode; request.TerminalCPU = _connectionInfo.USCPU; request.TerminalOS = ""; request.SoftwareVersion = _connectionInfo.SoftwareVersion; request.IsSonopost = true; request.Capability = _deviceName; request.SonopostVerson = SonopostVersonEnum.Sonopost2; var result = _leaf?.Send(request); var changeTerminalDeviceInfoResult = ChangeTerminalDeviceInfoResult.Convert(result); if (changeTerminalDeviceInfoResult != null && changeTerminalDeviceInfoResult.IsSuccess) { Logger.WriteLineInfo($"ChangeTerminalDeviceInfoRequest success"); } else { Logger.WriteLineError($"ChangeTerminalDeviceInfoRequest failed: {changeTerminalDeviceInfoResult.Msg}"); } } } else { using (var request = MessagePool.GetMessage()) { request.TerminalId = _id; request.SerialNumber = _connectionInfo.SerialNumber; request.TerminalMode = _connectionInfo.DeviceMode; request.TerminalCPU = _connectionInfo.USCPU; request.TerminalOS = _connectionInfo.USOS; request.SoftwareVersion = _connectionInfo.SoftwareVersion; request.IsSonopost = false; request.Capability = _deviceName; var result = _leaf?.Send(request); var changeTerminalDeviceInfoResult = ChangeTerminalDeviceInfoResult.Convert(result); if (changeTerminalDeviceInfoResult != null && changeTerminalDeviceInfoResult.IsSuccess) { Logger.WriteLineInfo($"ChangeTerminalDeviceInfoRequest success"); } else { Logger.WriteLineError($"ChangeTerminalDeviceInfoRequest failed: {changeTerminalDeviceInfoResult.Msg}"); } } } } catch (Exception ex) { Logger.WriteLineError($"ChangeTerminalDeviceInfoRequest error: {ex}"); } } private void OnStatusChanged() { StatusChanged?.Invoke(this, EventArgs.Empty); } ////TODO(July):支里是否是超声机已在其他机器登录的原因,代码得看看 private void CreateLeaf() { _leaf = new ClientLeaf(new LeafIdContext(), LeafMode.Dual, new TcpCreator(_url), "Terminal connection:"); _leaf.MessageArrived += OnMessageArrived; if (!_leaf.Online) { _leaf.Close(); Status = TerminalStatus.Offline; } else { _leaf.RegisterSetAccountDataMessageFunc(SetAccountDataToMessage); Status = TerminalStatus.Online; } } private void DisposeLeaf() { try { if (_leaf != null) { _leaf.MessageArrived -= OnMessageArrived; _leaf.Close(); _leaf = null; } } catch (Exception ex) { Logger.WriteLineError($"VCloudTerminal DisposeLeaf Error:{ex}"); } } private void CreateHeartRateKeeper() { if (_heartRateKeeper == null) { _heartRateKeeper = new HeartRateKeeper(_sessionId, _leaf, _heartRateCycle); _heartRateKeeper.Start(); } } private void DisposeHeartRateKeeper() { try { if (_heartRateKeeper != null) { _heartRateKeeper.Stop(); _heartRateKeeper = null; } } catch (Exception ex) { Logger.WriteLineError($"VCloudTerminal DisposeHeartRateKeeper Error:{ex}"); } } private void CreateConnectionChecker() { if (_connectionChecker == null) { _connectionChecker = new ConnectionChecker(_leaf, _connectionCheckCycle); _connectionChecker.Offlined += ConnectionCheckerOnOfflined; _connectionChecker.Start(); } } private void DisposeConnectionChecker() { try { if (_connectionChecker != null) { _connectionChecker.Offlined -= ConnectionCheckerOnOfflined; _connectionChecker.Stop(); _connectionChecker = null; } } catch (Exception ex) { Logger.WriteLineError($"VCloudTerminal DisposeConnectionChecker Error:{ex}"); } } private void OnMessageArrived(object sender, Message e) { var connectionReplacedNotification = ConnectionReplacedNotification.Convert(e); if (connectionReplacedNotification != null) { HandleConnectionReplacedNotification(connectionReplacedNotification); } var connectNotification = ConnectNotification.Convert(e); if (connectNotification != null) { HandleConnectNotification(connectNotification); } } private void HandleConnectionReplacedNotification(ConnectionReplacedNotification connectionReplacedNotification) { Dispose(); Status = TerminalStatus.ReplacedNotification; } private void HandleConnectNotification(ConnectNotification connectNotification) { try { var afterSales = GetFeature(TerminalFeatureType.AfterSales); if (afterSales != null) { afterSales.UpdateAfterSalesInfo(connectNotification.UserId, connectNotification.UserName); using (var processReslut = MessagePool.GetMessage()) { processReslut.TargetId = connectNotification.UserId; processReslut.RunTaskType = TaskType.Connect; processReslut.RunTaskStatus = TaskStatus.Finished; processReslut.Detail = DetailType.Connected; processReslut.Percent = 1; processReslut.Source = ProcessSource.FromTerminal; processReslut.FileToken = string.Empty; _leaf?.Send(processReslut); } } else { using (var processReslut = MessagePool.GetMessage()) { processReslut.TargetId = connectNotification.UserId; processReslut.RunTaskType = TaskType.Connect; processReslut.RunTaskStatus = TaskStatus.Finished; processReslut.Detail = DetailType.ConnectedDeny; processReslut.Percent = 0; processReslut.Source = ProcessSource.FromTerminal; processReslut.FileToken = string.Empty; _leaf?.Send(processReslut); } } } catch (Exception ex) { Logger.WriteLineError($"HandleConnectNotification error:{ex}"); } } private Message SetAccountDataToMessage(Message message) { if (message is ClientRequestMessage clientRequestMessage) { clientRequestMessage.AccountData = GetAccountDataMessage() as ClientAccountMessage; return clientRequestMessage; } return message; } private Message GetAccountDataMessage() { var accountData = MessagePool.GetMessage(); accountData.AccountId = _id ?? string.Empty; accountData.AccountName = _deviceName ?? string.Empty; accountData.SessionId = _sessionId ?? string.Empty; accountData.Source = _loginSource; return accountData; } private void ConnectionCheckerOnOfflined(object sender, EventArgs e) { Release(); Status = TerminalStatus.Offline; //No need to reconnect after instance release if (_disposed) return; //Reconnect if (_reconnectCounter < 5) { if (!string.IsNullOrEmpty(_deviceName) && !string.IsNullOrEmpty(_password)) { CreateLeaf(); CreateConnectionChecker(); if (Status == TerminalStatus.Online) { Status = TerminalStatus.Reconnecting; Connect(); _reconnectCounter++; } } } else { Dispose(); } } private void Release() { _id = string.Empty; DisposeConnectionChecker(); DisposeHeartRateKeeper(); ReleaseFeatures(); DisposeLeaf(); } private void ReleaseFeatures() { CommonParameter.Instance.IsFeatureReleased = true; foreach (var type in _features.Keys.ToList()) { var feature = _features[type]; var disposable = feature as IDisposable; disposable?.Dispose(); _features.Remove(type); } } private IFeature GetFeature(TerminalFeatureType terminalFeatureType) { switch (terminalFeatureType) { case TerminalFeatureType.Remedical: return new Remedical(_id, _leaf); case TerminalFeatureType.LiveVideo: if (CommonParameter.Instance.IsSonopost) { return new LiveVideoForSonopost(_id, _deviceName, _leaf); } else { return new LiveVideo(_id, _deviceName, _leaf, _usScreenWidth, _usScreenHeight); } case TerminalFeatureType.AfterSales: return new AfterSales.AfterSales(_id, _leaf); case TerminalFeatureType.Upgrade: return new Upgraders.Upgraders(_leaf); case TerminalFeatureType.Teaching: return new Teaching.Teaching(_id, _leaf); case TerminalFeatureType.Consultation: return new Consultation.Consultation(_id, _deviceName, _url, LoginSource.PersonalComputer, UniqueId); case TerminalFeatureType.Log: return new Log.Log(_id, _leaf); default: return null; } } } }