using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.SignalR.Client; using Newtonsoft.Json; using Thinktecture.IdentityModel.Client; namespace Modbus.Net.FBox { public struct SignalRSigninMsg { public string ClientId { get; set; } public string ClientSecret { get; set; } public string UserId { get; set; } public string Password { get; set; } public string SigninAdditionalValues { get; set; } public SignalRServer SignalRServer { get; set; } } public class FBoxConnector : BaseConnector { private OAuth2Client _oauth2; private string _refreshToken; private HttpClient _httpClient { get; set; } private HttpClient _httpClient2 { get; set; } private HubConnection _hubConnection { get; set; } protected SignalRSigninMsg Msg { get; } private DMonGroup _dataGroup { get; set; } private string _groupUid { get; set; } private string _boxUid { get; set; } private string _boxNo { get; set; } private int _boxSessionId { get; set; } private int _connectionState { get; set; } private Dictionary _data { get; set; } private Dictionary _dataType { get; set; } private DateTime _timeStamp { get; set; } = DateTime.MinValue; public override string ConnectionToken { get; } //private AsyncLock _lock = new AsyncLock(); private Timer _timer; private string MachineId => ConnectionToken.Split(',')[0]; private string LocalSequence => ConnectionToken.Split(',')[1]; private bool _connected; public override bool IsConnected => _connected; private Constants _constants; private Constants Constants => _constants ?? (_constants = new Constants()); public FBoxConnector(string machineId, string localSequence, SignalRSigninMsg msg) { Constants.SignalRServer = msg.SignalRServer; System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; ConnectionToken = machineId + "," + localSequence; Msg = msg; _data = new Dictionary(); _dataType = new Dictionary(); } private async void ChangeToken(object sender) { try { var tokenResponse = await _oauth2.RequestRefreshTokenAsync(_refreshToken); _refreshToken = tokenResponse.RefreshToken; _httpClient.SetBearerToken(tokenResponse.AccessToken); _httpClient2.SetBearerToken(tokenResponse.AccessToken); _hubConnection.Headers["Authorization"] = "Bearer " + tokenResponse.AccessToken; await _hubConnection.Start(); await _httpClient2.PostAsync( "dmon/group/" + _dataGroup.Uid + "/start", null); } catch (Exception e) { Console.WriteLine("Retoken failed." + e.Message); } Console.WriteLine("Retoken success."); } public override bool Connect() { return AsyncHelper.RunSync(ConnectAsync); } public override async Task ConnectAsync() { try { _oauth2 = new OAuth2Client( new Uri(Constants.TokenEndpoint), Msg.ClientId, Msg.ClientSecret ); var tokenResponse = await _oauth2.RequestResourceOwnerPasswordAsync ( Msg.UserId, Msg.Password, Msg.SigninAdditionalValues ); if (tokenResponse != null) { _refreshToken = tokenResponse.RefreshToken; if (await CallService(Msg, tokenResponse.AccessToken)) { await _httpClient2.PostAsync( "dmon/group/" + _dataGroup.Uid + "/start", null); _connected = true; _timer = new Timer(ChangeToken, null, 3600*1000*4, 3600*1000*4); Console.WriteLine("SignalR Connected success"); return true; } } return false; } catch (Exception e) { _oauth2 = null; Console.WriteLine("SignalR Connected failed " + e.Message); Clear(); return false; } } private async Task CallService(SignalRSigninMsg msg, string token) { try { var guid = Guid.NewGuid().ToString(); var baseAddress = Constants.AspNetWebApiSampleApi; _httpClient = new HttpClient { BaseAddress = new Uri(baseAddress) }; _httpClient.SetBearerToken(token); //var response = await _httpClient.GetStringAsync("device/spec"); //List deviceSpecs = JsonConvert.DeserializeObject>(response); //deviceSpecs = deviceSpecs.OrderBy(p => p.Id).ToList(); var response = await _httpClient.GetStringAsync("boxgroup"); List boxGroups = JsonConvert.DeserializeObject>(response); if (boxGroups == null) return false; foreach (var boxGroup in boxGroups) { var boxes = boxGroup.BoxRegs; if (boxes == null) continue; foreach (var box in boxes) { var sessionId = box.Box.CurrentSessionId; var baseUrl = box.Box.CommServer.ApiBaseUrl; var signalrUrl = box.Box.CommServer.SignalRUrl; var boxUid = box.Box.Uid; var boxNo = box.Box.BoxNo; var connectionState = box.Box.ConnectionState; if (boxNo != MachineId) continue; if (connectionState != 1) { _connected = false; return false; } _httpClient2 = new HttpClient { BaseAddress = new Uri(baseUrl) }; _httpClient2.SetBearerToken(token); _httpClient2.DefaultRequestHeaders.Add("X-FBox-ClientId", guid); response = await _httpClient2.GetStringAsync("box/" + box.Box.Uid + "/dmon/def/grouped"); List dataGroups = JsonConvert.DeserializeObject>(response); foreach (var dataGroup in dataGroups) { if (dataGroup.Name == LocalSequence) { _dataGroup = dataGroup; break; } } _boxSessionId = sessionId; _boxNo = boxNo; _connectionState = connectionState; _hubConnection = new HubConnection(signalrUrl); _hubConnection.Headers.Add("Authorization", "Bearer " + token); _hubConnection.Headers.Add("X-FBox-ClientId", guid); _hubConnection.Headers.Add("X-FBox-Session", sessionId.ToString()); IHubProxy dataHubProxy = _hubConnection.CreateHubProxy("clientHub"); dataHubProxy.On>("dMonUpdateValue", (boxSessionId, values) => { //#if DEBUG //Console.WriteLine($"Box session {boxSessionId} return at {DateTime.Now}"); //#endif _timeStamp = DateTime.Now; foreach (var value in values) { if (value.Status != 0) { lock (_data) { var dMonEntry = _dataGroup.DMonEntries.FirstOrDefault( p => p.Uid == value.Id); if (dMonEntry != null) { if (_data.ContainsKey(dMonEntry.Desc)) { _data.Remove(dMonEntry.Desc); } } } return; } lock (_data) { if (_dataGroup.DMonEntries.Any(p => p.Uid == value.Id)) { if (_data == null) { _data = new Dictionary(); } var dMonEntry = _dataGroup.DMonEntries.FirstOrDefault( p => p.Uid == value.Id); if (value.Value.HasValue && dMonEntry != null) { if (_data.ContainsKey(dMonEntry.Desc)) { _data[dMonEntry.Desc] = value.Value.Value; } else { _data.Add(dMonEntry.Desc, value.Value.Value); } } } } } }); dataHubProxy.On("boxConnectionStateChanged", async (newConnectionToken, getBoxUid, oldStatus, newStatus) => { #if DEBUG Console.WriteLine( $"Box uid {getBoxUid} change state at {DateTime.Now} new connectionToken {newConnectionToken} newStatus {newStatus}"); #endif sessionId = newConnectionToken; _boxSessionId = sessionId; _connectionState = newStatus; if (!IsConnected || _httpClient2 == null || _hubConnection == null) { Clear(); return; } try { while ( !_httpClient2.DefaultRequestHeaders.TryAddWithoutValidation("X-FBox-Session", sessionId.ToString())) { _httpClient2.DefaultRequestHeaders.Remove("X-FBox-Session"); } _httpClient2.DefaultRequestHeaders.Add("X-FBox-Session", sessionId.ToString()); _hubConnection.Headers["X-FBox-Session"] = sessionId.ToString(); await _hubConnection.Start(); if (newStatus == 1) { if (IsConnected) { await _httpClient2.PostAsync( "dmon/group/" + _dataGroup.Uid + "/start", null); } } else { lock (_data) { _data.Clear(); } //await DisconnectAsync(); //_connected = false; } } catch (Exception ex) { Console.WriteLine("SignalR boxSessionId change error: " + ex.Message); await DisconnectAsync(); } }); _hubConnection.Error += async ex => { Console.WriteLine(@"SignalR error: {0}", ex.Message); await DisconnectAsync(); _connected = false; }; _hubConnection.Closed += () => { _hubConnection.Dispose(); _connected = false; }; ServicePointManager.DefaultConnectionLimit = 10; if (_dataGroup == null) return false; var groupUid = _dataGroup.Uid; var groupName = _dataGroup.Name; if (groupName != "(Default)" && groupName != "默认组" && _connectionState == 1) { _groupUid = groupUid; } if (groupName != "(Default)" && groupName != "默认组" && _connectionState == 1) { _boxUid = boxUid; } _dataType = new Dictionary(); if (_dataGroup.DMonEntries != null) { foreach (var dMonEntry in _dataGroup.DMonEntries) { Type type; switch (dMonEntry.DataType) { //位 case 0: { type = typeof (bool); break; } //16位无符号 case 1: { type = typeof (ushort); break; } //16位有符号 case 2: { type = typeof (short); break; } //32位无符号 case 11: { type = typeof (uint); break; } //32位有符号 case 12: { type = typeof (int); break; } //16位BCD case 3: { type = typeof (short); break; } //32位BCD case 13: { type = typeof (int); break; } //浮点数 case 16: { type = typeof (float); break; } //16位16进制 case 4: { type = typeof (short); break; } //32位16进制 case 14: { type = typeof (int); break; } //16位2进制 case 5: { type = typeof (short); break; } //32位2进制 case 15: { type = typeof (int); break; } default: { type = typeof (short); break; } } if (!_dataType.ContainsKey(dMonEntry.Desc)) { _dataType.Add(dMonEntry.Desc, type); } else { _dataType[dMonEntry.Desc] = type; } } } await _hubConnection.Start(); await dataHubProxy.Invoke("updateClientId", guid); return true; } } return false; } catch { Clear(); return false; } } public override bool Disconnect() { return AsyncHelper.RunSync(DisconnectAsync); } public async Task DisconnectAsync() { try { if (_httpClient2 != null) { await _httpClient2.PostAsync( "dmon/group/" + _groupUid + "/stop", null); } Clear(); Console.WriteLine("SignalR Disconnect success"); return true; } catch (Exception e) { Console.WriteLine("SignalR Disconnect failed " + e.Message); Clear(); return false; } } private async void Clear() { try { if (_hubConnection != null) { await Task.Run(() => _hubConnection.Stop(TimeSpan.FromSeconds(10))); _hubConnection = null; } } catch (Exception) { _hubConnection = null; // ignored } try { if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } } catch (Exception) { //ignore } try { if (_httpClient2 != null) { _httpClient2.Dispose(); _httpClient2 = null; } } catch (Exception) { //ignore } if (_data != null) { lock (_data) { _data.Clear(); _dataType.Clear(); } } _timeStamp = DateTime.MinValue; _connected = false; try { if (_timer != null) { _timer.Dispose(); _timer = null; } } catch (Exception) { //ignore } } public override bool SendMsgWithoutReturn(byte[] message) { throw new NotImplementedException(); } public override Task SendMsgWithoutReturnAsync(byte[] message) { throw new NotImplementedException(); } public override byte[] SendMsg(byte[] message) { return AsyncHelper.RunSync(() => SendMsgAsync(message)); } public override async Task SendMsgAsync(byte[] message) { if (_httpClient == null) { await DisconnectAsync(); _connected = false; return null; } if (_hubConnection.State == ConnectionState.Disconnected) { await _hubConnection.Start(); } var formater = new AddressFormaterFBox(); var translator = new AddressTranslatorFBox(); byte[] ans; //if (_connectionState != 1) //{ //await DisconnectAsync(); //_connected = false; //Console.WriteLine($"Return Value Rejected with connectionToken {ConnectionToken}"); //return null; //} if (_timeStamp == DateTime.MinValue) { return Encoding.ASCII.GetBytes("NoData"); } if (DateTime.Now - _timeStamp > TimeSpan.FromMinutes(2)) { Console.WriteLine("SignalR Timeout: {0} {1} {2}", _timeStamp, DateTime.Now, ConnectionToken); if (_connectionState != 1) { await DisconnectAsync(); _connected = false; return null; } } Dictionary machineDataValue; lock (_data) { machineDataValue = _data.ToDictionary(pair => pair.Key, pair => pair.Value); } var machineDataType = _dataType; if (machineDataType == null || machineDataType.Count == 0) { await DisconnectAsync(); _connected = false; Console.WriteLine($"Return Value Rejected with connectionToken {ConnectionToken}"); return null; } if (machineDataValue == null || machineDataValue.Count == 0) { return Encoding.ASCII.GetBytes("NoData"); } int pos = 0; int area = BigEndianValueHelper.Instance.GetInt(message, ref pos); int address = BigEndianValueHelper.Instance.GetInt(message, ref pos); //short count = BigEndianValueHelper.Instance.GetShort(message, ref pos); object[] dataAns = new object[1]; try { dataAns[0] = Convert.ChangeType( machineDataValue[formater.FormatAddress(translator.GetAreaName(area), address)], machineDataType[formater.FormatAddress(translator.GetAreaName(area), address)]); } catch (Exception) { if (machineDataValue.Count != machineDataType.Count) { await _httpClient2.PostAsync( "dmon/group/" + _dataGroup.Uid + "/start", null); } return Encoding.ASCII.GetBytes("NoData"); //dataAns[0] = //Convert.ChangeType( //0, //machineDataType[formater.FormatAddress(translator.GetAreaName(area), address)]); } finally { ans = BigEndianValueHelper.Instance.ObjectArrayToByteArray(dataAns); } return ans; } } }