|  | @@ -0,0 +1,148 @@
 | 
	
		
			
				|  |  | +using Microsoft.AspNet.SignalR.Client;
 | 
	
		
			
				|  |  | +using System.Collections.Concurrent;
 | 
	
		
			
				|  |  | +using System.Threading;
 | 
	
		
			
				|  |  | +using System.Threading.Tasks.Dataflow;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace Communicator
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    public class UISignalRClient : ICommunicator
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        private string _connectionUrl;
 | 
	
		
			
				|  |  | +        private string _hubName;
 | 
	
		
			
				|  |  | +        private HubConnection? _hubConnection;
 | 
	
		
			
				|  |  | +        private IHubProxy? _hubProxy;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private readonly BufferBlock<KeyValuePair<string, object>> _changedDataItems;
 | 
	
		
			
				|  |  | +        private readonly CancellationTokenSource _cancellationTokenSource;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private readonly ConcurrentDictionary<string,List<Action<object>>> _subscribedKeys;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private bool disposedValue;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public UISignalRClient()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            _connectionUrl = "http://localhost:9999/NewUI";
 | 
	
		
			
				|  |  | +            _hubName = "UIHub";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            _changedDataItems = new BufferBlock<KeyValuePair<string, object>>();
 | 
	
		
			
				|  |  | +            _cancellationTokenSource = new CancellationTokenSource();
 | 
	
		
			
				|  |  | +            _subscribedKeys = new ConcurrentDictionary<string, List<Action<object>>>();
 | 
	
		
			
				|  |  | +            Task.Run(() =>
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                ProcessReceivedData();
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public bool Initialize()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            try
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (_hubConnection is not null)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    return true;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                _hubConnection = new HubConnection(_connectionUrl);
 | 
	
		
			
				|  |  | +                _hubConnection.StateChanged += OnStateChanged;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                _hubProxy = _hubConnection.CreateHubProxy(_hubName);
 | 
	
		
			
				|  |  | +                _hubProxy.On<Dictionary<string, object>>("ReceiveChangedDataItems", items =>
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    if(items.Count<=0)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        return;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    foreach (var item in items)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        _changedDataItems.Post(item);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                return true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            catch (Exception)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void SubscribeDataItem(string dataKey, Action<object> callback)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if(!_subscribedKeys.ContainsKey(dataKey))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                _subscribedKeys.TryAdd(dataKey, new List<Action<object>>());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            _subscribedKeys[dataKey].Add(callback);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void UnsubscribeDataItem(string dataKey, Action<object> callback)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if( _subscribedKeys.ContainsKey(dataKey))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                _subscribedKeys[dataKey].Remove(callback);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private async void ProcessReceivedData()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            while (await _changedDataItems.OutputAvailableAsync() && !_cancellationTokenSource.IsCancellationRequested)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var item = await _changedDataItems.ReceiveAsync();
 | 
	
		
			
				|  |  | +                if (!_subscribedKeys.ContainsKey(item.Key))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                foreach (var action in _subscribedKeys[item.Key])
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    action?.Invoke(item.Value);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void OnStateChanged(StateChange obj)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            //TODO:
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        protected virtual void Dispose(bool disposing)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (!disposedValue)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (disposing)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    // TODO: dispose managed state (managed objects)
 | 
	
		
			
				|  |  | +                    if (_hubConnection is not null)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        _hubConnection.StateChanged -= OnStateChanged;
 | 
	
		
			
				|  |  | +                        _hubConnection.Dispose();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        _changedDataItems.Complete();
 | 
	
		
			
				|  |  | +                        _cancellationTokenSource.Cancel();
 | 
	
		
			
				|  |  | +                        _cancellationTokenSource.Dispose();
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
 | 
	
		
			
				|  |  | +                // TODO: set large fields to null
 | 
	
		
			
				|  |  | +                disposedValue = true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
 | 
	
		
			
				|  |  | +        // ~SignalRClient()
 | 
	
		
			
				|  |  | +        // {
 | 
	
		
			
				|  |  | +        //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
	
		
			
				|  |  | +        //     Dispose(disposing: false);
 | 
	
		
			
				|  |  | +        // }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void Dispose()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
	
		
			
				|  |  | +            Dispose(disposing: true);
 | 
	
		
			
				|  |  | +            GC.SuppressFinalize(this);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |