From 190cc675eaf3b905bd4d3a13126444901de46a2e Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Fri, 28 Jun 2019 22:03:47 +0100 Subject: [PATCH 01/13] Added a new diagnostics view Changed the URL on the broadcast Refactored the code that listened for broadcasts in the client Added listeners to events in FusionInvest and we resubscribe the cells when something changes A single reference to gMain is now done and passed to each cell --- src/Clients/ExcelClient/AddIn.cs | 12 +- .../ExcelClient/AvailableConnections.cs | 185 ++++++++++++++++++ src/Clients/ExcelClient/ComAddIn.cs | 6 +- src/Clients/ExcelClient/ConnectionHelpers.cs | 2 +- src/Clients/ExcelClient/ConnectionMonitor.cs | 169 +--------------- src/Clients/ExcelClient/CustomRibbon.cs | 19 +- ...ailableConnectionsStatusExcelObservable.cs | 4 +- .../Observables/ConnectionExcelObservable.cs | 6 +- .../ExcelClient/UDF/ConnectionFunctions.cs | 4 +- .../DataServer/DataAvailableEventArgs.cs | 2 + src/Servers/DataServer/DataServer.cs | 3 + .../DataServer/DataServerHostFactory.cs | 7 +- .../FusionLink/Client/DiagnosticsView.xaml | 46 +++++ .../FusionLink/Client/DiagnosticsView.xaml.cs | 30 +++ .../FusionLink/Client/DiagnosticsViewModel.cs | 131 +++++++++++++ .../OpenFusionLinkExcelScenario.cs | 0 .../Client/ShowDiagnosticsScenario.cs | 46 +++++ .../{ => Client}/ShowFusionLinkScenario.cs | 0 src/Servers/FusionLink/FusionLink.csproj | 13 ++ ...tener.cs => AggregatePortfolioListener.cs} | 8 +- .../Listeners/AggregatePositionListener.cs | 29 +++ .../Listeners/AggregateTransactionListener.cs | 29 +++ .../FusionLink/Listeners/IPositionListener.cs | 12 ++ .../Listeners/ITransactionListener.cs | 12 ++ .../Listeners/PortfolioActionListener.cs | 4 +- .../Listeners/PortfolioEventListener.cs | 4 +- .../Listeners/PositionActionListener.cs | 35 ++++ .../Listeners/PositionChangedEventArgs.cs | 18 ++ .../Listeners/PositionEventListener.cs | 34 ++++ .../Listeners/TransactionActionListener.cs | 35 ++++ .../Listeners/TransactionChangedEventArgs.cs | 21 ++ .../Listeners/TransactionEventListener.cs | 34 ++++ src/Servers/FusionLink/Main.cs | 75 ++++--- .../Properties/Resources.Designer.cs | 9 + .../FusionLink/Properties/Resources.resx | 3 + .../FusionLink/Provider/CellValueBase.cs | 6 +- .../Provider/FusionDataServiceProvider.cs | 103 +++++++++- .../FusionLink/Provider/PortfolioCellValue.cs | 4 +- .../FusionLink/Provider/PositionCellValue.cs | 4 +- 39 files changed, 938 insertions(+), 226 deletions(-) create mode 100644 src/Clients/ExcelClient/AvailableConnections.cs create mode 100644 src/Servers/FusionLink/Client/DiagnosticsView.xaml create mode 100644 src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs create mode 100644 src/Servers/FusionLink/Client/DiagnosticsViewModel.cs rename src/Servers/FusionLink/{ => Client}/OpenFusionLinkExcelScenario.cs (100%) create mode 100644 src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs rename src/Servers/FusionLink/{ => Client}/ShowFusionLinkScenario.cs (100%) rename src/Servers/FusionLink/Listeners/{AggregateListener.cs => AggregatePortfolioListener.cs} (66%) create mode 100644 src/Servers/FusionLink/Listeners/AggregatePositionListener.cs create mode 100644 src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs create mode 100644 src/Servers/FusionLink/Listeners/IPositionListener.cs create mode 100644 src/Servers/FusionLink/Listeners/ITransactionListener.cs create mode 100644 src/Servers/FusionLink/Listeners/PositionActionListener.cs create mode 100644 src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs create mode 100644 src/Servers/FusionLink/Listeners/PositionEventListener.cs create mode 100644 src/Servers/FusionLink/Listeners/TransactionActionListener.cs create mode 100644 src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs create mode 100644 src/Servers/FusionLink/Listeners/TransactionEventListener.cs diff --git a/src/Clients/ExcelClient/AddIn.cs b/src/Clients/ExcelClient/AddIn.cs index e57d169..c390dd4 100644 --- a/src/Clients/ExcelClient/AddIn.cs +++ b/src/Clients/ExcelClient/AddIn.cs @@ -14,6 +14,7 @@ public class AddIn : IExcelAddIn //The client needs to be static so the Excel functions (which must be static) can access it. public static DataServiceClient Client; public static ConnectionMonitor ConnectionMonitor; + public static AvailableConnections AvailableConnections; public static bool IsShuttingDown; @@ -32,15 +33,18 @@ public void AutoOpen() var app = ExcelDnaUtil.Application as Microsoft.Office.Interop.Excel.Application; app.RTD.ThrottleInterval = 100; + //Monitor for FusionLink connections + AvailableConnections = new AvailableConnections(); + //Open the client connection - ConnectionMonitor = new ConnectionMonitor(); + ConnectionMonitor = new ConnectionMonitor(AvailableConnections); ConnectionMonitor.RegisterClient(Client); - ExcelComAddInHelper.LoadComAddIn(new ComAddIn(Client, ConnectionMonitor)); + ExcelComAddInHelper.LoadComAddIn(new ComAddIn(Client, ConnectionMonitor, AvailableConnections)); Client.OnConnectionStatusChanged += Client_OnConnectionStatusChanged; - + //Start the monitor - ConnectionMonitor.FindAvailableServicesAsync().ContinueWith(result => + AvailableConnections.FindAvailableServicesAsync().ContinueWith(result => { if (IsShuttingDown) return; diff --git a/src/Clients/ExcelClient/AvailableConnections.cs b/src/Clients/ExcelClient/AvailableConnections.cs new file mode 100644 index 0000000..8ccdd06 --- /dev/null +++ b/src/Clients/ExcelClient/AvailableConnections.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceModel; +using System.ServiceModel.Discovery; +using System.Text; +using System.Threading.Tasks; +using RxdSolutions.FusionLink.Interface; + +namespace RxdSolutions.FusionLink.ExcelClient +{ + public class AvailableConnections : IDisposable + { + public event EventHandler AvailableEndpointsChanged; + + public bool IsSearchingForEndPoints { get; private set; } + + private readonly List _availableEndpoints; + private readonly ServiceHost _announcementServiceHost; + private readonly AnnouncementService _announcementService; + + public AvailableConnections() + { + _availableEndpoints = new List(); + + // Subscribe the announcement events + _announcementService = new AnnouncementService(); + _announcementService.OnlineAnnouncementReceived += OnOnlineEvent; + _announcementService.OfflineAnnouncementReceived += OnOfflineEvent; + + // Create ServiceHost for the AnnouncementService + _announcementServiceHost = new ServiceHost(_announcementService); + _announcementServiceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint()); + _announcementServiceHost.Open(); + } + + public IReadOnlyList AvailableEndpoints + { + get + { + lock (_availableEndpoints) + { + return _availableEndpoints.ToList(); + } + } + } + + public Task FindAvailableServicesAsync() + { + IsSearchingForEndPoints = true; + + return Task.Run(() => { + + FindAvailableServices(); + + }); + } + + public void FindAvailableServices() + { + try + { + IsSearchingForEndPoints = true; + + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + + var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint()); + var findResponse = discoveryClient.Find(new FindCriteria(typeof(IDataServiceServer)) { Duration = TimeSpan.FromSeconds(3) }); + + foreach (var endPoints in findResponse.Endpoints) + { + if (string.Compare(endPoints.ListenUris[0].Host, Environment.MachineName, StringComparison.InvariantCultureIgnoreCase) != 0) + { + continue; + } + + if (endPoints.ListenUris[0].Segments[2].Replace("/", "") != System.Diagnostics.Process.GetCurrentProcess().SessionId.ToString()) + { + continue; + } + + var found = false; + + foreach (var knownEndpoint in AvailableEndpoints) + { + if (knownEndpoint.Uri == endPoints.Address.Uri) + { + found = true; + break; + } + } + + if (!found) + { + lock (_availableEndpoints) + { + _availableEndpoints.Add(endPoints.Address); + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + } + } + } + + foreach (var knownEndpoint in AvailableEndpoints) + { + var found = false; + foreach (var endPoints in findResponse.Endpoints) + { + if (knownEndpoint.Uri == endPoints.Address.Uri) + { + found = true; + break; + } + } + + if (!found) + { + lock (_availableEndpoints) + { + _availableEndpoints.Remove(knownEndpoint); + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + } + } + } + } + finally + { + IsSearchingForEndPoints = false; + + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + } + } + + public void Remove(EndpointAddress ea) + { + _availableEndpoints.Remove(ea); + + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + } + + private void OnOfflineEvent(object sender, AnnouncementEventArgs e) + { + _availableEndpoints.Remove(e.EndpointDiscoveryMetadata.Address); + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + } + + private void OnOnlineEvent(object sender, AnnouncementEventArgs e) + { + _availableEndpoints.Add(e.EndpointDiscoveryMetadata.Address); + AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + } + + public EndpointAddress FindEndpoint(Uri connection) + { + lock (_availableEndpoints) + { + return _availableEndpoints.SingleOrDefault(x => x.Uri == connection); + } + } + + #region IDisposable Support + + private bool disposedValue = false; + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _announcementServiceHost.Close(); + } + + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(true); + } + + #endregion + + } +} diff --git a/src/Clients/ExcelClient/ComAddIn.cs b/src/Clients/ExcelClient/ComAddIn.cs index 39e4bb4..c9b6a39 100644 --- a/src/Clients/ExcelClient/ComAddIn.cs +++ b/src/Clients/ExcelClient/ComAddIn.cs @@ -15,11 +15,13 @@ public class ComAddIn : ExcelComAddIn { private readonly DataServiceClient client; private readonly ConnectionMonitor monitor; + private readonly AvailableConnections availableConnections; - public ComAddIn(DataServiceClient client, ConnectionMonitor monitor) + public ComAddIn(DataServiceClient client, ConnectionMonitor monitor, AvailableConnections availableConnections) { this.client = client; this.monitor = monitor; + this.availableConnections = availableConnections; } public override void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom) @@ -30,7 +32,7 @@ public override void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array cu client.Dispose(); monitor.Stop(); - monitor.Dispose(); + availableConnections.Dispose(); } } } diff --git a/src/Clients/ExcelClient/ConnectionHelpers.cs b/src/Clients/ExcelClient/ConnectionHelpers.cs index 357d0cc..74ad9a5 100644 --- a/src/Clients/ExcelClient/ConnectionHelpers.cs +++ b/src/Clients/ExcelClient/ConnectionHelpers.cs @@ -14,7 +14,7 @@ public static class ConnectionHelper { public static int GetConnectionId(Uri uri) { - return Convert.ToInt32(uri.ToString().Split('_')[1]); + return Convert.ToInt32(uri.Segments[3].Replace("/", "")); } public static string GetConnectionName(Uri uri) diff --git a/src/Clients/ExcelClient/ConnectionMonitor.cs b/src/Clients/ExcelClient/ConnectionMonitor.cs index 1df7711..d052e6e 100644 --- a/src/Clients/ExcelClient/ConnectionMonitor.cs +++ b/src/Clients/ExcelClient/ConnectionMonitor.cs @@ -5,14 +5,12 @@ using System.Collections.Generic; using System.Linq; using System.ServiceModel; -using System.ServiceModel.Discovery; using System.Threading; using System.Threading.Tasks; -using RxdSolutions.FusionLink.Interface; namespace RxdSolutions.FusionLink.ExcelClient { - public class ConnectionMonitor : IDisposable + public class ConnectionMonitor { private readonly List _clients; @@ -20,50 +18,22 @@ public class ConnectionMonitor : IDisposable private Uri _connection; - private readonly AnnouncementService _announcementService; - private readonly List _availableEndpoints; - private readonly ServiceHost _announcementServiceHost; - private bool _running = false; private readonly AutoResetEvent _resetEvent; private Task _monitor; private readonly object _monitorLock = new object(); - - public event EventHandler AvailableEndpointsChanged; - - public bool IsSearchingForEndPoints { get; private set; } - - public IReadOnlyList AvailableEndpoints - { - get - { - lock(_availableEndpoints) - { - return _availableEndpoints.ToList(); - } - } - } + private readonly AvailableConnections _connections; public bool IsConnected { get; private set; } - public ConnectionMonitor() + public ConnectionMonitor(AvailableConnections connections) { _clients = new List(); _resetEvent = new AutoResetEvent(false); - _availableEndpoints = new List(); - - // Subscribe the announcement events - _announcementService = new AnnouncementService(); - _announcementService.OnlineAnnouncementReceived += OnOnlineEvent; - _announcementService.OfflineAnnouncementReceived += OnOfflineEvent; - - // Create ServiceHost for the AnnouncementService - _announcementServiceHost = new ServiceHost(_announcementService); - _announcementServiceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint()); - _announcementServiceHost.Open(); + _connections = connections; } public void RegisterClient(DataServiceClient client) @@ -71,81 +41,7 @@ public void RegisterClient(DataServiceClient client) _clients.Add(client); } - public Task FindAvailableServicesAsync() - { - IsSearchingForEndPoints = true; - - return Task.Run(() => { - - FindAvailableServices(); - - }); - } - public void FindAvailableServices() - { - try - { - IsSearchingForEndPoints = true; - - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); - - var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint()); - var findResponse = discoveryClient.Find(new FindCriteria(typeof(IDataServiceServer)) { Duration = TimeSpan.FromSeconds(3) }); - - foreach (var endPoints in findResponse.Endpoints) - { - var found = false; - - foreach (var knownEndpoint in AvailableEndpoints) - { - if (knownEndpoint.Uri == endPoints.Address.Uri) - { - found = true; - break; - } - } - - if (!found) - { - lock (_availableEndpoints) - { - _availableEndpoints.Add(endPoints.Address); - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); - } - } - } - - foreach (var knownEndpoint in AvailableEndpoints) - { - var found = false; - foreach (var endPoints in findResponse.Endpoints) - { - if (knownEndpoint.Uri == endPoints.Address.Uri) - { - found = true; - break; - } - } - - if (!found) - { - lock (_availableEndpoints) - { - _availableEndpoints.Remove(knownEndpoint); - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); - } - } - } - } - finally - { - IsSearchingForEndPoints = false; - - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); - } - } - public void Start() { lock(_monitorLock) @@ -176,17 +72,17 @@ public void Start() client.Close(); } - if(AvailableEndpoints.Count == 0) + if(_connections.AvailableEndpoints.Count == 0) { continue; } if (_connection == null) { - _connection = AvailableEndpoints.FirstOrDefault()?.Uri; + _connection = _connections.AvailableEndpoints.FirstOrDefault()?.Uri; } - var connectionToAttempt = FindEndpoint(_connection); + var connectionToAttempt = _connections.FindEndpoint(_connection); if (connectionToAttempt is null) { @@ -214,12 +110,7 @@ public void Start() IsConnected = false; //Looks like the server is dead. Remove from the available list. - lock (_availableEndpoints) - { - _availableEndpoints.Remove(connectionToAttempt); - } - - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); + _connections.Remove(connectionToAttempt); } catch (Exception) { @@ -270,49 +161,5 @@ public Uri GetConnection() { return _connection; } - - private void OnOfflineEvent(object sender, AnnouncementEventArgs e) - { - _availableEndpoints.Remove(e.EndpointDiscoveryMetadata.Address); - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); - } - - private void OnOnlineEvent(object sender, AnnouncementEventArgs e) - { - _availableEndpoints.Add(e.EndpointDiscoveryMetadata.Address); - AvailableEndpointsChanged?.Invoke(this, new EventArgs()); - } - - private EndpointAddress FindEndpoint(Uri connection) - { - lock (_availableEndpoints) - { - return _availableEndpoints.SingleOrDefault(x => x.Uri == connection); - } - } - - #region IDisposable Support - - private bool disposedValue = false; - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - _announcementServiceHost.Close(); - } - - disposedValue = true; - } - } - - public void Dispose() - { - Dispose(true); - } - - #endregion } } \ No newline at end of file diff --git a/src/Clients/ExcelClient/CustomRibbon.cs b/src/Clients/ExcelClient/CustomRibbon.cs index 2908f5f..bd6fb6d 100644 --- a/src/Clients/ExcelClient/CustomRibbon.cs +++ b/src/Clients/ExcelClient/CustomRibbon.cs @@ -25,6 +25,7 @@ public class CustomRibbon : ExcelRibbon private ConnectionMonitor _connectionMonitor; private DataServiceClient _client; private Microsoft.Office.Interop.Excel.Application _application; + private AvailableConnections _availableConnections; public CustomRibbon() { @@ -35,6 +36,8 @@ public void Ribbon_Load(IRibbonUI sender) _ribbonUi = sender; _connectionMonitor = AddIn.ConnectionMonitor; + _availableConnections = AddIn.AvailableConnections; + _client = AddIn.Client; _application = ExcelDnaUtil.Application as Microsoft.Office.Interop.Excel.Application; @@ -46,18 +49,18 @@ public bool OnRefreshEnabled(IRibbonControl control) if (_client.IsConnecting) return false; - return !_connectionMonitor.IsSearchingForEndPoints; + return !_availableConnections.IsSearchingForEndPoints; } public bool OnConnectionsEnabled(IRibbonControl control) { - if (_connectionMonitor.IsSearchingForEndPoints) + if (_availableConnections.IsSearchingForEndPoints) return false; if (_client.IsConnecting) return false; - return _connectionMonitor.AvailableEndpoints.Count > 0; + return _availableConnections.AvailableEndpoints.Count > 0; } public bool OnConnectionActionEnabled(IRibbonControl control) @@ -104,12 +107,12 @@ private string GetCustomRibbonXML() public string OnGetContent(IRibbonControl control) { - if (_connectionMonitor.IsSearchingForEndPoints) + if (_availableConnections.IsSearchingForEndPoints) { return BuildMessage(Resources.SearchingForServersMessage).ToString(); } - if(_connectionMonitor.AvailableEndpoints.Count == 0) + if(_availableConnections.AvailableEndpoints.Count == 0) { return BuildMessage(Resources.NoEndPointsAvailableMessage).ToString(); } @@ -148,7 +151,7 @@ string GetCurrentFlag() private IEnumerable<(Uri Uri, Process Process)> GetAliveEndPoints() { - foreach (var endPoint in _connectionMonitor.AvailableEndpoints) + foreach (var endPoint in _availableConnections.AvailableEndpoints) { var id = ConnectionHelper.GetConnectionId(endPoint.Uri); @@ -212,7 +215,7 @@ public void OnRefresh(IRibbonControl control) task.ContinueWith(x => { - if(_connectionMonitor.AvailableEndpoints.Count == 0) + if(_availableConnections.AvailableEndpoints.Count == 0) { ExcelStatusBarHelperAsync.SetStatusBarWithResetDelay(Resources.NoEndPointsAvailableMessage, 5); } @@ -260,7 +263,7 @@ public void OnLoadPositions(IRibbonControl control) private Task RefreshAvailableConnections() { - return _connectionMonitor.FindAvailableServicesAsync(); + return _availableConnections.FindAvailableServicesAsync(); } } } diff --git a/src/Clients/ExcelClient/Observables/AvailableConnectionsStatusExcelObservable.cs b/src/Clients/ExcelClient/Observables/AvailableConnectionsStatusExcelObservable.cs index a059286..3fb99da 100644 --- a/src/Clients/ExcelClient/Observables/AvailableConnectionsStatusExcelObservable.cs +++ b/src/Clients/ExcelClient/Observables/AvailableConnectionsStatusExcelObservable.cs @@ -10,10 +10,10 @@ namespace RxdSolutions.FusionLink.ExcelClient { public class AvailableConnectionsExcelObservable : IExcelObservable { - private readonly ConnectionMonitor _monitor; + private readonly AvailableConnections _monitor; private IExcelObserver _observer; - public AvailableConnectionsExcelObservable(ConnectionMonitor monitor) + public AvailableConnectionsExcelObservable(AvailableConnections monitor) { _monitor = monitor; } diff --git a/src/Clients/ExcelClient/Observables/ConnectionExcelObservable.cs b/src/Clients/ExcelClient/Observables/ConnectionExcelObservable.cs index 1580971..4437920 100644 --- a/src/Clients/ExcelClient/Observables/ConnectionExcelObservable.cs +++ b/src/Clients/ExcelClient/Observables/ConnectionExcelObservable.cs @@ -11,12 +11,14 @@ public class ConnectionNameExcelObservable : IExcelObservable { private readonly DataServiceClient _rtdClient; private readonly ConnectionMonitor _connectionMonitor; + private readonly AvailableConnections _availableConnections; private IExcelObserver _observer; - public ConnectionNameExcelObservable(DataServiceClient rtdClient, ConnectionMonitor connectionMonitor) + public ConnectionNameExcelObservable(DataServiceClient rtdClient, ConnectionMonitor connectionMonitor, AvailableConnections availableConnections) { _rtdClient = rtdClient; _connectionMonitor = connectionMonitor; + _availableConnections = availableConnections; } public IDisposable Subscribe(IExcelObserver observer) @@ -24,7 +26,7 @@ public IDisposable Subscribe(IExcelObserver observer) _observer = observer; _rtdClient.OnConnectionStatusChanged += OnConnectionStatusChanged; - _connectionMonitor.AvailableEndpointsChanged += AvailableEndpointsChanged; + _availableConnections.AvailableEndpointsChanged += AvailableEndpointsChanged; if (_rtdClient.Connection is object) { diff --git a/src/Clients/ExcelClient/UDF/ConnectionFunctions.cs b/src/Clients/ExcelClient/UDF/ConnectionFunctions.cs index dbd414b..eb5366c 100644 --- a/src/Clients/ExcelClient/UDF/ConnectionFunctions.cs +++ b/src/Clients/ExcelClient/UDF/ConnectionFunctions.cs @@ -20,7 +20,7 @@ public static object GetConnectionStatus() HelpTopic = "Get-Available-Connections")] public static object GetAvailableConnections() { - return ExcelAsyncUtil.Observe(nameof(GetAvailableConnections), null, () => new AvailableConnectionsExcelObservable(AddIn.ConnectionMonitor)); + return ExcelAsyncUtil.Observe(nameof(GetAvailableConnections), null, () => new AvailableConnectionsExcelObservable(AddIn.AvailableConnections)); } [ExcelFunction(Name = "GETCONNECTION", @@ -28,7 +28,7 @@ public static object GetAvailableConnections() HelpTopic = "Get-Connection")] public static object GetConnection() { - return ExcelAsyncUtil.Observe(nameof(GetConnection), null, () => new ConnectionNameExcelObservable(AddIn.Client, AddIn.ConnectionMonitor)); + return ExcelAsyncUtil.Observe(nameof(GetConnection), null, () => new ConnectionNameExcelObservable(AddIn.Client, AddIn.ConnectionMonitor, AddIn.AvailableConnections)); } [ExcelFunction(Name = "GETCONNECTIONID", diff --git a/src/Servers/DataServer/DataAvailableEventArgs.cs b/src/Servers/DataServer/DataAvailableEventArgs.cs index eb97d9f..da0cc03 100644 --- a/src/Servers/DataServer/DataAvailableEventArgs.cs +++ b/src/Servers/DataServer/DataAvailableEventArgs.cs @@ -17,6 +17,8 @@ public DataAvailableEventArgs() SystemValues = new Dictionary(); } + public TimeSpan TimeTaken { get; set; } + public IDictionary<(int folioId, string column), object> PortfolioValues { get; } public IDictionary<(int positionId, string column), object> PositionValues { get; } diff --git a/src/Servers/DataServer/DataServer.cs b/src/Servers/DataServer/DataServer.cs index c6557cf..ff895dc 100644 --- a/src/Servers/DataServer/DataServer.cs +++ b/src/Servers/DataServer/DataServer.cs @@ -35,6 +35,7 @@ public class DataServer : IDataServiceServer public event EventHandler OnClientConnectionChanged; public event EventHandler OnSubscriptionChanged; + public event EventHandler OnDataReceived; public int ClientCount { @@ -626,6 +627,8 @@ private void SendServiceStatus() private void DataService_DataAvailable(object sender, DataAvailableEventArgs e) { + OnDataReceived?.Invoke(this, e); + _publishQueue.Add(e); } diff --git a/src/Servers/DataServer/DataServerHostFactory.cs b/src/Servers/DataServer/DataServerHostFactory.cs index 21d682e..fb0382b 100644 --- a/src/Servers/DataServer/DataServerHostFactory.cs +++ b/src/Servers/DataServer/DataServerHostFactory.cs @@ -13,9 +13,12 @@ public class DataServerHostFactory { private static readonly string ServiceName = $"/"; - private static Uri GetListeningAddress() + public static Uri GetListeningAddress() { - return new Uri($"net.pipe://localhost/SophisDataService_{System.Diagnostics.Process.GetCurrentProcess().Id}"); + var processId = System.Diagnostics.Process.GetCurrentProcess().Id; + var sessionId = System.Diagnostics.Process.GetCurrentProcess().SessionId; + + return new Uri($"net.pipe://{Environment.MachineName}/FusionLink/{sessionId}/{processId}"); } public static ServiceHost Create(DataServer server) diff --git a/src/Servers/FusionLink/Client/DiagnosticsView.xaml b/src/Servers/FusionLink/Client/DiagnosticsView.xaml new file mode 100644 index 0000000..6ea6a48 --- /dev/null +++ b/src/Servers/FusionLink/Client/DiagnosticsView.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs b/src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs new file mode 100644 index 0000000..984de25 --- /dev/null +++ b/src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace RxdSolutions.FusionLink.Client +{ + /// + /// Interaction logic for DiagnosticsView.xaml + /// + public partial class DiagnosticsView : UserControl + { + public DiagnosticsView(DiagnosticsViewModel viewModel) + { + InitializeComponent(); + + DataContext = viewModel; + } + } +} diff --git a/src/Servers/FusionLink/Client/DiagnosticsViewModel.cs b/src/Servers/FusionLink/Client/DiagnosticsViewModel.cs new file mode 100644 index 0000000..328ca09 --- /dev/null +++ b/src/Servers/FusionLink/Client/DiagnosticsViewModel.cs @@ -0,0 +1,131 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace RxdSolutions.FusionLink.Client +{ + public class DiagnosticsViewModel : INotifyPropertyChanged + { + private int _portfolioSubscriptionCount; + private int _systemSubscriptionCount; + private int _positionSubscriptionCount; + private int _portfolioPropertySubscriptionCount; + private int _clientCount; + private TimeSpan _lastTimeTaken; + + private readonly DataServer _dataServer; + + public DiagnosticsViewModel(DataServer dataServer) + { + _dataServer = dataServer; + _dataServer.OnSubscriptionChanged += OnSubscriptionChanged; + _dataServer.OnClientConnectionChanged += OnClientConnectionChanged; + _dataServer.OnDataReceived += OnDataReceived; + + PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; + SystemSubscriptionCount = _dataServer.SystemValueCount; + PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; + PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; + ClientCount = _dataServer.ClientCount; + + } + + private void OnDataReceived(object sender, DataAvailableEventArgs e) + { + LastTimeTaken = e.TimeTaken; + } + + private void OnClientConnectionChanged(object sender, ClientConnectionChangedEventArgs e) + { + ClientCount = _dataServer.ClientCount; + } + + private void OnSubscriptionChanged(object sender, EventArgs e) + { + PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; + SystemSubscriptionCount = _dataServer.SystemValueCount; + PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; + PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public int ClientCount + { + get { return _clientCount; } + set + { + _clientCount = value; + OnPropertyChanged(); + } + } + + public TimeSpan LastTimeTaken + { + get { return _lastTimeTaken; } + set + { + _lastTimeTaken = value; + OnPropertyChanged(); + } + } + + public string ServerUri + { + get { return DataServerHostFactory.GetListeningAddress().ToString(); } + } + + public bool IsTerminalServices + { + get + { + return System.Windows.Forms.SystemInformation.TerminalServerSession; + } + } + + public int PortfolioSubscriptionCount + { + get { return _portfolioSubscriptionCount; } + set + { + _portfolioSubscriptionCount = value; + OnPropertyChanged(); + } + } + + public int SystemSubscriptionCount + { + get { return _systemSubscriptionCount; } + set + { + _systemSubscriptionCount = value; + OnPropertyChanged(); + } + } + + public int PositionSubscriptionCount + { + get { return _positionSubscriptionCount; } + set + { + _positionSubscriptionCount = value; + OnPropertyChanged(); + } + } + + public int PortfolioPropertySubscriptionCount + { + get { return _portfolioPropertySubscriptionCount; } + set + { + _portfolioPropertySubscriptionCount = value; + OnPropertyChanged(); + } + } + + public void OnPropertyChanged([CallerMemberName] string callerMemberName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(callerMemberName)); + } + } +} diff --git a/src/Servers/FusionLink/OpenFusionLinkExcelScenario.cs b/src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs similarity index 100% rename from src/Servers/FusionLink/OpenFusionLinkExcelScenario.cs rename to src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs diff --git a/src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs b/src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs new file mode 100644 index 0000000..4ff3e56 --- /dev/null +++ b/src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs @@ -0,0 +1,46 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using RxdSolutions.FusionLink.Properties; +using sophis.scenario; +using sophis.utils; +using Sophis.Windows.Integration; + +namespace RxdSolutions.FusionLink.Client +{ + public class ShowDiagnosticsScenario : CSMScenario + { + public override eMProcessingType GetProcessingType() + { + return eMProcessingType.M_pUserPreference; + } + + public override bool AlwaysEnabled() + { + return true; + } + + public override CMString GetName() + { + return Resources.ShowDiagnostics; + } + + public override void Run() + { + var wndKey = new WindowKey(3049, 0, 0); + IntPtr activeWindow = WPFAdapter.Instance.GetActiveWindow(wndKey); + if (IntPtr.Zero != activeWindow) + { + WPFAdapter.Instance.ActivateWindow(activeWindow); + } + else + { + var fwkElement = new DiagnosticsView(new DiagnosticsViewModel(Main.DataServer)); + WPFAdapter.Instance.OpenWindow(fwkElement, "FusionLink", wndKey, false); + } + + base.Run(); + } + } +} diff --git a/src/Servers/FusionLink/ShowFusionLinkScenario.cs b/src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs similarity index 100% rename from src/Servers/FusionLink/ShowFusionLinkScenario.cs rename to src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs diff --git a/src/Servers/FusionLink/FusionLink.csproj b/src/Servers/FusionLink/FusionLink.csproj index 42cef7c..a51fbe1 100644 --- a/src/Servers/FusionLink/FusionLink.csproj +++ b/src/Servers/FusionLink/FusionLink.csproj @@ -35,6 +35,9 @@ TRACE;V72 true + + + @@ -45,6 +48,7 @@ + @@ -253,6 +257,10 @@ E:\UAT_SOPHIS\CLIENT\SophisDotNetValue.dll false + + E:\UAT_SOPHIS\CLIENT\Sophis.Windows.dll + false + E:\UAT_SOPHIS\CLIENT\DevExpress.XtraTreeList.v11.2.dll false @@ -311,6 +319,11 @@ + + + MSBuild:Compile + + diff --git a/src/Servers/FusionLink/Listeners/AggregateListener.cs b/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs similarity index 66% rename from src/Servers/FusionLink/Listeners/AggregateListener.cs rename to src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs index 8ac69aa..9d970dc 100644 --- a/src/Servers/FusionLink/Listeners/AggregateListener.cs +++ b/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs @@ -5,20 +5,20 @@ namespace RxdSolutions.FusionLink { - public class AggregateListener : IPortfolioListener + public class AggregatePortfolioListener : IPortfolioListener { private readonly PortfolioActionListener _actionListener; private readonly PortfolioEventListener _eventListener; public event EventHandler PortfolioChanged; - public AggregateListener(PortfolioActionListener actionListener, PortfolioEventListener eventListener) + public AggregatePortfolioListener(PortfolioActionListener actionListener, PortfolioEventListener eventListener) { _actionListener = actionListener; _eventListener = eventListener; - _actionListener.PortfolioChanged += OnPortfolioChanged; - _eventListener.PortfolioChanged += OnPortfolioChanged; + PortfolioActionListener.PortfolioChanged += OnPortfolioChanged; + PortfolioEventListener.PortfolioChanged += OnPortfolioChanged; } private void OnPortfolioChanged(object sender, PortfolioChangedEventArgs e) diff --git a/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs b/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs new file mode 100644 index 0000000..4ed1375 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs @@ -0,0 +1,29 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace RxdSolutions.FusionLink +{ + public class AggregatePositionListener : IPositionListener + { + private readonly PositionActionListener _actionListener; + private readonly PositionEventListener _eventListener; + + public event EventHandler PositionChanged; + + public AggregatePositionListener(PositionActionListener actionListener, PositionEventListener eventListener) + { + _actionListener = actionListener; + _eventListener = eventListener; + + PositionActionListener.PositionChanged += OnPositionChanged; + PositionEventListener.PositionChanged += OnPositionChanged; + } + + private void OnPositionChanged(object sender, PositionChangedEventArgs e) + { + PositionChanged?.Invoke(this, e); + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs b/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs new file mode 100644 index 0000000..53232a1 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs @@ -0,0 +1,29 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace RxdSolutions.FusionLink +{ + public class AggregateTransactionListener : ITransactionListener + { + private readonly TransactionActionListener _actionListener; + private readonly TransactionEventListener _eventListener; + + public event EventHandler TransactionChanged; + + public AggregateTransactionListener(TransactionActionListener actionListener, TransactionEventListener eventListener) + { + _actionListener = actionListener; + _eventListener = eventListener; + + TransactionActionListener.TransactionChanged += OnTransactionChanged; + TransactionEventListener.TransactionChanged += OnTransactionChanged; + } + + private void OnTransactionChanged(object sender, TransactionChangedEventArgs e) + { + TransactionChanged?.Invoke(this, e); + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/IPositionListener.cs b/src/Servers/FusionLink/Listeners/IPositionListener.cs new file mode 100644 index 0000000..3973b17 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/IPositionListener.cs @@ -0,0 +1,12 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace RxdSolutions.FusionLink +{ + public interface IPositionListener + { + event EventHandler PositionChanged; + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/ITransactionListener.cs b/src/Servers/FusionLink/Listeners/ITransactionListener.cs new file mode 100644 index 0000000..7c0dd84 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/ITransactionListener.cs @@ -0,0 +1,12 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace RxdSolutions.FusionLink +{ + public interface ITransactionListener + { + event EventHandler TransactionChanged; + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs b/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs index c77835b..7c07563 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs @@ -7,9 +7,9 @@ namespace RxdSolutions.FusionLink { - public class PortfolioActionListener : CSMPortfolioAction, IPortfolioListener + public class PortfolioActionListener : CSMPortfolioAction { - public event EventHandler PortfolioChanged; + public static event EventHandler PortfolioChanged; public override void NotifyCreated(CSMPortfolio portfolio, CSMEventVector message) { diff --git a/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs b/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs index dbca38c..eb2e3a4 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs @@ -6,9 +6,9 @@ namespace RxdSolutions.FusionLink { - public class PortfolioEventListener : CSMPortfolioEvent, IPortfolioListener + public class PortfolioEventListener : CSMPortfolioEvent { - public event EventHandler PortfolioChanged; + public static event EventHandler PortfolioChanged; public override bool HasBeenCreated(CSMPortfolio portfolio) { diff --git a/src/Servers/FusionLink/Listeners/PositionActionListener.cs b/src/Servers/FusionLink/Listeners/PositionActionListener.cs new file mode 100644 index 0000000..06fc6b7 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/PositionActionListener.cs @@ -0,0 +1,35 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using sophis.portfolio; +using sophis.tools; + +namespace RxdSolutions.FusionLink +{ + public class PositionActionListener : CSMPositionAction + { + public static event EventHandler PositionChanged; + + public override void NotifyDeleted(CSMPosition position, CSMEventVector message) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + + base.NotifyDeleted(position, message); + } + + public override void NotifyModified(CSMPosition position, CSMEventVector message) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + + base.NotifyModified(position, message); + } + + public override void NotifyTransferred(CSMPosition position, CSMEventVector message) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + + base.NotifyTransferred(position, message); + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs b/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs new file mode 100644 index 0000000..1cb4503 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs @@ -0,0 +1,18 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +namespace RxdSolutions.FusionLink +{ + public class PositionChangedEventArgs + { + public int PositionId { get; } + + public bool IsLocal { get; } + + public PositionChangedEventArgs(int positionId, bool isLocal) + { + PositionId = positionId; + IsLocal = isLocal; + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/PositionEventListener.cs b/src/Servers/FusionLink/Listeners/PositionEventListener.cs new file mode 100644 index 0000000..2020da1 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/PositionEventListener.cs @@ -0,0 +1,34 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using sophis.portfolio; + +namespace RxdSolutions.FusionLink +{ + public class PositionEventListener : CSMPositionEvent + { + public static event EventHandler PositionChanged; + + public override bool HasBeenDeleted(CSMPosition position) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + + return base.HasBeenDeleted(position); + } + + public override bool HasBeenModified(CSMPosition position) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + + return base.HasBeenModified(position); + } + + public override void HasBeenTransferred(CSMPosition position) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + + base.HasBeenTransferred(position); + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/TransactionActionListener.cs b/src/Servers/FusionLink/Listeners/TransactionActionListener.cs new file mode 100644 index 0000000..31c2d70 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/TransactionActionListener.cs @@ -0,0 +1,35 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using sophis.portfolio; +using sophis.tools; + +namespace RxdSolutions.FusionLink +{ + public class TransactionActionListener : CSMTransactionAction + { + public static event EventHandler TransactionChanged; + + public override void NotifyCreated(CSMTransaction transaction, CSMEventVector message, int event_id) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); + + base.NotifyCreated(transaction, message, event_id); + } + + public override void NotifyDeleted(CSMTransaction transaction, CSMEventVector message, int event_id) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); + + base.NotifyDeleted(transaction, message, event_id); + } + + public override void NotifyModified(CSMTransaction original, CSMTransaction transaction, CSMEventVector message, int event_id) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); + + base.NotifyModified(original, transaction, message, event_id); + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs b/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs new file mode 100644 index 0000000..eb65009 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs @@ -0,0 +1,21 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +namespace RxdSolutions.FusionLink +{ + public class TransactionChangedEventArgs + { + public int PositionId { get; } + + public long TransactionId { get; } + + public bool IsLocal { get; } + + public TransactionChangedEventArgs(long tranactionId, int positionId, bool isLocal) + { + TransactionId = tranactionId; + PositionId = positionId; + IsLocal = isLocal; + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Listeners/TransactionEventListener.cs b/src/Servers/FusionLink/Listeners/TransactionEventListener.cs new file mode 100644 index 0000000..e3c4282 --- /dev/null +++ b/src/Servers/FusionLink/Listeners/TransactionEventListener.cs @@ -0,0 +1,34 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using sophis.portfolio; + +namespace RxdSolutions.FusionLink +{ + public class TransactionEventListener : CSMTransactionEvent + { + public static event EventHandler TransactionChanged; + + public override bool HasBeenCreated(CSMTransaction transaction) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetReference(), transaction.GetPositionID(), false)); + + return base.HasBeenCreated(transaction); + } + + public override bool HasBeenDeleted(CSMTransaction transaction) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetReference(), transaction.GetPositionID(), false)); + + return base.HasBeenDeleted(transaction); + } + + public override bool HasBeenModified(CSMTransaction original, CSMTransaction transaction) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetReference(), transaction.GetPositionID(), false)); + + return base.HasBeenModified(original, transaction); + } + } +} \ No newline at end of file diff --git a/src/Servers/FusionLink/Main.cs b/src/Servers/FusionLink/Main.cs index 75561a3..0eba5de 100644 --- a/src/Servers/FusionLink/Main.cs +++ b/src/Servers/FusionLink/Main.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Threading; +using RxdSolutions.FusionLink.Client; using RxdSolutions.FusionLink.Properties; using RxdSolutions.FusionLink.Services; using sophis; @@ -21,13 +22,19 @@ namespace RxdSolutions.FusionLink public class Main : IMain { private static CSMGlobalFunctions _globalFunctions; + private static PortfolioActionListener _portfolioActionListener; private static PortfolioEventListener _portfolioEventListener; - private static ServiceHost _host; - private static DataServer _dataServer; + private static PositionActionListener _positionActionListener; + private static PositionEventListener _positionEventListener; + private static TransactionActionListener _transactionActionListener; + private static TransactionEventListener _transactionEventListener; + private static ServiceHost _host; + private bool _displayDebugMessage = false; + public static DataServer DataServer; public static CaptionBar CaptionBar; public Dispatcher _context; @@ -52,8 +59,8 @@ public void Close() { try { - _dataServer.OnClientConnectionChanged -= OnClientConnectionChanged; - _dataServer.Close(); + DataServer.OnClientConnectionChanged -= OnClientConnectionChanged; + DataServer.Close(); _host?.Close(); } catch (Exception ex) @@ -97,12 +104,25 @@ private void RegisterListeners() _portfolioActionListener = new PortfolioActionListener(); CSMPortfolioAction.Register("PortfolioActionListener", CSMPortfolioAction.eMOrder.M_oAfter, _portfolioActionListener); + + _positionEventListener = new PositionEventListener(); + CSMPositionEvent.Register("PositionEventListener", CSMPositionEvent.eMOrder.M_oAfter, _positionEventListener); + + _positionActionListener = new PositionActionListener(); + CSMPositionAction.Register("PositionActionListener", CSMPositionAction.eMOrder.M_oAfter, _positionActionListener); + + _transactionEventListener = new TransactionEventListener(); + CSMTransactionEvent.Register("TransactionEventListener", CSMTransactionEvent.eMOrder.M_oAfter, _transactionEventListener); + + _transactionActionListener = new TransactionActionListener(); + CSMTransactionAction.Register("TransactionActionListener", CSMTransactionAction.eMOrder.M_oSavingInDataBase, _transactionActionListener); } private void RegisterScenarios() { CSMScenario.Register(Resources.ScenarioShowCaptionBarMessage, new ShowFusionLinkScenario()); CSMScenario.Register(Resources.OpenFusionLinkExcel, new OpenFusionLinkExcelScenario()); + CSMScenario.Register(Resources.ShowDiagnostics, new ShowDiagnosticsScenario()); } private void RegisterUI() @@ -125,15 +145,15 @@ private void OnCaptionBarButtonClicked(object sender, EventArgs e) { try { - if (_dataServer == null) + if (DataServer == null) return; - if (_dataServer.IsRunning) - _dataServer.Stop(); + if (DataServer.IsRunning) + DataServer.Stop(); else - _dataServer.Start(); + DataServer.Start(); - CaptionBar.ButtonText = _dataServer.IsRunning ? Resources.StopButtonText : Resources.StartButtonText; + CaptionBar.ButtonText = DataServer.IsRunning ? Resources.StopButtonText : Resources.StartButtonText; UpdateCaption(); } @@ -147,9 +167,14 @@ private void OnCaptionBarButtonClicked(object sender, EventArgs e) private Task RegisterServer() { - var aggregateListener = new AggregateListener(_portfolioActionListener, _portfolioEventListener); - var dataServiceProvider = new FusionDataServiceProvider(_globalFunctions as IGlobalFunctions, - aggregateListener, + var aggregatePortfolioListener = new AggregatePortfolioListener(_portfolioActionListener, _portfolioEventListener); + var aggregatePositionListener = new AggregatePositionListener(_positionActionListener, _positionEventListener); + var aggregateTransactionListener = new AggregateTransactionListener(_transactionActionListener, _transactionEventListener); + + var dataServiceProvider = new FusionDataServiceProvider(_globalFunctions as IGlobalFunctions, + aggregatePortfolioListener, + aggregatePositionListener, + aggregateTransactionListener, new PositionService(), new InstrumentService(), new CurveService()); @@ -160,7 +185,7 @@ private Task RegisterServer() { try { - _host = DataServerHostFactory.Create(_dataServer); + _host = DataServerHostFactory.Create(DataServer); _host.Faulted += Host_Faulted; } catch (AddressAlreadyInUseException) @@ -171,13 +196,13 @@ private Task RegisterServer() _host = null; } - _dataServer = _host.SingletonInstance as DataServer; + DataServer = _host.SingletonInstance as DataServer; - _dataServer.Start(); - _dataServer.OnClientConnectionChanged += OnClientConnectionChanged; + DataServer.Start(); + DataServer.OnClientConnectionChanged += OnClientConnectionChanged; if(_displayDebugMessage) - _dataServer.OnSubscriptionChanged += OnSubscriptionChanged; + DataServer.OnSubscriptionChanged += OnSubscriptionChanged; }); } @@ -197,12 +222,12 @@ private void OnSubscriptionChanged(object sender, EventArgs e) private static void CreateDataServerFromConfig(FusionDataServiceProvider dataServiceProvider) { - _dataServer = new DataServer(dataServiceProvider); + DataServer = new DataServer(dataServiceProvider); string defaultMessage = ""; CSMConfigurationFile.getEntryValue("FusionLink", "DefaultMessage", ref defaultMessage, Resources.DefaultDataLoadingMessage); - _dataServer.DefaultMessage = defaultMessage; + DataServer.DefaultMessage = defaultMessage; } private void LoadConfig() @@ -219,16 +244,16 @@ private void UpdateCaption() string dataServiceIdentifierCaption = string.Format(Resources.ConnectionIdMessage, processId); caption.Append(dataServiceIdentifierCaption); - if (_dataServer.ClientCount > 0) + if (DataServer.ClientCount > 0) { string clientsConnectedCaption = ""; - if (_dataServer.ClientCount == 1) + if (DataServer.ClientCount == 1) { - clientsConnectedCaption = string.Format(Resources.SingleClientConnectedMessage, _dataServer.ClientCount); + clientsConnectedCaption = string.Format(Resources.SingleClientConnectedMessage, DataServer.ClientCount); } - else if (_dataServer.ClientCount > 1) + else if (DataServer.ClientCount > 1) { - clientsConnectedCaption = string.Format(Resources.MultipleClientsConnectedMessage, _dataServer.ClientCount); + clientsConnectedCaption = string.Format(Resources.MultipleClientsConnectedMessage, DataServer.ClientCount); } caption.Append(" / "); @@ -237,7 +262,7 @@ private void UpdateCaption() if (_displayDebugMessage) { - var subs = $"(Subs = PortVal:{_dataServer.PortfolioValueSubscriptionCount},PortProp:{_dataServer.PortfolioPropertySubscriptionCount},Pos:{_dataServer.PositonValueSubscriptionCount},Sys:{_dataServer.SystemValueCount})"; + var subs = $"(Subs = PortVal:{DataServer.PortfolioValueSubscriptionCount},PortProp:{DataServer.PortfolioPropertySubscriptionCount},Pos:{DataServer.PositonValueSubscriptionCount},Sys:{DataServer.SystemValueCount})"; caption.Append(" / "); caption.Append(subs); } diff --git a/src/Servers/FusionLink/Properties/Resources.Designer.cs b/src/Servers/FusionLink/Properties/Resources.Designer.cs index b6ee155..6a33483 100644 --- a/src/Servers/FusionLink/Properties/Resources.Designer.cs +++ b/src/Servers/FusionLink/Properties/Resources.Designer.cs @@ -196,6 +196,15 @@ internal static string ScenarioShowCaptionBarMessage { } } + /// + /// Looks up a localized string similar to Display FusionLink Diagnostics. + /// + internal static string ShowDiagnostics { + get { + return ResourceManager.GetString("ShowDiagnostics", resourceCulture); + } + } + /// /// Looks up a localized string similar to Display FusionLink. /// diff --git a/src/Servers/FusionLink/Properties/Resources.resx b/src/Servers/FusionLink/Properties/Resources.resx index 8da90ae..9d712e3 100644 --- a/src/Servers/FusionLink/Properties/Resources.resx +++ b/src/Servers/FusionLink/Properties/Resources.resx @@ -163,6 +163,9 @@ Display FusionLink + + Display FusionLink Diagnostics + Display FusionLink diff --git a/src/Servers/FusionLink/Provider/CellValueBase.cs b/src/Servers/FusionLink/Provider/CellValueBase.cs index bcb5086..edc1928 100644 --- a/src/Servers/FusionLink/Provider/CellValueBase.cs +++ b/src/Servers/FusionLink/Provider/CellValueBase.cs @@ -12,17 +12,21 @@ internal abstract class CellValueBase : IDisposable public SSMCellStyle CellStyle = new SSMCellStyle(); + public CSMExtraction Extraction { get; } + public CSMPortfolioColumn Column { get; } public string ColumnName { get; } public Exception Error { get; set; } - protected CellValueBase(string columnName) + protected CellValueBase(string columnName, CSMExtraction extraction) { ColumnName = columnName; Column = CSMPortfolioColumn.GetCSRPortfolioColumn(columnName); + + Extraction = extraction; } public abstract object GetValue(); diff --git a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs index d3f2985..0932d45 100644 --- a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs +++ b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Windows.Threading; using RxdSolutions.FusionLink.Interface; using RxdSolutions.FusionLink.Services; @@ -12,13 +14,15 @@ namespace RxdSolutions.FusionLink { - public class FusionDataServiceProvider : IDataServerProvider + public class FusionDataServiceProvider : IDataServerProvider, IDisposable { private readonly string _className = nameof(FusionDataServiceProvider); private readonly Dispatcher _context; private readonly IGlobalFunctions _globalFunctions; private readonly IPortfolioListener _portfolioListener; + private readonly IPositionListener _positionListener; + private readonly ITransactionListener _transactionListener; private readonly PositionService _positionService; private readonly InstrumentService _instrumentService; private readonly CurveService _curveService; @@ -28,6 +32,8 @@ public class FusionDataServiceProvider : IDataServerProvider private readonly Subscriptions _positionCellSubscriptions; private readonly Dictionary _systemValueSubscriptions; + private readonly CSMExtraction _mainExtraction; + //Avoid infinite loops private int _computeCount; @@ -42,12 +48,17 @@ public bool HasSubscriptions { get { - return _portfolioCellSubscriptions.Count > 0 || _positionCellSubscriptions.Count > 0 || _systemValueSubscriptions.Count > 0; + return _portfolioCellSubscriptions.Count > 0 || + _positionCellSubscriptions.Count > 0 || + _systemValueSubscriptions.Count > 0 || + _portfolioPropertySubscriptions.Count > 0; } } public FusionDataServiceProvider(IGlobalFunctions globalFunctions, IPortfolioListener portfolioListener, + IPositionListener positionListener, + ITransactionListener transactionListener, PositionService positionService, InstrumentService instrumentService, CurveService curveService) @@ -55,17 +66,22 @@ public FusionDataServiceProvider(IGlobalFunctions globalFunctions, _context = Dispatcher.CurrentDispatcher; _globalFunctions = globalFunctions; _portfolioListener = portfolioListener; + _positionListener = positionListener; + _transactionListener = transactionListener; _positionService = positionService; _instrumentService = instrumentService; _curveService = curveService; + _mainExtraction = sophis.globals.CSMExtraction.gMain(); - _portfolioCellSubscriptions = new Subscriptions((i, s) => new PortfolioCellValue(i, s)); - _positionCellSubscriptions = new Subscriptions((i, s) => new PositionCellValue(i, s)); + _portfolioCellSubscriptions = new Subscriptions((i, s) => new PortfolioCellValue(i, s, _mainExtraction)); + _positionCellSubscriptions = new Subscriptions((i, s) => new PositionCellValue(i, s, _mainExtraction)); _portfolioPropertySubscriptions = new Subscriptions((i, s) => new PortfolioPropertyValue(i, s)); _systemValueSubscriptions = new Dictionary(); _globalFunctions.PortfolioCalculationEnded += GlobalFunctions_PortfolioCalculationEnded; _portfolioListener.PortfolioChanged += PortfolioListener_PortfolioChanged; + _positionListener.PositionChanged += PositionListener_PositionChanged; + _transactionListener.TransactionChanged += TransactionListener_TransactionChanged; } public void Start() @@ -519,12 +535,18 @@ void RefreshSystemValues() } } + var sw = Stopwatch.StartNew(); + RefreshPortfolioCells(); RefreshPositionCells(); RefreshSystemValues(); + sw.Stop(); + + args.TimeTaken = sw.Elapsed; + DataAvailable?.Invoke(this, args); } @@ -532,6 +554,17 @@ private void PortfolioListener_PortfolioChanged(object sender, PortfolioChangedE { try { + foreach (var cell in _portfolioCellSubscriptions.GetCells().ToList()) + { + if (cell.FolioId == e.PortfolioId) + { + cell.Dispose(); + _positionCellSubscriptions.Remove(cell.FolioId, cell.ColumnName); + } + + _portfolioCellSubscriptions.Add(e.PortfolioId, cell.ColumnName); + } + if (e.IsLocal) { //There must be a Sophis API call which does the same work without the risk of deadlock. @@ -566,6 +599,44 @@ private void PortfolioListener_PortfolioChanged(object sender, PortfolioChangedE } } + private void PositionListener_PositionChanged(object sender, PositionChangedEventArgs e) + { + try + { + foreach (var cell in _positionCellSubscriptions.GetCells().ToList()) + { + if(cell.PositionId == e.PositionId) + { + _positionCellSubscriptions.Remove(cell.PositionId, cell.ColumnName); + _positionCellSubscriptions.Add(e.PositionId, cell.ColumnName); + } + } + } + catch (Exception ex) + { + CSMLog.Write(_className, "PositionListener_PositionChanged", CSMLog.eMVerbosity.M_error, ex.ToString()); + } + } + + private void TransactionListener_TransactionChanged(object sender, TransactionChangedEventArgs e) + { + try + { + foreach (var cell in _positionCellSubscriptions.GetCells().ToList()) + { + if (cell.PositionId == e.PositionId) + { + _positionCellSubscriptions.Remove(cell.PositionId, cell.ColumnName); + _positionCellSubscriptions.Add(e.PositionId, cell.ColumnName); + } + } + } + catch (Exception ex) + { + CSMLog.Write(_className, "TransactionListener_TransactionChanged", CSMLog.eMVerbosity.M_error, ex.ToString()); + } + } + private SystemValue GetSystemValueProperty(SystemProperty property) { switch (property) @@ -632,5 +703,29 @@ public void LoadPositions() }, DispatcherPriority.ApplicationIdle); } + + #region IDisposable Support + + private bool disposedValue = false; + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _mainExtraction.Dispose(); + } + + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(true); + } + + #endregion } } \ No newline at end of file diff --git a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs index 817bda9..984371c 100644 --- a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs +++ b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs @@ -14,7 +14,7 @@ internal class PortfolioCellValue : CellValueBase public int FolioId { get; } - public PortfolioCellValue(int folioId, string column) : base(column) + public PortfolioCellValue(int folioId, string column, CSMExtraction extraction) : base(column, extraction) { FolioId = folioId; Portfolio = CSMPortfolio.GetCSRPortfolio(folioId); @@ -45,7 +45,7 @@ public override object GetValue() return string.Format(Resources.ColumnNotFoundMessage, ColumnName); } - Column.GetPortfolioCell(Portfolio.GetCode(), Portfolio.GetCode(), sophis.globals.CSMExtraction.gMain(), ref CellValue, CellStyle, false); + Column.GetPortfolioCell(Portfolio.GetCode(), Portfolio.GetCode(), Extraction, ref CellValue, CellStyle, false); var value = CellValue.ExtractValueFromSophisCell(CellStyle); diff --git a/src/Servers/FusionLink/Provider/PositionCellValue.cs b/src/Servers/FusionLink/Provider/PositionCellValue.cs index 49c87df..e6f0eeb 100644 --- a/src/Servers/FusionLink/Provider/PositionCellValue.cs +++ b/src/Servers/FusionLink/Provider/PositionCellValue.cs @@ -14,7 +14,7 @@ internal class PositionCellValue : CellValueBase public int PositionId { get; } - public PositionCellValue(int positionId, string column) : base(column) + public PositionCellValue(int positionId, string column, CSMExtraction extraction) : base(column, extraction) { PositionId = positionId; Position = CSMPosition.GetCSRPosition(positionId); @@ -37,7 +37,7 @@ public override object GetValue() if (Position is object) { - Column.GetPositionCell(Position, Position.GetPortfolioCode(), Position.GetPortfolioCode(), sophis.globals.CSMExtraction.gMain(), 0, Position.GetInstrumentCode(), ref CellValue, CellStyle, false); + Column.GetPositionCell(Position, Position.GetPortfolioCode(), Position.GetPortfolioCode(), Extraction, 0, Position.GetInstrumentCode(), ref CellValue, CellStyle, false); var value = CellValue.ExtractValueFromSophisCell(CellStyle); From 1db31d45af4e38bc5619583ddf811da861fab828 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sat, 29 Jun 2019 08:39:31 +0100 Subject: [PATCH 02/13] Updates to the Wpf UI --- .../FusionLink/Client/DiagnosticsView.xaml | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Servers/FusionLink/Client/DiagnosticsView.xaml b/src/Servers/FusionLink/Client/DiagnosticsView.xaml index 6ea6a48..c91564d 100644 --- a/src/Servers/FusionLink/Client/DiagnosticsView.xaml +++ b/src/Servers/FusionLink/Client/DiagnosticsView.xaml @@ -14,6 +14,11 @@ + @@ -26,7 +31,12 @@ - + + + + + + @@ -34,12 +44,22 @@ - + + + + + + + + + + - - + + - + + From c1bc335b573d89e692c71da0734a924da15ab851 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sat, 29 Jun 2019 22:10:28 +0100 Subject: [PATCH 03/13] Removed the button from the MFC Captionbar (Just v10 for now) --- libs/RXDSolutions/MFCCaptionBar10.dll | Bin 300544 -> 297472 bytes src/Controls/MFCCaptionBar10/CaptionBar.cpp | 121 +++----------------- src/Controls/MFCCaptionBar10/CaptionBar.h | 78 +++++-------- 3 files changed, 40 insertions(+), 159 deletions(-) diff --git a/libs/RXDSolutions/MFCCaptionBar10.dll b/libs/RXDSolutions/MFCCaptionBar10.dll index c81eb4d58301b0bf7ef6667da64fe28da36f64b4..6d2189eb1cde7f7c7104c4cfd344f48357ac1f43 100644 GIT binary patch literal 297472 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
  • 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 QFT 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 literal 300544 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 G2s{8gqRgiN diff --git a/src/Controls/MFCCaptionBar10/CaptionBar.cpp b/src/Controls/MFCCaptionBar10/CaptionBar.cpp index da96019..e14138b 100644 --- a/src/Controls/MFCCaptionBar10/CaptionBar.cpp +++ b/src/Controls/MFCCaptionBar10/CaptionBar.cpp @@ -8,99 +8,59 @@ using namespace System::Drawing; using namespace System::Runtime::InteropServices; using namespace msclr::interop; -CaptionBar::CaptionBar() -{ - del = gcnew GetMsgProcDelegate(this, &CaptionBar::GetMsgProc); -} - -LRESULT CALLBACK CaptionBar::GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) -{ - if (nCode < 0) // do not process message - return CallNextHookEx(hook, nCode, wParam, lParam); - - CWPSTRUCT cwp = *(CWPSTRUCT*)lParam; - if (cwp.message == WM_COMMAND) - { - if (cwp.wParam == ID_TOOLS_OPTIONS) - { - OnButtonClicked(this, gcnew System::EventArgs()); - } - } - - return CallNextHookEx(hook, nCode, wParam, lParam); -} - -static GCHandle gch; static CMFCCaptionBar m_wndCaptionBar; -void CaptionBar::Create() +void RxdSolutions::FusionLink::CaptionBar::Create() { - //if (!m_wndCaptionBar.Create(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, this, ID_VIEW_CAPTION_BAR, -1, TRUE)) BOOL result = m_wndCaptionBar.Create(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, AfxGetMainWnd(), 100000, -1, TRUE); - gch = GCHandle::Alloc(del); - IntPtr ip = Marshal::GetFunctionPointerForDelegate(del); - HOOKPROC hp = static_cast(ip.ToPointer()); - hook = SetWindowsHookEx(WH_CALLWNDPROC, hp, (HINSTANCE)NULL, GetCurrentThreadId()); - _isCreated = true; } -void CaptionBar::Update() +void RxdSolutions::FusionLink::CaptionBar::Update() { if (!_isCreated) { Create(); } - //Create the button - if (DisplayButton) - { - marshal_context context; - LPCTSTR cstrT = context.marshal_as(this->ButtonText); - LPCTSTR cstrTT = context.marshal_as(this->ButtonToolTip); - - m_wndCaptionBar.SetButton(cstrT, ID_TOOLS_OPTIONS, CMFCCaptionBar::ALIGN_LEFT, FALSE); - m_wndCaptionBar.SetButtonToolTip(cstrTT); - } - - if (this->Image != nullptr) - { - HBITMAP hbitMap = (HBITMAP)this->Image->GetHbitmap().ToInt32(); - m_wndCaptionBar.SetBitmap(hbitMap, RGB(255, 255, 255), FALSE, CMFCCaptionBar::ALIGN_LEFT); - } - m_wndCaptionBar.SetText(this->Text, CMFCCaptionBar::ALIGN_LEFT); } -void CaptionBar::Show() +void RxdSolutions::FusionLink::CaptionBar::Show() { if (!_isCreated) { Create(); } + if (this->Image != nullptr) + { + HBITMAP hbitMap = (HBITMAP)this->Image->GetHbitmap().ToInt32(); + m_wndCaptionBar.SetBitmap(hbitMap, RGB(255, 255, 255), FALSE, CMFCCaptionBar::ALIGN_LEFT); + } + m_wndCaptionBar.ShowWindow(SW_SHOW); Update(); } -Bitmap^ CaptionBar::Image::get() +Bitmap^ RxdSolutions::FusionLink::CaptionBar::Image::get() { return _image; } -void CaptionBar::Image::set(Bitmap^ value) +void RxdSolutions::FusionLink::CaptionBar::Image::set(Bitmap^ value) { _image = value; } -System::String^ CaptionBar::Text::get() +System::String^ RxdSolutions::FusionLink::CaptionBar::Text::get() { return _messageText; } -void CaptionBar::Text::set(System::String^ value) +void RxdSolutions::FusionLink::CaptionBar::Text::set(System::String^ value) { _messageText = value; @@ -109,58 +69,3 @@ void CaptionBar::Text::set(System::String^ value) m_wndCaptionBar.SetText(this->Text, CMFCCaptionBar::ALIGN_LEFT); } } - -System::String^ CaptionBar::ButtonText::get() -{ - return _buttonText; -} - -void CaptionBar::ButtonText::set(System::String^ value) -{ - _buttonText = value; - - if (_isCreated) - { - m_wndCaptionBar.SetText(this->Text, CMFCCaptionBar::ALIGN_LEFT); - } -} - -System::String^ CaptionBar::ButtonToolTip::get() -{ - return _buttonToolTip; -} - -void CaptionBar::ButtonToolTip::set(System::String^ value) -{ - _buttonToolTip = value; - - if (_isCreated && DisplayButton) - { - marshal_context context; - LPCTSTR cstrT = context.marshal_as(this->ButtonText); - LPCTSTR cstrTT = context.marshal_as(this->ButtonToolTip); - - m_wndCaptionBar.SetButton(cstrT, ID_TOOLS_OPTIONS, CMFCCaptionBar::ALIGN_LEFT, FALSE); - m_wndCaptionBar.SetButtonToolTip(cstrTT); - } -} - -System::Boolean^ CaptionBar::DisplayButton::get() -{ - return _displayButton; -} - -void CaptionBar::DisplayButton::set(System::Boolean^ value) -{ - _displayButton = value; - - if (_isCreated && DisplayButton) - { - marshal_context context; - LPCTSTR cstrT = context.marshal_as(this->ButtonText); - LPCTSTR cstrTT = context.marshal_as(this->ButtonToolTip); - - m_wndCaptionBar.SetButton(cstrT, ID_TOOLS_OPTIONS, CMFCCaptionBar::ALIGN_LEFT, FALSE); - m_wndCaptionBar.SetButtonToolTip(cstrTT); - } -} \ No newline at end of file diff --git a/src/Controls/MFCCaptionBar10/CaptionBar.h b/src/Controls/MFCCaptionBar10/CaptionBar.h index c8dfdca..28bcab1 100644 --- a/src/Controls/MFCCaptionBar10/CaptionBar.h +++ b/src/Controls/MFCCaptionBar10/CaptionBar.h @@ -4,65 +4,41 @@ using namespace System; using namespace System::Drawing; -delegate LRESULT GetMsgProcDelegate(int nCode, WPARAM wParam, LPARAM lParam); - -public ref class CaptionBar +namespace RxdSolutions { -private: - System::Boolean^ _isCreated; - - System::String^ _messageText; - Bitmap^ _image; - - System::Boolean^ _displayButton; - System::String^ _buttonText; - System::String^ _buttonToolTip; - - HHOOK hook; - LRESULT WINAPI GetMsgProc(int, WPARAM, LPARAM); - GetMsgProcDelegate^ del; +namespace FusionLink +{ + public ref class CaptionBar + { + private: + System::Boolean^ _isCreated; - void Create(); - void Update(); + System::String^ _messageText; + Bitmap^ _image; -public: - CaptionBar(); + void Create(); + void Update(); - event EventHandler^ OnButtonClicked; + public: - void Show(); + void Show(); - property Bitmap^ Image - { - Bitmap^ get(); - void set(Bitmap^ value); - } + property Bitmap^ Image + { + Bitmap^ get(); + void set(Bitmap^ value); + } - property System::String^ Text - { - System::String^ get(); - void set(System::String^ value); - } + property System::String^ Text + { + System::String^ get(); + void set(System::String^ value); + } - property System::String^ ButtonText - { - System::String^ get(); - void set(System::String^ value); - } - - property System::String^ ButtonToolTip - { - System::String^ get(); - void set(System::String^ value); - } - - property System::Boolean^ DisplayButton - { - System::Boolean^ get(); - void set(System::Boolean^ value); - } + protected: -protected: + }; +} +} -}; From 036bc0da9ae7133d1b8e6adfe8bc0370ecc42adf Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sat, 29 Jun 2019 22:11:04 +0100 Subject: [PATCH 04/13] Changed the initial value returned to excel to make it clear it is subscribing --- .../Observables/PortfolioPropertyExcelObservable.cs | 3 ++- .../Observables/PortfolioValueExcelObservable.cs | 3 ++- .../Observables/PositionValueExcelObservable.cs | 3 ++- .../Observables/SystemPropertyExcelObservable.cs | 3 ++- src/Clients/ExcelClient/Properties/Resources.Designer.cs | 9 +++++++++ src/Clients/ExcelClient/Properties/Resources.resx | 3 +++ 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Clients/ExcelClient/Observables/PortfolioPropertyExcelObservable.cs b/src/Clients/ExcelClient/Observables/PortfolioPropertyExcelObservable.cs index 44786ed..da5bb3a 100644 --- a/src/Clients/ExcelClient/Observables/PortfolioPropertyExcelObservable.cs +++ b/src/Clients/ExcelClient/Observables/PortfolioPropertyExcelObservable.cs @@ -3,6 +3,7 @@ using System; using ExcelDna.Integration; +using RxdSolutions.FusionLink.ExcelClient.Properties; using RxdSolutions.FusionLink.Interface; namespace RxdSolutions.FusionLink.ExcelClient @@ -30,7 +31,7 @@ public IDisposable Subscribe(IExcelObserver observer) try { - _observer.OnNext(ExcelEmpty.Value); + _observer.OnNext(Resources.SubscribingToData); _rtdClient.SubscribeToPortfolioProperty(PortfolioId, Property); } catch(Exception ex) diff --git a/src/Clients/ExcelClient/Observables/PortfolioValueExcelObservable.cs b/src/Clients/ExcelClient/Observables/PortfolioValueExcelObservable.cs index 002ca60..b732c98 100644 --- a/src/Clients/ExcelClient/Observables/PortfolioValueExcelObservable.cs +++ b/src/Clients/ExcelClient/Observables/PortfolioValueExcelObservable.cs @@ -3,6 +3,7 @@ using System; using ExcelDna.Integration; +using RxdSolutions.FusionLink.ExcelClient.Properties; namespace RxdSolutions.FusionLink.ExcelClient { @@ -29,7 +30,7 @@ public IDisposable Subscribe(IExcelObserver observer) try { - _observer.OnNext(ExcelEmpty.Value); + _observer.OnNext(Resources.SubscribingToData); _rtdClient.SubscribeToPortfolioValue(PortfolioId, Column); } catch(Exception ex) diff --git a/src/Clients/ExcelClient/Observables/PositionValueExcelObservable.cs b/src/Clients/ExcelClient/Observables/PositionValueExcelObservable.cs index 8ba0af8..2e87b7b 100644 --- a/src/Clients/ExcelClient/Observables/PositionValueExcelObservable.cs +++ b/src/Clients/ExcelClient/Observables/PositionValueExcelObservable.cs @@ -3,6 +3,7 @@ using System; using ExcelDna.Integration; +using RxdSolutions.FusionLink.ExcelClient.Properties; namespace RxdSolutions.FusionLink.ExcelClient { @@ -30,7 +31,7 @@ public IDisposable Subscribe(IExcelObserver observer) try { - _observer.OnNext(ExcelEmpty.Value); + _observer.OnNext(Resources.SubscribingToData); _rtdClient.SubscribeToPositionValue(PositionId, Column); } catch(Exception ex) diff --git a/src/Clients/ExcelClient/Observables/SystemPropertyExcelObservable.cs b/src/Clients/ExcelClient/Observables/SystemPropertyExcelObservable.cs index 972daa1..1e416f1 100644 --- a/src/Clients/ExcelClient/Observables/SystemPropertyExcelObservable.cs +++ b/src/Clients/ExcelClient/Observables/SystemPropertyExcelObservable.cs @@ -3,6 +3,7 @@ using System; using ExcelDna.Integration; +using RxdSolutions.FusionLink.ExcelClient.Properties; using RxdSolutions.FusionLink.Interface; namespace RxdSolutions.FusionLink.ExcelClient @@ -28,7 +29,7 @@ public IDisposable Subscribe(IExcelObserver observer) try { - _observer.OnNext(ExcelEmpty.Value); + _observer.OnNext(Resources.SubscribingToData); _rtdClient.SubscribeToSystemValue(SystemProperty); } catch(Exception ex) diff --git a/src/Clients/ExcelClient/Properties/Resources.Designer.cs b/src/Clients/ExcelClient/Properties/Resources.Designer.cs index 0128e73..fdbaba1 100644 --- a/src/Clients/ExcelClient/Properties/Resources.Designer.cs +++ b/src/Clients/ExcelClient/Properties/Resources.Designer.cs @@ -167,5 +167,14 @@ internal static string StartDateGreaterThanEndDateMessage { return ResourceManager.GetString("StartDateGreaterThanEndDateMessage", resourceCulture); } } + + /// + /// Looks up a localized string similar to Subscribing... please wait. + /// + internal static string SubscribingToData { + get { + return ResourceManager.GetString("SubscribingToData", resourceCulture); + } + } } } diff --git a/src/Clients/ExcelClient/Properties/Resources.resx b/src/Clients/ExcelClient/Properties/Resources.resx index 09cce9d..36c97d5 100644 --- a/src/Clients/ExcelClient/Properties/Resources.resx +++ b/src/Clients/ExcelClient/Properties/Resources.resx @@ -153,4 +153,7 @@ Start date must be before the end date + + Subscribing... please wait + \ No newline at end of file From 8ac16ba8577804c0c7cfda52dd7876a49606a2c9 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sat, 29 Jun 2019 22:11:39 +0100 Subject: [PATCH 05/13] Changes to support the new Dashboard feature Change to update the subscrptions based on events in the system. --- src/Server.sln | 70 +++ .../DataServer.Wpf.csproj | 76 +++ .../DataServer.Wpf - Copy/DelegateCommand.cs | 45 ++ .../DiagnosticsView.xaml | 103 ++++ .../DiagnosticsView.xaml.cs | 17 + .../DiagnosticsViewModel.cs | 93 +++- .../Properties/Resources.Designer.cs | 90 ++++ .../Properties/Resources.resx | 129 +++++ src/Servers/DataServer.Wpf - Copy/app.config | 19 + .../DataServer.Wpf - Copy/mainimage_light.svg | 451 ++++++++++++++++++ .../mainimage_light.xaml | 196 ++++++++ .../DataServer.Wpf/DataServer.Wpf.csproj | 87 ++++ src/Servers/DataServer.Wpf/DelegateCommand.cs | 45 ++ .../DataServer.Wpf/DiagnosticsView.xaml | 338 +++++++++++++ .../DataServer.Wpf/DiagnosticsView.xaml.cs | 22 + .../DataServer.Wpf/DiagnosticsViewModel.cs | 198 ++++++++ .../Properties/Resources.Designer.cs | 90 ++++ .../DataServer.Wpf/Properties/Resources.resx | 129 +++++ .../RxdSolutionsBackground.xaml | 196 ++++++++ .../DataServer.Wpf/RxdSolutionsLogo.xaml | 28 ++ src/Servers/DataServer.Wpf/app.config | 19 + src/Servers/DataServer.Wpf/mainlogo.svg | 1 + src/Servers/DataServer/DataServer.cs | 18 +- src/Servers/DataServer/DataServer.csproj | 2 +- .../FusionLink/Client/DiagnosticsView.xaml | 66 --- .../FusionLink/Client/DiagnosticsView.xaml.cs | 30 -- ...csScenario.cs => ShowDashboardScenario.cs} | 6 +- src/Servers/FusionLink/FusionLink.csproj | 9 +- .../Listeners/PositionActionListener.cs | 6 +- .../Listeners/TransactionActionListener.cs | 6 +- src/Servers/FusionLink/Main.cs | 59 +-- .../Properties/Resources.Designer.cs | 24 +- .../FusionLink/Properties/Resources.resx | 10 +- .../FusionLink/Provider/CellValueBase.cs | 2 +- .../Provider/FusionDataServiceProvider.cs | 119 +++-- .../FusionLink/Provider/PortfolioCellValue.cs | 28 +- .../FusionLink/Provider/PositionCellValue.cs | 24 +- 37 files changed, 2613 insertions(+), 238 deletions(-) create mode 100644 src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj create mode 100644 src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs create mode 100644 src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml create mode 100644 src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs rename src/Servers/{FusionLink/Client => DataServer.Wpf - Copy}/DiagnosticsViewModel.cs (69%) create mode 100644 src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs create mode 100644 src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx create mode 100644 src/Servers/DataServer.Wpf - Copy/app.config create mode 100644 src/Servers/DataServer.Wpf - Copy/mainimage_light.svg create mode 100644 src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml create mode 100644 src/Servers/DataServer.Wpf/DataServer.Wpf.csproj create mode 100644 src/Servers/DataServer.Wpf/DelegateCommand.cs create mode 100644 src/Servers/DataServer.Wpf/DiagnosticsView.xaml create mode 100644 src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs create mode 100644 src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs create mode 100644 src/Servers/DataServer.Wpf/Properties/Resources.Designer.cs create mode 100644 src/Servers/DataServer.Wpf/Properties/Resources.resx create mode 100644 src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml create mode 100644 src/Servers/DataServer.Wpf/RxdSolutionsLogo.xaml create mode 100644 src/Servers/DataServer.Wpf/app.config create mode 100644 src/Servers/DataServer.Wpf/mainlogo.svg delete mode 100644 src/Servers/FusionLink/Client/DiagnosticsView.xaml delete mode 100644 src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs rename src/Servers/FusionLink/Client/{ShowDiagnosticsScenario.cs => ShowDashboardScenario.cs} (84%) diff --git a/src/Server.sln b/src/Server.sln index 07b55d8..3e54f19 100644 --- a/src/Server.sln +++ b/src/Server.sln @@ -18,12 +18,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataServiceInterface", "Sha EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FusionLink", "Servers\FusionLink\FusionLink.csproj", "{17BF2445-0643-4CDF-B678-7F3472910D9B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataServer.Wpf", "Servers\DataServer.Wpf\DataServer.Wpf.csproj", "{4EA8C78B-118D-4FD4-A745-2EBED8F68062}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_713|Any CPU = Debug_713|Any CPU Debug_713|x64 = Debug_713|x64 Debug_72|Any CPU = Debug_72|Any CPU Debug_72|x64 = Debug_72|x64 + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 Develop_713|Any CPU = Develop_713|Any CPU Develop_713|x64 = Develop_713|x64 Develop_72|Any CPU = Develop_72|Any CPU @@ -32,6 +36,8 @@ Global Release_713|x64 = Release_713|x64 Release_72|Any CPU = Release_72|Any CPU Release_72|x64 = Release_72|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug_713|Any CPU.ActiveCfg = Debug|Any CPU @@ -42,6 +48,10 @@ Global {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug_72|Any CPU.Build.0 = Debug|Any CPU {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug_72|x64.ActiveCfg = Debug|Any CPU {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug_72|x64.Build.0 = Debug|Any CPU + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug|x64.ActiveCfg = Debug|x64 + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Debug|x64.Build.0 = Debug|x64 {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Develop_713|Any CPU.ActiveCfg = Debug|Any CPU {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Develop_713|Any CPU.Build.0 = Debug|Any CPU {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Develop_713|x64.ActiveCfg = Debug|Any CPU @@ -58,6 +68,10 @@ Global {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release_72|Any CPU.Build.0 = Release|Any CPU {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release_72|x64.ActiveCfg = Release|Any CPU {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release_72|x64.Build.0 = Release|Any CPU + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release|Any CPU.Build.0 = Release|Any CPU + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release|x64.ActiveCfg = Release|x64 + {1D5FF3ED-123D-41FC-A6ED-C2C763A71168}.Release|x64.Build.0 = Release|x64 {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug_713|Any CPU.ActiveCfg = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug_713|Any CPU.Build.0 = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug_713|x64.ActiveCfg = Debug|Any CPU @@ -66,6 +80,10 @@ Global {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug_72|Any CPU.Build.0 = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug_72|x64.ActiveCfg = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug_72|x64.Build.0 = Debug|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Debug|x64.Build.0 = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Develop_713|Any CPU.ActiveCfg = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Develop_713|Any CPU.Build.0 = Debug|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Develop_713|x64.ActiveCfg = Debug|Any CPU @@ -82,6 +100,10 @@ Global {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release_72|Any CPU.Build.0 = Release|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release_72|x64.ActiveCfg = Release|Any CPU {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release_72|x64.Build.0 = Release|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release|Any CPU.Build.0 = Release|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release|x64.ActiveCfg = Release|Any CPU + {34BEF956-77AB-4AB6-87EC-2A72798941DE}.Release|x64.Build.0 = Release|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug_713|Any CPU.ActiveCfg = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug_713|Any CPU.Build.0 = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug_713|x64.ActiveCfg = Debug|Any CPU @@ -90,6 +112,10 @@ Global {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug_72|Any CPU.Build.0 = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug_72|x64.ActiveCfg = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug_72|x64.Build.0 = Debug|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug|x64.ActiveCfg = Debug|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Debug|x64.Build.0 = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Develop_713|Any CPU.ActiveCfg = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Develop_713|Any CPU.Build.0 = Debug|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Develop_713|x64.ActiveCfg = Debug|Any CPU @@ -106,6 +132,10 @@ Global {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release_72|Any CPU.Build.0 = Release|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release_72|x64.ActiveCfg = Release|Any CPU {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release_72|x64.Build.0 = Release|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release|Any CPU.Build.0 = Release|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release|x64.ActiveCfg = Release|Any CPU + {DA859D73-3D82-4EE1-BC66-1D47D16E5AC4}.Release|x64.Build.0 = Release|Any CPU {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug_713|Any CPU.ActiveCfg = Debug_713|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug_713|Any CPU.Build.0 = Debug_713|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug_713|x64.ActiveCfg = Debug_72|x64 @@ -114,6 +144,10 @@ Global {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug_72|Any CPU.Build.0 = Debug_72|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug_72|x64.ActiveCfg = Debug_72|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug_72|x64.Build.0 = Debug_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug|Any CPU.ActiveCfg = Release_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug|Any CPU.Build.0 = Release_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug|x64.ActiveCfg = Debug_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Debug|x64.Build.0 = Debug_72|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Develop_713|Any CPU.ActiveCfg = Develop_713|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Develop_713|Any CPU.Build.0 = Develop_713|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Develop_713|x64.ActiveCfg = Develop_713|x64 @@ -130,6 +164,42 @@ Global {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release_72|Any CPU.Build.0 = Release_72|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release_72|x64.ActiveCfg = Release_72|x64 {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release_72|x64.Build.0 = Release_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release|Any CPU.ActiveCfg = Release_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release|Any CPU.Build.0 = Release_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release|x64.ActiveCfg = Release_72|x64 + {17BF2445-0643-4CDF-B678-7F3472910D9B}.Release|x64.Build.0 = Release_72|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_713|Any CPU.ActiveCfg = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_713|Any CPU.Build.0 = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_713|x64.ActiveCfg = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_713|x64.Build.0 = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_72|Any CPU.ActiveCfg = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_72|Any CPU.Build.0 = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_72|x64.ActiveCfg = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug_72|x64.Build.0 = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug|x64.ActiveCfg = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Debug|x64.Build.0 = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_713|Any CPU.ActiveCfg = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_713|Any CPU.Build.0 = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_713|x64.ActiveCfg = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_713|x64.Build.0 = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_72|Any CPU.ActiveCfg = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_72|Any CPU.Build.0 = Debug|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_72|x64.ActiveCfg = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Develop_72|x64.Build.0 = Debug|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_713|Any CPU.ActiveCfg = Release|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_713|Any CPU.Build.0 = Release|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_713|x64.ActiveCfg = Release|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_713|x64.Build.0 = Release|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_72|Any CPU.ActiveCfg = Release|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_72|Any CPU.Build.0 = Release|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_72|x64.ActiveCfg = Release|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release_72|x64.Build.0 = Release|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release|Any CPU.Build.0 = Release|Any CPU + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release|x64.ActiveCfg = Release|x64 + {4EA8C78B-118D-4FD4-A745-2EBED8F68062}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj b/src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj new file mode 100644 index 0000000..ee11078 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj @@ -0,0 +1,76 @@ + + + + {4EA8C78B-118D-4FD4-A745-2EBED8F68062} + Library + RxdSolutions.FusionLink + DataServer.Wpf + net452 + 512 + AnyCPU;x64 + Debug;Release; + RXD Solutions + RXD Solutions + FusionLink + Copyright ©2019 RXD Solutions + https://rxdsolutions.github.io/FusionLink/ + https://github.com/RXDSolutions/FusionLink + + + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + + + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs b/src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs new file mode 100644 index 0000000..cf25e61 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs @@ -0,0 +1,45 @@ +using System; +using System.Windows.Input; + +namespace RxdSolutions.FusionLink.Client +{ + public class DelegateCommand : ICommand + { + private readonly Predicate _canExecute; + private readonly Action _execute; + + public event EventHandler CanExecuteChanged; + + public DelegateCommand(Action execute) + : this(execute, null) + { + } + + public DelegateCommand(Action execute, + Predicate canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public bool CanExecute(object parameter) + { + if (_canExecute == null) + { + return true; + } + + return _canExecute(parameter); + } + + public void Execute(object parameter) + { + _execute(parameter); + } + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml b/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml new file mode 100644 index 0000000..4773564 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FusionLink + + + + + + + + + + + + + + + + + Clients connected: + + + Listening on: + + + Terminal Services: + + + Last refresh duration: + + + + Average refresh duration: + + + + Data server running: + + + + + + + diff --git a/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs b/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs new file mode 100644 index 0000000..81added --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs @@ -0,0 +1,17 @@ +using System.Windows.Controls; + +namespace RxdSolutions.FusionLink.Client +{ + /// + /// Interaction logic for DiagnosticsView.xaml + /// + public partial class DiagnosticsView : UserControl + { + public DiagnosticsView(DiagnosticsViewModel viewModel) + { + InitializeComponent(); + + DataContext = viewModel; + } + } +} diff --git a/src/Servers/FusionLink/Client/DiagnosticsViewModel.cs b/src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs similarity index 69% rename from src/Servers/FusionLink/Client/DiagnosticsViewModel.cs rename to src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs index 328ca09..4c50060 100644 --- a/src/Servers/FusionLink/Client/DiagnosticsViewModel.cs +++ b/src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs @@ -1,6 +1,8 @@ using System; using System.ComponentModel; using System.Runtime.CompilerServices; +using System.Windows; +using RxdSolutions.FusionLink.Properties; namespace RxdSolutions.FusionLink.Client { @@ -12,9 +14,11 @@ public class DiagnosticsViewModel : INotifyPropertyChanged private int _portfolioPropertySubscriptionCount; private int _clientCount; private TimeSpan _lastTimeTaken; - private readonly DataServer _dataServer; + private TimeSpan _refreshTimeTaken; + private int _numberOfRefreshes; + public DiagnosticsViewModel(DataServer dataServer) { _dataServer = dataServer; @@ -27,29 +31,14 @@ public DiagnosticsViewModel(DataServer dataServer) PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; ClientCount = _dataServer.ClientCount; - - } - - private void OnDataReceived(object sender, DataAvailableEventArgs e) - { - LastTimeTaken = e.TimeTaken; - } - private void OnClientConnectionChanged(object sender, ClientConnectionChangedEventArgs e) - { - ClientCount = _dataServer.ClientCount; - } - - private void OnSubscriptionChanged(object sender, EventArgs e) - { - PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; - SystemSubscriptionCount = _dataServer.SystemValueCount; - PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; - PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; + ToggleDataServer = new DelegateCommand(ExecuteToggleDataServer); } public event PropertyChangedEventHandler PropertyChanged; + public DelegateCommand ToggleDataServer { get; } + public int ClientCount { get { return _clientCount; } @@ -60,6 +49,11 @@ public int ClientCount } } + public bool IsRunning + { + get { return _dataServer.IsRunning; } + } + public TimeSpan LastTimeTaken { get { return _lastTimeTaken; } @@ -70,6 +64,17 @@ public TimeSpan LastTimeTaken } } + public TimeSpan AverageTimeTaken + { + get + { + if (_numberOfRefreshes == 0) + return new TimeSpan(0); + + return new TimeSpan(0, 0, 0, 0, Convert.ToInt32(_lastTimeTaken.TotalMilliseconds / _numberOfRefreshes)); + } + } + public string ServerUri { get { return DataServerHostFactory.GetListeningAddress().ToString(); } @@ -123,9 +128,57 @@ public int PortfolioPropertySubscriptionCount } } - public void OnPropertyChanged([CallerMemberName] string callerMemberName = "") + public string ToggleDataServerLabel + { + get { return IsRunning ? Resources.StopButtonText : Resources.StartButtonText; } + } + + private void OnPropertyChanged([CallerMemberName] string callerMemberName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(callerMemberName)); } + + private void OnDataReceived(object sender, DataAvailableEventArgs e) + { + LastTimeTaken = e.TimeTaken; + _refreshTimeTaken += e.TimeTaken; + _numberOfRefreshes++; + + OnPropertyChanged("AverageTimeTaken"); + } + + private void OnClientConnectionChanged(object sender, ClientConnectionChangedEventArgs e) + { + ClientCount = _dataServer.ClientCount; + } + + private void OnSubscriptionChanged(object sender, EventArgs e) + { + PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; + SystemSubscriptionCount = _dataServer.SystemValueCount; + PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; + PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; + } + + private void ExecuteToggleDataServer(object sender) + { + try + { + if (_dataServer == null) + return; + + if (_dataServer.IsRunning) + _dataServer.Stop(); + else + _dataServer.Start(); + + OnPropertyChanged("IsRunning"); + OnPropertyChanged("ToggleDataServerLabel"); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, Resources.ErrorCaption, MessageBoxButton.OK, MessageBoxImage.Error); + } + } } } diff --git a/src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs b/src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d4c93ff --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RxdSolutions.FusionLink.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RxdSolutions.FusionLink.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to FusionLink. + /// + internal static string ErrorCaption { + get { + return ResourceManager.GetString("ErrorCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start. + /// + internal static string StartButtonText { + get { + return ResourceManager.GetString("StartButtonText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stop. + /// + internal static string StopButtonText { + get { + return ResourceManager.GetString("StopButtonText", resourceCulture); + } + } + } +} diff --git a/src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx b/src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx new file mode 100644 index 0000000..197a197 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FusionLink + + + Start + + + Stop + + \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf - Copy/app.config b/src/Servers/DataServer.Wpf - Copy/app.config new file mode 100644 index 0000000..88d3c81 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Servers/DataServer.Wpf - Copy/mainimage_light.svg b/src/Servers/DataServer.Wpf - Copy/mainimage_light.svg new file mode 100644 index 0000000..29a2bc8 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/mainimage_light.svg @@ -0,0 +1,451 @@ + +image/svg+xml \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml b/src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml new file mode 100644 index 0000000..c9388b7 --- /dev/null +++ b/src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj b/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj new file mode 100644 index 0000000..e79b999 --- /dev/null +++ b/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj @@ -0,0 +1,87 @@ + + + + {4EA8C78B-118D-4FD4-A745-2EBED8F68062} + Library + RxdSolutions.FusionLink + DataServer.Wpf + net452 + 512 + AnyCPU;x64 + Debug;Release; + RXD Solutions + RXD Solutions + FusionLink + Copyright ©2019 RXD Solutions + https://rxdsolutions.github.io/FusionLink/ + https://github.com/RXDSolutions/FusionLink + + + x64 + + + + + + + + MSBuild:Compile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + + + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + MSBuild:Compile + + + \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf/DelegateCommand.cs b/src/Servers/DataServer.Wpf/DelegateCommand.cs new file mode 100644 index 0000000..cf25e61 --- /dev/null +++ b/src/Servers/DataServer.Wpf/DelegateCommand.cs @@ -0,0 +1,45 @@ +using System; +using System.Windows.Input; + +namespace RxdSolutions.FusionLink.Client +{ + public class DelegateCommand : ICommand + { + private readonly Predicate _canExecute; + private readonly Action _execute; + + public event EventHandler CanExecuteChanged; + + public DelegateCommand(Action execute) + : this(execute, null) + { + } + + public DelegateCommand(Action execute, + Predicate canExecute) + { + _execute = execute; + _canExecute = canExecute; + } + + public bool CanExecute(object parameter) + { + if (_canExecute == null) + { + return true; + } + + return _canExecute(parameter); + } + + public void Execute(object parameter) + { + _execute(parameter); + } + + public void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml new file mode 100644 index 0000000..1f19973 --- /dev/null +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FusionLink + + + + + + + + + + + + + + + + + + + + + + + Clients connected: + + + Listening on: + + + Terminal Services: + + + Last refresh duration: + + + + Average refresh duration: + + + + Data server running: + + + + + Publish queue: + + + + + diff --git a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs new file mode 100644 index 0000000..bb148fe --- /dev/null +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs @@ -0,0 +1,22 @@ +using System.Windows.Controls; + +namespace RxdSolutions.FusionLink.Client +{ + /// + /// Interaction logic for DiagnosticsView.xaml + /// + public partial class DiagnosticsView : UserControl + { + public DiagnosticsView(DiagnosticsViewModel viewModel) + { + InitializeComponent(); + + DataContext = viewModel; + } + + private void Logo_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + System.Diagnostics.Process.Start("http://www.rxdsolutions.co.uk/FusionLink"); + } + } +} diff --git a/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs b/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs new file mode 100644 index 0000000..2d09770 --- /dev/null +++ b/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs @@ -0,0 +1,198 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows; +using RxdSolutions.FusionLink.Properties; + +namespace RxdSolutions.FusionLink.Client +{ + public class DiagnosticsViewModel : INotifyPropertyChanged + { + private int _portfolioSubscriptionCount; + private int _systemSubscriptionCount; + private int _positionSubscriptionCount; + private int _portfolioPropertySubscriptionCount; + private int _clientCount; + private TimeSpan _lastTimeTaken; + private readonly DataServer _dataServer; + + private TimeSpan _refreshTimeTaken; + private int _numberOfRefreshes; + + public DiagnosticsViewModel(DataServer dataServer) + { + _dataServer = dataServer; + _dataServer.OnSubscriptionChanged += OnSubscriptionChanged; + _dataServer.OnClientConnectionChanged += OnClientConnectionChanged; + _dataServer.OnDataReceived += OnDataReceived; + _dataServer.OnPublishQueueChanged += OnPublishQueueChanged; + + PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; + SystemSubscriptionCount = _dataServer.SystemValueCount; + PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; + PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; + ClientCount = _dataServer.ClientCount; + + ToggleDataServer = new DelegateCommand(ExecuteToggleDataServer); + } + + public event PropertyChangedEventHandler PropertyChanged; + + public DelegateCommand ToggleDataServer { get; } + + public int ClientCount + { + get { return _clientCount; } + set + { + _clientCount = value; + OnPropertyChanged(); + } + } + + public bool IsRunning + { + get { return _dataServer.IsRunning; } + } + + public TimeSpan LastTimeTaken + { + get { return _lastTimeTaken; } + set + { + _lastTimeTaken = value; + OnPropertyChanged(); + } + } + + public TimeSpan AverageTimeTaken + { + get + { + if (_numberOfRefreshes == 0) + return new TimeSpan(0); + + return new TimeSpan(0, 0, 0, 0, Convert.ToInt32(_refreshTimeTaken.TotalMilliseconds / _numberOfRefreshes)); + } + } + + public int PublishQueueCount + { + get + { + return _dataServer.PublishQueueCount; + } + } + + public string ServerUri + { + get { return DataServerHostFactory.GetListeningAddress().ToString(); } + } + + public bool IsTerminalServices + { + get + { + return System.Windows.Forms.SystemInformation.TerminalServerSession; + } + } + + public int PortfolioSubscriptionCount + { + get { return _portfolioSubscriptionCount; } + set + { + _portfolioSubscriptionCount = value; + OnPropertyChanged(); + } + } + + public int SystemSubscriptionCount + { + get { return _systemSubscriptionCount; } + set + { + _systemSubscriptionCount = value; + OnPropertyChanged(); + } + } + + public int PositionSubscriptionCount + { + get { return _positionSubscriptionCount; } + set + { + _positionSubscriptionCount = value; + OnPropertyChanged(); + } + } + + public int PortfolioPropertySubscriptionCount + { + get { return _portfolioPropertySubscriptionCount; } + set + { + _portfolioPropertySubscriptionCount = value; + OnPropertyChanged(); + } + } + + public string ToggleDataServerLabel + { + get { return IsRunning ? Resources.StopButtonText : Resources.StartButtonText; } + } + + private void OnPropertyChanged([CallerMemberName] string callerMemberName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(callerMemberName)); + } + + private void OnDataReceived(object sender, DataAvailableEventArgs e) + { + LastTimeTaken = e.TimeTaken; + _refreshTimeTaken += e.TimeTaken; + _numberOfRefreshes++; + + OnPropertyChanged(nameof(AverageTimeTaken)); + } + + private void OnPublishQueueChanged(object sender, EventArgs e) + { + OnPropertyChanged(nameof(PublishQueueCount)); + } + + private void OnClientConnectionChanged(object sender, ClientConnectionChangedEventArgs e) + { + ClientCount = _dataServer.ClientCount; + } + + private void OnSubscriptionChanged(object sender, EventArgs e) + { + PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; + SystemSubscriptionCount = _dataServer.SystemValueCount; + PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; + PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; + } + + private void ExecuteToggleDataServer(object sender) + { + try + { + if (_dataServer == null) + return; + + if (_dataServer.IsRunning) + _dataServer.Stop(); + else + _dataServer.Start(); + + OnPropertyChanged(nameof(IsRunning)); + OnPropertyChanged(nameof(ToggleDataServerLabel)); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, Resources.ErrorCaption, MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } +} diff --git a/src/Servers/DataServer.Wpf/Properties/Resources.Designer.cs b/src/Servers/DataServer.Wpf/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d4c93ff --- /dev/null +++ b/src/Servers/DataServer.Wpf/Properties/Resources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RxdSolutions.FusionLink.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RxdSolutions.FusionLink.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to FusionLink. + /// + internal static string ErrorCaption { + get { + return ResourceManager.GetString("ErrorCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start. + /// + internal static string StartButtonText { + get { + return ResourceManager.GetString("StartButtonText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stop. + /// + internal static string StopButtonText { + get { + return ResourceManager.GetString("StopButtonText", resourceCulture); + } + } + } +} diff --git a/src/Servers/DataServer.Wpf/Properties/Resources.resx b/src/Servers/DataServer.Wpf/Properties/Resources.resx new file mode 100644 index 0000000..197a197 --- /dev/null +++ b/src/Servers/DataServer.Wpf/Properties/Resources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + FusionLink + + + Start + + + Stop + + \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml b/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml new file mode 100644 index 0000000..c9388b7 --- /dev/null +++ b/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Servers/DataServer.Wpf/RxdSolutionsLogo.xaml b/src/Servers/DataServer.Wpf/RxdSolutionsLogo.xaml new file mode 100644 index 0000000..e5a2929 --- /dev/null +++ b/src/Servers/DataServer.Wpf/RxdSolutionsLogo.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Servers/DataServer.Wpf/app.config b/src/Servers/DataServer.Wpf/app.config new file mode 100644 index 0000000..88d3c81 --- /dev/null +++ b/src/Servers/DataServer.Wpf/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Servers/DataServer.Wpf/mainlogo.svg b/src/Servers/DataServer.Wpf/mainlogo.svg new file mode 100644 index 0000000..bae8fad --- /dev/null +++ b/src/Servers/DataServer.Wpf/mainlogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Servers/DataServer/DataServer.cs b/src/Servers/DataServer/DataServer.cs index ff895dc..714bca1 100644 --- a/src/Servers/DataServer/DataServer.cs +++ b/src/Servers/DataServer/DataServer.cs @@ -34,8 +34,10 @@ public class DataServer : IDataServiceServer private BlockingCollection _publishQueue; public event EventHandler OnClientConnectionChanged; + public event EventHandler OnStatusChanged; public event EventHandler OnSubscriptionChanged; public event EventHandler OnDataReceived; + public event EventHandler OnPublishQueueChanged; public int ClientCount { @@ -101,6 +103,8 @@ public void Start() StartPublisher(); _dataServiceProvider.Start(); + + OnStatusChanged?.Invoke(this, new EventArgs()); } SendServiceStatus(); @@ -125,6 +129,8 @@ public void Stop() _dataPublisher = null; _dataServiceProvider.Stop(); + + OnStatusChanged?.Invoke(this, new EventArgs()); } SendServiceStatus(); @@ -456,6 +462,10 @@ public int SystemValueCount get => _systemSubscriptions.Count; } + public int PublishQueueCount + { + get => _publishQueue.Count; + } private void StartPublisher() { @@ -469,6 +479,8 @@ private void StartPublisher() return; MergeData(e); + + OnPublishQueueChanged?.Invoke(this, new EventArgs()); } } catch (Exception ex) @@ -629,7 +641,11 @@ private void DataService_DataAvailable(object sender, DataAvailableEventArgs e) { OnDataReceived?.Invoke(this, e); - _publishQueue.Add(e); + if(!_publishQueue.IsAddingCompleted) + { + _publishQueue.Add(e); + OnPublishQueueChanged?.Invoke(this, new EventArgs()); + } } private void SystemSubscriptionAdded(object sender, SubscriptionChangedEventArgs e) diff --git a/src/Servers/DataServer/DataServer.csproj b/src/Servers/DataServer/DataServer.csproj index bca207d..4070f12 100644 --- a/src/Servers/DataServer/DataServer.csproj +++ b/src/Servers/DataServer/DataServer.csproj @@ -4,7 +4,7 @@ {1D5FF3ED-123D-41FC-A6ED-C2C763A71168} Library RxdSolutions.FusionLink - FusionLinkDataServer + DataServer net452 512 AnyCPU;x64 diff --git a/src/Servers/FusionLink/Client/DiagnosticsView.xaml b/src/Servers/FusionLink/Client/DiagnosticsView.xaml deleted file mode 100644 index c91564d..0000000 --- a/src/Servers/FusionLink/Client/DiagnosticsView.xaml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs b/src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs deleted file mode 100644 index 984de25..0000000 --- a/src/Servers/FusionLink/Client/DiagnosticsView.xaml.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace RxdSolutions.FusionLink.Client -{ - /// - /// Interaction logic for DiagnosticsView.xaml - /// - public partial class DiagnosticsView : UserControl - { - public DiagnosticsView(DiagnosticsViewModel viewModel) - { - InitializeComponent(); - - DataContext = viewModel; - } - } -} diff --git a/src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs b/src/Servers/FusionLink/Client/ShowDashboardScenario.cs similarity index 84% rename from src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs rename to src/Servers/FusionLink/Client/ShowDashboardScenario.cs index 4ff3e56..bd5a2a9 100644 --- a/src/Servers/FusionLink/Client/ShowDiagnosticsScenario.cs +++ b/src/Servers/FusionLink/Client/ShowDashboardScenario.cs @@ -9,7 +9,7 @@ namespace RxdSolutions.FusionLink.Client { - public class ShowDiagnosticsScenario : CSMScenario + public class ShowDashboardScenario : CSMScenario { public override eMProcessingType GetProcessingType() { @@ -23,7 +23,7 @@ public override bool AlwaysEnabled() public override CMString GetName() { - return Resources.ShowDiagnostics; + return Resources.ShowDashboard; } public override void Run() @@ -37,7 +37,7 @@ public override void Run() else { var fwkElement = new DiagnosticsView(new DiagnosticsViewModel(Main.DataServer)); - WPFAdapter.Instance.OpenWindow(fwkElement, "FusionLink", wndKey, false); + WPFAdapter.Instance.OpenWindow(fwkElement, Resources.DashboardWindowCaption, wndKey, false); } base.Run(); diff --git a/src/Servers/FusionLink/FusionLink.csproj b/src/Servers/FusionLink/FusionLink.csproj index a51fbe1..92d4fa5 100644 --- a/src/Servers/FusionLink/FusionLink.csproj +++ b/src/Servers/FusionLink/FusionLink.csproj @@ -35,9 +35,6 @@ TRACE;V72 true - - - @@ -319,13 +316,9 @@ - - - MSBuild:Compile - - + diff --git a/src/Servers/FusionLink/Listeners/PositionActionListener.cs b/src/Servers/FusionLink/Listeners/PositionActionListener.cs index 06fc6b7..db4b9a7 100644 --- a/src/Servers/FusionLink/Listeners/PositionActionListener.cs +++ b/src/Servers/FusionLink/Listeners/PositionActionListener.cs @@ -13,21 +13,21 @@ public class PositionActionListener : CSMPositionAction public override void NotifyDeleted(CSMPosition position, CSMEventVector message) { - PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), true)); base.NotifyDeleted(position, message); } public override void NotifyModified(CSMPosition position, CSMEventVector message) { - PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), true)); base.NotifyModified(position, message); } public override void NotifyTransferred(CSMPosition position, CSMEventVector message) { - PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), false)); + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), true)); base.NotifyTransferred(position, message); } diff --git a/src/Servers/FusionLink/Listeners/TransactionActionListener.cs b/src/Servers/FusionLink/Listeners/TransactionActionListener.cs index 31c2d70..0c38ff2 100644 --- a/src/Servers/FusionLink/Listeners/TransactionActionListener.cs +++ b/src/Servers/FusionLink/Listeners/TransactionActionListener.cs @@ -13,21 +13,21 @@ public class TransactionActionListener : CSMTransactionAction public override void NotifyCreated(CSMTransaction transaction, CSMEventVector message, int event_id) { - TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), true)); base.NotifyCreated(transaction, message, event_id); } public override void NotifyDeleted(CSMTransaction transaction, CSMEventVector message, int event_id) { - TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), true)); base.NotifyDeleted(transaction, message, event_id); } public override void NotifyModified(CSMTransaction original, CSMTransaction transaction, CSMEventVector message, int event_id) { - TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), true)); base.NotifyModified(original, transaction, message, event_id); } diff --git a/src/Servers/FusionLink/Main.cs b/src/Servers/FusionLink/Main.cs index 0eba5de..db10b31 100644 --- a/src/Servers/FusionLink/Main.cs +++ b/src/Servers/FusionLink/Main.cs @@ -32,8 +32,6 @@ public class Main : IMain private static ServiceHost _host; - private bool _displayDebugMessage = false; - public static DataServer DataServer; public static CaptionBar CaptionBar; @@ -45,8 +43,6 @@ public void EntryPoint() { _context = Dispatcher.CurrentDispatcher; - LoadConfig(); - RegisterListeners(); RegisterScenarios(); @@ -122,7 +118,7 @@ private void RegisterScenarios() { CSMScenario.Register(Resources.ScenarioShowCaptionBarMessage, new ShowFusionLinkScenario()); CSMScenario.Register(Resources.OpenFusionLinkExcel, new OpenFusionLinkExcelScenario()); - CSMScenario.Register(Resources.ShowDiagnostics, new ShowDiagnosticsScenario()); + CSMScenario.Register(Resources.ShowDashboard, new ShowDashboardScenario()); } private void RegisterUI() @@ -131,40 +127,12 @@ private void RegisterUI() CSMPositionCtxMenu.Register(Resources.CopyTableAsExcelReference, new CopyRowAsRTDTableToClipboard()); CaptionBar = new CaptionBar(); - CaptionBar.OnButtonClicked += OnCaptionBarButtonClicked; - CaptionBar.DisplayButton = true; - CaptionBar.ButtonText = Resources.StopButtonText; - CaptionBar.ButtonToolTip = Resources.StartStopButtonTooltip; CaptionBar.Image = Resources.InfoIcon; CaptionBar.Show(); CaptionBar.Text = Resources.LoadingMessage; } - private void OnCaptionBarButtonClicked(object sender, EventArgs e) - { - try - { - if (DataServer == null) - return; - - if (DataServer.IsRunning) - DataServer.Stop(); - else - DataServer.Start(); - - CaptionBar.ButtonText = DataServer.IsRunning ? Resources.StopButtonText : Resources.StartButtonText; - - UpdateCaption(); - } - catch (Exception ex) - { - CSMLog.Write("Main", "OnCaptionBarButtonClicked", CSMLog.eMVerbosity.M_error, ex.ToString()); - - MessageBox.Show(ex.Message, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - private Task RegisterServer() { var aggregatePortfolioListener = new AggregatePortfolioListener(_portfolioActionListener, _portfolioEventListener); @@ -200,9 +168,7 @@ private Task RegisterServer() DataServer.Start(); DataServer.OnClientConnectionChanged += OnClientConnectionChanged; - - if(_displayDebugMessage) - DataServer.OnSubscriptionChanged += OnSubscriptionChanged; + DataServer.OnStatusChanged += OnStatusChanged; ; }); } @@ -230,11 +196,6 @@ private static void CreateDataServerFromConfig(FusionDataServiceProvider dataSer DataServer.DefaultMessage = defaultMessage; } - private void LoadConfig() - { - CSMConfigurationFile.getEntryValue("FusionLink", "ShowDebug", ref _displayDebugMessage, false); - } - private void UpdateCaption() { var caption = new StringBuilder(); @@ -260,12 +221,11 @@ private void UpdateCaption() caption.Append(clientsConnectedCaption); } - if (_displayDebugMessage) + if (!DataServer.IsRunning) { - var subs = $"(Subs = PortVal:{DataServer.PortfolioValueSubscriptionCount},PortProp:{DataServer.PortfolioPropertySubscriptionCount},Pos:{DataServer.PositonValueSubscriptionCount},Sys:{DataServer.SystemValueCount})"; caption.Append(" / "); - caption.Append(subs); - } + caption.Append(Resources.DataServerStopped); + } CaptionBar.Text = caption.ToString(); } @@ -278,5 +238,14 @@ private void OnClientConnectionChanged(object sender, ClientConnectionChangedEve }, DispatcherPriority.ApplicationIdle); } + + private void OnStatusChanged(object sender, EventArgs e) + { + _context.InvokeAsync(() => + { + UpdateCaption(); + + }, DispatcherPriority.ApplicationIdle); + } } } diff --git a/src/Servers/FusionLink/Properties/Resources.Designer.cs b/src/Servers/FusionLink/Properties/Resources.Designer.cs index 6a33483..d204074 100644 --- a/src/Servers/FusionLink/Properties/Resources.Designer.cs +++ b/src/Servers/FusionLink/Properties/Resources.Designer.cs @@ -96,6 +96,24 @@ internal static string CopyTableAsExcelReference { } } + /// + /// Looks up a localized string similar to FusionLink Dashboard. + /// + internal static string DashboardWindowCaption { + get { + return ResourceManager.GetString("DashboardWindowCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stopped. + /// + internal static string DataServerStopped { + get { + return ResourceManager.GetString("DataServerStopped", resourceCulture); + } + } + /// /// Looks up a localized string similar to Getting data... please wait. /// @@ -197,11 +215,11 @@ internal static string ScenarioShowCaptionBarMessage { } /// - /// Looks up a localized string similar to Display FusionLink Diagnostics. + /// Looks up a localized string similar to Display FusionLink Dashboard. /// - internal static string ShowDiagnostics { + internal static string ShowDashboard { get { - return ResourceManager.GetString("ShowDiagnostics", resourceCulture); + return ResourceManager.GetString("ShowDashboard", resourceCulture); } } diff --git a/src/Servers/FusionLink/Properties/Resources.resx b/src/Servers/FusionLink/Properties/Resources.resx index 9d712e3..85ef7a1 100644 --- a/src/Servers/FusionLink/Properties/Resources.resx +++ b/src/Servers/FusionLink/Properties/Resources.resx @@ -129,6 +129,12 @@ Copy Row(s) as Excel FusionLink References + + FusionLink Dashboard + + + Stopped + Getting data... please wait @@ -163,8 +169,8 @@ Display FusionLink - - Display FusionLink Diagnostics + + Display FusionLink Dashboard Display FusionLink diff --git a/src/Servers/FusionLink/Provider/CellValueBase.cs b/src/Servers/FusionLink/Provider/CellValueBase.cs index edc1928..8983969 100644 --- a/src/Servers/FusionLink/Provider/CellValueBase.cs +++ b/src/Servers/FusionLink/Provider/CellValueBase.cs @@ -14,7 +14,7 @@ internal abstract class CellValueBase : IDisposable public CSMExtraction Extraction { get; } - public CSMPortfolioColumn Column { get; } + public CSMPortfolioColumn Column { get; protected set; } public string ColumnName { get; } diff --git a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs index 0932d45..e1a6a3f 100644 --- a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs +++ b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs @@ -551,89 +551,120 @@ void RefreshSystemValues() } private void PortfolioListener_PortfolioChanged(object sender, PortfolioChangedEventArgs e) + { + RefreshPortfolio(e.PortfolioId, e.IsLocal); + } + + private void PositionListener_PositionChanged(object sender, PositionChangedEventArgs e) + { + RefreshPosition(e.PositionId, e.IsLocal); + } + + private void TransactionListener_TransactionChanged(object sender, TransactionChangedEventArgs e) + { + RefreshPosition(e.PositionId, e.IsLocal); + } + + private void RefreshPortfolio(int portfolioId, bool isLocal) { try { - foreach (var cell in _portfolioCellSubscriptions.GetCells().ToList()) + void NotifyDataChanged() { - if (cell.FolioId == e.PortfolioId) + foreach (var cell in _portfolioCellSubscriptions.GetCells().ToList()) { - cell.Dispose(); - _positionCellSubscriptions.Remove(cell.FolioId, cell.ColumnName); + if (cell.FolioId == portfolioId) + { + _portfolioCellSubscriptions.Remove(cell.FolioId, cell.ColumnName); + _portfolioCellSubscriptions.Add(portfolioId, cell.ColumnName); + } } - _portfolioCellSubscriptions.Add(e.PortfolioId, cell.ColumnName); - } - - if (e.IsLocal) - { - //There must be a Sophis API call which does the same work without the risk of deadlock. - var yield = Dispatcher.Yield(); - yield.GetAwaiter().OnCompleted(() => + foreach (var cell in _portfolioPropertySubscriptions.GetCells().ToList()) { - var args = new DataAvailableEventArgs(); - - foreach (var value in _portfolioPropertySubscriptions.GetCells()) + if (cell.FolioId == portfolioId) { - args.PortfolioProperties.Add((value.FolioId, value.Property), value.GetValue()); + _portfolioPropertySubscriptions.Remove(cell.FolioId, cell.Property); + _portfolioPropertySubscriptions.Add(portfolioId, cell.Property); } + } - DataAvailable?.Invoke(this, args); - }); - } - else - { var args = new DataAvailableEventArgs(); - foreach (var value in _portfolioPropertySubscriptions.GetCells()) + foreach (var value in _portfolioCellSubscriptions.Get(portfolioId)) + { + args.PortfolioValues.Add((value.FolioId, value.ColumnName), value.GetValue()); + } + + foreach (var value in _portfolioPropertySubscriptions.Get(portfolioId)) { args.PortfolioProperties.Add((value.FolioId, value.Property), value.GetValue()); } DataAvailable?.Invoke(this, args); } - } - catch(Exception ex) - { - CSMLog.Write(_className, "PortfolioListener_PortfolioChanged", CSMLog.eMVerbosity.M_error, ex.ToString()); - } - } - private void PositionListener_PositionChanged(object sender, PositionChangedEventArgs e) - { - try - { - foreach (var cell in _positionCellSubscriptions.GetCells().ToList()) + if (isLocal) { - if(cell.PositionId == e.PositionId) + //There must be a Sophis API call which does the same work without the risk of deadlock. + var yield = Dispatcher.Yield(DispatcherPriority.ApplicationIdle); + yield.GetAwaiter().OnCompleted(() => { - _positionCellSubscriptions.Remove(cell.PositionId, cell.ColumnName); - _positionCellSubscriptions.Add(e.PositionId, cell.ColumnName); - } + NotifyDataChanged(); + }); + } + else + { + NotifyDataChanged(); } } catch (Exception ex) { - CSMLog.Write(_className, "PositionListener_PositionChanged", CSMLog.eMVerbosity.M_error, ex.ToString()); + CSMLog.Write(_className, "RefreshPortfolio", CSMLog.eMVerbosity.M_error, ex.ToString()); } } - private void TransactionListener_TransactionChanged(object sender, TransactionChangedEventArgs e) + private void RefreshPosition(int positionId, bool isLocal) { try { - foreach (var cell in _positionCellSubscriptions.GetCells().ToList()) + void NotifyDataChanged() { - if (cell.PositionId == e.PositionId) + foreach (var cell in _positionCellSubscriptions.GetCells().ToList()) + { + if (cell.PositionId == positionId) + { + _positionCellSubscriptions.Remove(cell.PositionId, cell.ColumnName); + _positionCellSubscriptions.Add(positionId, cell.ColumnName); + } + } + + var args = new DataAvailableEventArgs(); + + foreach (var value in _positionCellSubscriptions.Get(positionId)) { - _positionCellSubscriptions.Remove(cell.PositionId, cell.ColumnName); - _positionCellSubscriptions.Add(e.PositionId, cell.ColumnName); + args.PositionValues.Add((value.PositionId, value.ColumnName), value.GetValue()); } + + DataAvailable?.Invoke(this, args); + } + + if (isLocal) + { + var yield = Dispatcher.Yield(DispatcherPriority.ApplicationIdle); + yield.GetAwaiter().OnCompleted(() => + { + NotifyDataChanged(); + }); + } + else + { + NotifyDataChanged(); } } catch (Exception ex) { - CSMLog.Write(_className, "TransactionListener_TransactionChanged", CSMLog.eMVerbosity.M_error, ex.ToString()); + CSMLog.Write(_className, "RefreshPosition", CSMLog.eMVerbosity.M_error, ex.ToString()); } } diff --git a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs index 984371c..d88ba4a 100644 --- a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs +++ b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs @@ -10,7 +10,7 @@ namespace RxdSolutions.FusionLink { internal class PortfolioCellValue : CellValueBase { - public CSMPortfolio Portfolio { get; } + public CSMPortfolio Portfolio { get; private set; } public int FolioId { get; } @@ -23,9 +23,9 @@ public PortfolioCellValue(int folioId, string column, CSMExtraction extraction) [HandleProcessCorruptedStateExceptions] public override object GetValue() { - try + object GetValueInternal() { - if(Error is object) + if (Error is object) { return Error.Message; } @@ -51,9 +51,27 @@ public override object GetValue() return value; } - catch(Exception ex) + + try + { + return GetValueInternal(); + } + catch { - return ex.Message; + Portfolio?.Dispose(); + Portfolio = CSMPortfolio.GetCSRPortfolio(FolioId); + + Column?.Dispose(); + Column = CSMPortfolioColumn.GetCSRPortfolioColumn(ColumnName); + + try + { + return GetValueInternal(); + } + catch(Exception ex) + { + return ex.Message; + } } } diff --git a/src/Servers/FusionLink/Provider/PositionCellValue.cs b/src/Servers/FusionLink/Provider/PositionCellValue.cs index e6f0eeb..14d76a1 100644 --- a/src/Servers/FusionLink/Provider/PositionCellValue.cs +++ b/src/Servers/FusionLink/Provider/PositionCellValue.cs @@ -23,7 +23,7 @@ public PositionCellValue(int positionId, string column, CSMExtraction extraction [HandleProcessCorruptedStateExceptions] public override object GetValue() { - try + object GetValueInternal() { if (Position is null) { @@ -48,9 +48,27 @@ public override object GetValue() return string.Format(Resources.PositionNotLoadedOrMissingMessage, PositionId); } } - catch(Exception ex) + + try { - return ex.Message; + return GetValueInternal(); + } + catch + { + try + { + Position?.Dispose(); + Column?.Dispose(); + + Position = CSMPosition.GetCSRPosition(PositionId); + Column = CSMPortfolioColumn.GetCSRPortfolioColumn(ColumnName); + + return GetValueInternal(); + } + catch(Exception ex) + { + return ex.Message; + } } } From 8b49e81f3ae2cb07b344aaa1cc0ccfd2c1390802 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sun, 30 Jun 2019 20:08:04 +0100 Subject: [PATCH 06/13] The Excel client now only considers processes on the same box running under the same account --- .../ExcelClient/AvailableConnections.cs | 78 +++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/src/Clients/ExcelClient/AvailableConnections.cs b/src/Clients/ExcelClient/AvailableConnections.cs index 8ccdd06..9918a0a 100644 --- a/src/Clients/ExcelClient/AvailableConnections.cs +++ b/src/Clients/ExcelClient/AvailableConnections.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; using System.ServiceModel; using System.ServiceModel.Discovery; using System.Text; @@ -11,6 +14,13 @@ namespace RxdSolutions.FusionLink.ExcelClient { public class AvailableConnections : IDisposable { + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CloseHandle(IntPtr hObject); + public event EventHandler AvailableEndpointsChanged; public bool IsSearchingForEndPoints { get; private set; } @@ -65,16 +75,54 @@ public void FindAvailableServices() AvailableEndpointsChanged?.Invoke(this, new EventArgs()); var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint()); + var findResponse = discoveryClient.Find(new FindCriteria(typeof(IDataServiceServer)) { Duration = TimeSpan.FromSeconds(3) }); - foreach (var endPoints in findResponse.Endpoints) + discoveryClient.Close(); + + int SessionIdFromUri(EndpointDiscoveryMetadata metaData) + { + if (int.TryParse(metaData.ListenUris[0].Segments[2].Replace("/", ""), out int sessionId)) + { + return sessionId; + } + + return 0; + } + + int ProcessIdFromUri(EndpointDiscoveryMetadata metaData) { - if (string.Compare(endPoints.ListenUris[0].Host, Environment.MachineName, StringComparison.InvariantCultureIgnoreCase) != 0) + if (int.TryParse(metaData.ListenUris[0].Segments[3].Replace("/", ""), out int processId)) + { + return processId; + } + + return 0; + } + + foreach (var endPoint in findResponse.Endpoints) + { + if (string.Compare(endPoint.ListenUris[0].Host, Environment.MachineName, StringComparison.InvariantCultureIgnoreCase) != 0) { continue; } - if (endPoints.ListenUris[0].Segments[2].Replace("/", "") != System.Diagnostics.Process.GetCurrentProcess().SessionId.ToString()) + if (SessionIdFromUri(endPoint) != Process.GetCurrentProcess().SessionId) + { + continue; + } + + //Check the process is running under the same user account + var processId = ProcessIdFromUri(endPoint); + + if (processId == 0) + continue; + + var remoteProcess = Process.GetProcessById(processId); + if (remoteProcess == null) + continue; + + if(!GetProcessUser(remoteProcess).User.Value.Equals(WindowsIdentity.GetCurrent().User.Value)) { continue; } @@ -83,7 +131,7 @@ public void FindAvailableServices() foreach (var knownEndpoint in AvailableEndpoints) { - if (knownEndpoint.Uri == endPoints.Address.Uri) + if (knownEndpoint.Uri == endPoint.Address.Uri) { found = true; break; @@ -94,7 +142,7 @@ public void FindAvailableServices() { lock (_availableEndpoints) { - _availableEndpoints.Add(endPoints.Address); + _availableEndpoints.Add(endPoint.Address); AvailableEndpointsChanged?.Invoke(this, new EventArgs()); } } @@ -181,5 +229,25 @@ public void Dispose() #endregion + private static WindowsIdentity GetProcessUser(Process process) + { + IntPtr processHandle = IntPtr.Zero; + try + { + OpenProcessToken(process.Handle, 8, out processHandle); + return new WindowsIdentity(processHandle); + } + catch + { + return null; + } + finally + { + if (processHandle != IntPtr.Zero) + { + CloseHandle(processHandle); + } + } + } } } From 6a77ea2fee0ef8ad5a3b390b13f28ab2312d9157 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sun, 30 Jun 2019 20:08:36 +0100 Subject: [PATCH 07/13] Cleaned up the closing of the Excel client and disposing --- src/Clients/ExcelClient/ComAddIn.cs | 5 +- src/Clients/ExcelClient/ConnectionMonitor.cs | 34 +- src/Clients/ExcelClient/DataServiceClient.cs | 25 +- .../DataServer.Wpf/DataServer.Wpf.csproj | 5 +- .../DataServer.Wpf/DiagnosticsView.xaml | 1335 +++++++++++++---- .../DataServer.Wpf/DiagnosticsView.xaml.cs | 5 + .../DataServer.Wpf/DiagnosticsViewModel.cs | 20 +- .../RxdSolutionsBackground.xaml | 4 +- src/Servers/DataServer.Wpf/mainlogo.svg | 1 - .../CurrentUserOnlyAuthorizationManager.cs | 38 + src/Servers/DataServer/DataServer.cs | 95 +- src/Servers/DataServer/DataServer.csproj | 5 +- .../DataServer/DataServerHostFactory.cs | 16 +- src/Servers/FusionLink/FusionLink.csproj | 5 +- src/Servers/FusionLink/Main.cs | 2 +- .../DataServiceInterface.csproj | 2 +- .../DataServerInterface/IDataServiceServer.cs | 28 +- 17 files changed, 1287 insertions(+), 338 deletions(-) delete mode 100644 src/Servers/DataServer.Wpf/mainlogo.svg create mode 100644 src/Servers/DataServer/CurrentUserOnlyAuthorizationManager.cs diff --git a/src/Clients/ExcelClient/ComAddIn.cs b/src/Clients/ExcelClient/ComAddIn.cs index c9b6a39..cb59b0f 100644 --- a/src/Clients/ExcelClient/ComAddIn.cs +++ b/src/Clients/ExcelClient/ComAddIn.cs @@ -28,10 +28,9 @@ public override void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array cu { AddIn.IsShuttingDown = true; - client.Close(); - client.Dispose(); - monitor.Stop(); + monitor.Dispose(); + availableConnections.Dispose(); } } diff --git a/src/Clients/ExcelClient/ConnectionMonitor.cs b/src/Clients/ExcelClient/ConnectionMonitor.cs index d052e6e..3fb1016 100644 --- a/src/Clients/ExcelClient/ConnectionMonitor.cs +++ b/src/Clients/ExcelClient/ConnectionMonitor.cs @@ -10,7 +10,7 @@ namespace RxdSolutions.FusionLink.ExcelClient { - public class ConnectionMonitor + public class ConnectionMonitor : IDisposable { private readonly List _clients; @@ -161,5 +161,37 @@ public Uri GetConnection() { return _connection; } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + foreach (var client in _clients) + { + client.Dispose(); + } + + _clients.Clear(); + + _resetEvent.Set(); + _resetEvent.Dispose(); + _monitor.Wait(); + _monitor.Dispose(); + } + + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(true); + } + #endregion } } \ No newline at end of file diff --git a/src/Clients/ExcelClient/DataServiceClient.cs b/src/Clients/ExcelClient/DataServiceClient.cs index 3fe0c8e..e64d580 100644 --- a/src/Clients/ExcelClient/DataServiceClient.cs +++ b/src/Clients/ExcelClient/DataServiceClient.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.ServiceModel; using RxdSolutions.FusionLink.ExcelClient.Properties; using RxdSolutions.FusionLink.Interface; @@ -110,14 +111,9 @@ public void Open(EndpointAddress endpointAddress) _server.Register(); //Subscribe to any topics in case this is a reconnection - foreach (var (Id, Column) in _positionCellValueSubscriptions) - _server.SubscribeToPositionValue(Id, Column); - - foreach (var (Id, Column) in _portfolioCellValueSubscriptions) - _server.SubscribeToPortfolioValue(Id, Column); - - foreach (var (Id, Property) in _portfolioPropertySubscriptions) - _server.SubscribeToPortfolioProperty(Id, Property); + _server.SubscribeToPositionValues(_positionCellValueSubscriptions.ToList()); + _server.SubscribeToPortfolioValues(_portfolioCellValueSubscriptions.ToList()); + _server.SubscribeToPortfolioProperties(_portfolioPropertySubscriptions.ToList()); foreach (var ps in _systemSubscriptions) _server.SubscribeToSystemValue(ps); @@ -165,20 +161,15 @@ public void Close() if (State == CommunicationState.Opened) { - _server.Unregister(); - //Subscribe to any topics in case this is a reconnection - foreach (var (Id, Column) in _positionCellValueSubscriptions) - _server.UnsubscribeFromPositionValue(Id, Column); - - foreach (var (Id, Column) in _portfolioCellValueSubscriptions) - _server.UnsubscribeFromPortfolioValue(Id, Column); + _server.UnsubscribeFromPositionValues(_positionCellValueSubscriptions.ToList()); + _server.UnsubscribeFromPortfolioValues(_portfolioCellValueSubscriptions.ToList()); + _server.UnsubscribeFromPortfolioProperties(_portfolioPropertySubscriptions.ToList()); foreach (var ps in _systemSubscriptions) _server.UnsubscribeFromSystemValue(ps); - foreach (var (Id, Property) in _portfolioPropertySubscriptions) - _server.UnsubscribeFromPortfolioProperty(Id, Property); + _server.Unregister(); } try diff --git a/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj b/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj index e79b999..3b3abef 100644 --- a/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj +++ b/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj @@ -5,7 +5,7 @@ Library RxdSolutions.FusionLink DataServer.Wpf - net452 + net472 512 AnyCPU;x64 Debug;Release; @@ -50,9 +50,6 @@ - - - Designer diff --git a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml index 1f19973..cd0d705 100644 --- a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml @@ -1,258 +1,961 @@  + mc:Ignorable="d" + d:DesignHeight="800" + d:DesignWidth="800"> - - - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - + + + - - - - - - - - - + + + + + + + + + @@ -260,7 +963,9 @@ - + @@ -273,9 +978,10 @@ - - - + + + + @@ -285,53 +991,156 @@ - FusionLink + + FusionLink + - + Subscriptions + + - + - - - - + + + + + + + + + + + + + - - - - + + + + - - - - + Clients connected: + - Clients connected: - + Listening on: - Listening on: - + - Terminal Services: - + Terminal Services: + - Last refresh duration: - + Last refresh duration: + - Average refresh duration: - + Average refresh duration: + - Data server running: - + Data server running: + - + - Publish queue: - + Publish queue length: + diff --git a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs index bb148fe..f2b3284 100644 --- a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs @@ -14,6 +14,11 @@ public DiagnosticsView(DiagnosticsViewModel viewModel) DataContext = viewModel; } + private void Help_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + System.Diagnostics.Process.Start("https://github.com/rxdsolutions/fusionlink/wiki"); + } + private void Logo_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { System.Diagnostics.Process.Start("http://www.rxdsolutions.co.uk/FusionLink"); diff --git a/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs b/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs index 2d09770..c620a31 100644 --- a/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs +++ b/src/Servers/DataServer.Wpf/DiagnosticsViewModel.cs @@ -8,15 +8,15 @@ namespace RxdSolutions.FusionLink.Client { public class DiagnosticsViewModel : INotifyPropertyChanged { + private readonly DataServer _dataServer; + private int _portfolioSubscriptionCount; private int _systemSubscriptionCount; private int _positionSubscriptionCount; private int _portfolioPropertySubscriptionCount; private int _clientCount; - private TimeSpan _lastTimeTaken; - private readonly DataServer _dataServer; - - private TimeSpan _refreshTimeTaken; + private int _lastTimeTaken; + private long _refreshTimeTaken; private int _numberOfRefreshes; public DiagnosticsViewModel(DataServer dataServer) @@ -55,7 +55,7 @@ public bool IsRunning get { return _dataServer.IsRunning; } } - public TimeSpan LastTimeTaken + public int LastTimeTaken { get { return _lastTimeTaken; } set @@ -65,14 +65,14 @@ public TimeSpan LastTimeTaken } } - public TimeSpan AverageTimeTaken + public long AverageTimeTaken { get { if (_numberOfRefreshes == 0) - return new TimeSpan(0); + return 0; - return new TimeSpan(0, 0, 0, 0, Convert.ToInt32(_refreshTimeTaken.TotalMilliseconds / _numberOfRefreshes)); + return Convert.ToInt64(_refreshTimeTaken / _numberOfRefreshes); } } @@ -149,8 +149,8 @@ private void OnPropertyChanged([CallerMemberName] string callerMemberName = "") private void OnDataReceived(object sender, DataAvailableEventArgs e) { - LastTimeTaken = e.TimeTaken; - _refreshTimeTaken += e.TimeTaken; + LastTimeTaken = Convert.ToInt32(e.TimeTaken.TotalMilliseconds); + _refreshTimeTaken += Convert.ToInt32(e.TimeTaken.TotalMilliseconds); _numberOfRefreshes++; OnPropertyChanged(nameof(AverageTimeTaken)); diff --git a/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml b/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml index c9388b7..ed346df 100644 --- a/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml +++ b/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml @@ -1,13 +1,13 @@ - + - + diff --git a/src/Servers/DataServer.Wpf/mainlogo.svg b/src/Servers/DataServer.Wpf/mainlogo.svg deleted file mode 100644 index bae8fad..0000000 --- a/src/Servers/DataServer.Wpf/mainlogo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Servers/DataServer/CurrentUserOnlyAuthorizationManager.cs b/src/Servers/DataServer/CurrentUserOnlyAuthorizationManager.cs new file mode 100644 index 0000000..413930b --- /dev/null +++ b/src/Servers/DataServer/CurrentUserOnlyAuthorizationManager.cs @@ -0,0 +1,38 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using System.Security.Principal; +using System.ServiceModel; +using System.ServiceModel.Discovery; + +namespace RxdSolutions.FusionLink +{ + public class CurrentUserOnlyAuthorizationManager : ServiceAuthorizationManager + { + private readonly UdpDiscoveryEndpoint _udpDiscovery; + + public CurrentUserOnlyAuthorizationManager(UdpDiscoveryEndpoint udpDiscovery) + { + _udpDiscovery = udpDiscovery; + } + + protected override bool CheckAccessCore(OperationContext operationContext) + { + var currentUser = WindowsIdentity.GetCurrent()?.User; + var contextUser = operationContext?.ServiceSecurityContext?.WindowsIdentity?.User; + if (currentUser == null || contextUser == null) + { + //Allow service discovery through + if(operationContext.EndpointDispatcher.EndpointAddress.Uri.Equals(_udpDiscovery.Address.Uri)) + { + return true; + } + + return false; + } + + return currentUser.Equals(contextUser); + } + } +} diff --git a/src/Servers/DataServer/DataServer.cs b/src/Servers/DataServer/DataServer.cs index 714bca1..45532b0 100644 --- a/src/Servers/DataServer/DataServer.cs +++ b/src/Servers/DataServer/DataServer.cs @@ -46,7 +46,7 @@ public int ClientCount return _clients.Count; } } - + public string DefaultMessage { get; set; } = Resources.DefaultGettingDataMessage; public bool IsRunning { get; private set; } @@ -62,7 +62,7 @@ public DataServer(IDataServerProvider dataService) _positionSubscriptions.SubscriptionAdded += PositionSubscriptionAdded; _positionSubscriptions.SubscriptionRemoved += PositionSubscriptionRemoved; - _portfolioSubscriptions = new Subscriptions<(int, string)>() { DefaultMessage = DefaultMessage }; + _portfolioSubscriptions = new Subscriptions<(int, string)>() { DefaultMessage = DefaultMessage }; _portfolioSubscriptions.OnValueChanged += PortfolioDataPointChanged; _portfolioSubscriptions.SubscriptionAdded += PortfolioSubscriptionAdded; _portfolioSubscriptions.SubscriptionRemoved += PortfolioSubscriptionRemoved; @@ -161,7 +161,7 @@ public void Register() SendServiceStatus(); } - catch(Exception ex) + catch (Exception ex) { throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); } @@ -181,7 +181,7 @@ public void Unregister() throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); } } - + public ServiceStatus GetServiceStatus() { try @@ -202,7 +202,8 @@ public void SubscribeToPositionValue(int positionId, string column) OnSubscriptionChanged?.Invoke(this, new EventArgs()); - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_positionSubscriptions.IsSubscribed(OperationContext.Current.SessionId, (dp.Key.Id, dp.Key.Column))) { @@ -217,6 +218,14 @@ public void SubscribeToPositionValue(int positionId, string column) } } + public void SubscribeToPositionValues(List<(int positionId, string column)> items) + { + foreach (var (positionId, column) in items) + { + SubscribeToPositionValue(positionId, column); + } + } + public void SubscribeToPortfolioValue(int portfolioId, string column) { try @@ -225,7 +234,8 @@ public void SubscribeToPortfolioValue(int portfolioId, string column) OnSubscriptionChanged?.Invoke(this, new EventArgs()); - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_portfolioSubscriptions.IsSubscribed(OperationContext.Current.SessionId, (dp.Key.Id, dp.Key.Column))) { @@ -240,6 +250,14 @@ public void SubscribeToPortfolioValue(int portfolioId, string column) } } + public void SubscribeToPortfolioValues(List<(int portfolioId, string column)> items) + { + foreach (var (positionId, column) in items) + { + SubscribeToPortfolioValue(positionId, column); + } + } + public void SubscribeToSystemValue(SystemProperty property) { try @@ -248,7 +266,8 @@ public void SubscribeToSystemValue(SystemProperty property) OnSubscriptionChanged?.Invoke(this, new EventArgs()); - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_systemSubscriptions.IsSubscribed(OperationContext.Current.SessionId, dp.Key)) { @@ -271,7 +290,8 @@ public void SubscribeToPortfolioProperty(int portfolioId, PortfolioProperty prop OnSubscriptionChanged?.Invoke(this, new EventArgs()); - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_portfolioPropertySubscriptions.IsSubscribed(OperationContext.Current.SessionId, (dp.Key.Id, dp.Key.Property))) { @@ -286,6 +306,14 @@ public void SubscribeToPortfolioProperty(int portfolioId, PortfolioProperty prop } } + public void SubscribeToPortfolioProperties(List<(int portfolioId, PortfolioProperty property)> items) + { + foreach (var (positionId, property) in items) + { + SubscribeToPortfolioProperty(positionId, property); + } + } + public void UnsubscribeFromPositionValue(int positionId, string column) { try @@ -300,6 +328,14 @@ public void UnsubscribeFromPositionValue(int positionId, string column) } } + public void UnsubscribeFromPositionValues(List<(int positionId, string column)> items) + { + foreach(var (positionId, column) in items) + { + UnsubscribeFromPositionValue(positionId, column); + } + } + public void UnsubscribeFromPortfolioValue(int portfolioId, string column) { try @@ -314,6 +350,14 @@ public void UnsubscribeFromPortfolioValue(int portfolioId, string column) } } + public void UnsubscribeFromPortfolioValues(List<(int portfolioId, string column)> items) + { + foreach (var (portfolioId, column) in items) + { + UnsubscribeFromPortfolioValue(portfolioId, column); + } + } + public void UnsubscribeFromSystemValue(SystemProperty property) { try @@ -342,6 +386,14 @@ public void UnsubscribeFromPortfolioProperty(int portfolioId, PortfolioProperty } } + public void UnsubscribeFromPortfolioProperties(List<(int portfolioId, PortfolioProperty property)> items) + { + foreach (var (portfolioId, property) in items) + { + UnsubscribeFromPortfolioProperty(portfolioId, property); + } + } + public List GetPositions(int folioId, PositionsToRequest position) { try @@ -356,7 +408,7 @@ public List GetPositions(int folioId, PositionsToRequest position) { throw new FaultException(new PortfolioNotLoadedFaultContract() { PortfolioId = folioId }); } - catch(Exception ex) + catch (Exception ex) { throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); } @@ -368,7 +420,7 @@ public List GetPriceHistory(int instrumentId, DateTime startDate, { return _dataServiceProvider.GetPriceHistory(instrumentId, startDate, endDate); } - catch(InstrumentNotFoundException) + catch (InstrumentNotFoundException) { throw new FaultException(new InstrumentNotFoundFaultContract() { Instrument = instrumentId.ToString() }); } @@ -427,7 +479,7 @@ public void RequestCalculate() catch (Exception ex) { throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); - } + } } public void LoadPositions() @@ -541,7 +593,7 @@ private void CheckClientsAlive() private void SendMessageToAllClients(Action send) { - lock(_clients) + lock (_clients) { if (_clients.Count == 0) return; @@ -567,7 +619,8 @@ private void SendMessageToAllClients(Action send) private void SystemDataPointChanged(object sender, DataPointChangedEventArgs e) { - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_systemSubscriptions.IsSubscribed(s, e.DataPoint.Key)) c.SendSystemValue(e.DataPoint.Key, e.DataPoint.Value); @@ -577,7 +630,8 @@ private void SystemDataPointChanged(object sender, DataPointChangedEventArgs e) { - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_portfolioSubscriptions.IsSubscribed(s, e.DataPoint.Key)) c.SendPortfolioValue(e.DataPoint.Key.Id, e.DataPoint.Key.Column, e.DataPoint.Value); @@ -587,7 +641,8 @@ private void PortfolioDataPointChanged(object sender, DataPointChangedEventArgs< private void PositionDataPointChanged(object sender, DataPointChangedEventArgs<(int Id, string Column)> e) { - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_positionSubscriptions.IsSubscribed(s, e.DataPoint.Key)) c.SendPositionValue(e.DataPoint.Key.Id, e.DataPoint.Key.Column, e.DataPoint.Value); @@ -597,7 +652,8 @@ private void PositionDataPointChanged(object sender, DataPointChangedEventArgs<( private void PortfolioPropertyPointChanged(object sender, DataPointChangedEventArgs<(int Id, PortfolioProperty Property)> e) { - SendMessageToAllClients((s, c) => { + SendMessageToAllClients((s, c) => + { if (_portfolioPropertySubscriptions.IsSubscribed(s, e.DataPoint.Key)) c.SendPortfolioProperty(e.DataPoint.Key.Id, e.DataPoint.Key.Property, e.DataPoint.Value); @@ -612,7 +668,7 @@ private void Unregister(string sessionId) { if (_clients.ContainsKey(sessionId)) _clients.Remove(sessionId); - + foreach (var (Id, Column) in _portfolioSubscriptions.GetKeys()) _portfolioSubscriptions.Remove(sessionId, (Id, Column)); @@ -630,7 +686,8 @@ private void Unregister(string sessionId) private void SendServiceStatus() { - SendMessageToAllClients((id, client) => { + SendMessageToAllClients((id, client) => + { client.SendServiceStaus(GetServiceStatus()); @@ -641,7 +698,7 @@ private void DataService_DataAvailable(object sender, DataAvailableEventArgs e) { OnDataReceived?.Invoke(this, e); - if(!_publishQueue.IsAddingCompleted) + if (!_publishQueue.IsAddingCompleted) { _publishQueue.Add(e); OnPublishQueueChanged?.Invoke(this, new EventArgs()); diff --git a/src/Servers/DataServer/DataServer.csproj b/src/Servers/DataServer/DataServer.csproj index 4070f12..021a758 100644 --- a/src/Servers/DataServer/DataServer.csproj +++ b/src/Servers/DataServer/DataServer.csproj @@ -5,7 +5,7 @@ Library RxdSolutions.FusionLink DataServer - net452 + net472 512 AnyCPU;x64 Debug;Release; @@ -37,9 +37,6 @@ - - - diff --git a/src/Servers/DataServer/DataServerHostFactory.cs b/src/Servers/DataServer/DataServerHostFactory.cs index fb0382b..fbc077f 100644 --- a/src/Servers/DataServer/DataServerHostFactory.cs +++ b/src/Servers/DataServer/DataServerHostFactory.cs @@ -3,7 +3,9 @@ using System; using System.Net.Security; +using System.Security.Principal; using System.ServiceModel; +using System.ServiceModel.Description; using System.ServiceModel.Discovery; using RxdSolutions.FusionLink.Interface; @@ -26,10 +28,13 @@ public static ServiceHost Create(DataServer server) var baseAddress = GetListeningAddress(); var host = new ServiceHost(server, baseAddress); - var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport) { + //Add the NamedPipes endPoint + var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport) + { MaxReceivedMessageSize = int.MaxValue, }; + binding.MaxConnections = int.MaxValue; binding.ReaderQuotas.MaxArrayLength = int.MaxValue; binding.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign; @@ -37,11 +42,16 @@ public static ServiceHost Create(DataServer server) // Add ServiceDiscoveryBehavior var discoveryBehavior = new ServiceDiscoveryBehavior(); - discoveryBehavior.AnnouncementEndpoints.Add(new UdpAnnouncementEndpoint()); + var serviceAnnouncementEndpoint = new UdpAnnouncementEndpoint(); + discoveryBehavior.AnnouncementEndpoints.Add(serviceAnnouncementEndpoint); host.Description.Behaviors.Add(discoveryBehavior); // Add a UdpDiscoveryEndpoint - host.AddServiceEndpoint(new UdpDiscoveryEndpoint()); + var serviceDiscoveryEndPoint = new UdpDiscoveryEndpoint(); + host.AddServiceEndpoint(serviceDiscoveryEndPoint); + + //Secure to only the current user + host.Authorization.ServiceAuthorizationManager = new CurrentUserOnlyAuthorizationManager(serviceDiscoveryEndPoint); host.Open(); diff --git a/src/Servers/FusionLink/FusionLink.csproj b/src/Servers/FusionLink/FusionLink.csproj index 92d4fa5..e4c6548 100644 --- a/src/Servers/FusionLink/FusionLink.csproj +++ b/src/Servers/FusionLink/FusionLink.csproj @@ -5,7 +5,7 @@ Library RxdSolutions.FusionLink FusionLink - net462 + net472 512 x64 Develop_713;Develop_72;Debug_713;Debug_72;Release_713;Release_72 @@ -313,9 +313,6 @@ false - - - diff --git a/src/Servers/FusionLink/Main.cs b/src/Servers/FusionLink/Main.cs index db10b31..796c544 100644 --- a/src/Servers/FusionLink/Main.cs +++ b/src/Servers/FusionLink/Main.cs @@ -31,7 +31,7 @@ public class Main : IMain private static TransactionEventListener _transactionEventListener; private static ServiceHost _host; - + public static DataServer DataServer; public static CaptionBar CaptionBar; diff --git a/src/Shared/DataServerInterface/DataServiceInterface.csproj b/src/Shared/DataServerInterface/DataServiceInterface.csproj index a083ad7..ec10cae 100644 --- a/src/Shared/DataServerInterface/DataServiceInterface.csproj +++ b/src/Shared/DataServerInterface/DataServiceInterface.csproj @@ -5,7 +5,7 @@ Library RxdSolutions.FusionLink FusionLinkInterface - net452;net472 + net472 Debug;Release RXD Solutions RXD Solutions diff --git a/src/Shared/DataServerInterface/IDataServiceServer.cs b/src/Shared/DataServerInterface/IDataServiceServer.cs index b89bad2..93ca6e4 100644 --- a/src/Shared/DataServerInterface/IDataServiceServer.cs +++ b/src/Shared/DataServerInterface/IDataServiceServer.cs @@ -26,31 +26,49 @@ public interface IDataServiceServer void SubscribeToPositionValue(int positionId, string column); [OperationContract(IsOneWay = true)] - void SubscribeToPortfolioValue(int folioId, string column); + void SubscribeToPositionValues(List<(int positionId, string column)> items); + + [OperationContract(IsOneWay = true)] + void SubscribeToPortfolioValue(int portfolioId, string column); + + [OperationContract(IsOneWay = true)] + void SubscribeToPortfolioValues(List<(int portfolioId, string column)> items); [OperationContract(IsOneWay = true)] void SubscribeToSystemValue(SystemProperty systemValue); [OperationContract(IsOneWay = true)] - void SubscribeToPortfolioProperty(int folioId, PortfolioProperty property); + void SubscribeToPortfolioProperty(int portfolioId, PortfolioProperty property); + + [OperationContract(IsOneWay = true)] + void SubscribeToPortfolioProperties(List<(int portfolioId, PortfolioProperty property)> items); [OperationContract(IsOneWay = true)] void UnsubscribeFromPositionValue(int positionId, string column); [OperationContract(IsOneWay = true)] - void UnsubscribeFromPortfolioValue(int folioId, string column); + void UnsubscribeFromPositionValues(List<(int positionId, string column)> items); + + [OperationContract(IsOneWay = true)] + void UnsubscribeFromPortfolioValue(int portfolioId, string column); + + [OperationContract(IsOneWay = true)] + void UnsubscribeFromPortfolioValues(List<(int portfolioId, string column)> items); [OperationContract(IsOneWay = true)] void UnsubscribeFromSystemValue(SystemProperty systemValue); [OperationContract(IsOneWay = true)] - void UnsubscribeFromPortfolioProperty(int folioId, PortfolioProperty property); + void UnsubscribeFromPortfolioProperty(int portfolioId, PortfolioProperty property); + + [OperationContract(IsOneWay = true)] + void UnsubscribeFromPortfolioProperties(List<(int portfolioId, PortfolioProperty property)> items); [OperationContract] [FaultContract(typeof(PortfolioNotLoadedFaultContract))] [FaultContract(typeof(PortfolioNotFoundFaultContract))] [FaultContract(typeof(ErrorFaultContract))] - List GetPositions(int folioId, PositionsToRequest position); + List GetPositions(int portfolioId, PositionsToRequest position); [OperationContract(Name = "GetPriceHistoryById")] [FaultContract(typeof(InstrumentNotFoundFaultContract))] From 331b1bec9119675f8a89eb03185d3574e0106beb Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Sun, 30 Jun 2019 20:10:19 +0100 Subject: [PATCH 08/13] Removed the copy of the WPF project --- .../DataServer.Wpf.csproj | 76 --- .../DataServer.Wpf - Copy/DelegateCommand.cs | 45 -- .../DiagnosticsView.xaml | 103 ---- .../DiagnosticsView.xaml.cs | 17 - .../DiagnosticsViewModel.cs | 184 ------- .../Properties/Resources.Designer.cs | 90 ---- .../Properties/Resources.resx | 129 ----- src/Servers/DataServer.Wpf - Copy/app.config | 19 - .../DataServer.Wpf - Copy/mainimage_light.svg | 451 ------------------ .../mainimage_light.xaml | 196 -------- 10 files changed, 1310 deletions(-) delete mode 100644 src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj delete mode 100644 src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs delete mode 100644 src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml delete mode 100644 src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs delete mode 100644 src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs delete mode 100644 src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs delete mode 100644 src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx delete mode 100644 src/Servers/DataServer.Wpf - Copy/app.config delete mode 100644 src/Servers/DataServer.Wpf - Copy/mainimage_light.svg delete mode 100644 src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml diff --git a/src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj b/src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj deleted file mode 100644 index ee11078..0000000 --- a/src/Servers/DataServer.Wpf - Copy/DataServer.Wpf.csproj +++ /dev/null @@ -1,76 +0,0 @@ - - - - {4EA8C78B-118D-4FD4-A745-2EBED8F68062} - Library - RxdSolutions.FusionLink - DataServer.Wpf - net452 - 512 - AnyCPU;x64 - Debug;Release; - RXD Solutions - RXD Solutions - FusionLink - Copyright ©2019 RXD Solutions - https://rxdsolutions.github.io/FusionLink/ - https://github.com/RXDSolutions/FusionLink - - - x64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - MSBuild:Compile - - - - - - - - - - - - True - True - Resources.resx - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs b/src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs deleted file mode 100644 index cf25e61..0000000 --- a/src/Servers/DataServer.Wpf - Copy/DelegateCommand.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Windows.Input; - -namespace RxdSolutions.FusionLink.Client -{ - public class DelegateCommand : ICommand - { - private readonly Predicate _canExecute; - private readonly Action _execute; - - public event EventHandler CanExecuteChanged; - - public DelegateCommand(Action execute) - : this(execute, null) - { - } - - public DelegateCommand(Action execute, - Predicate canExecute) - { - _execute = execute; - _canExecute = canExecute; - } - - public bool CanExecute(object parameter) - { - if (_canExecute == null) - { - return true; - } - - return _canExecute(parameter); - } - - public void Execute(object parameter) - { - _execute(parameter); - } - - public void RaiseCanExecuteChanged() - { - CanExecuteChanged?.Invoke(this, EventArgs.Empty); - } - } -} diff --git a/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml b/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml deleted file mode 100644 index 4773564..0000000 --- a/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FusionLink - - - - - - - - - - - - - - - - - Clients connected: - - - Listening on: - - - Terminal Services: - - - Last refresh duration: - - - - Average refresh duration: - - - - Data server running: - - - - - - - diff --git a/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs b/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs deleted file mode 100644 index 81added..0000000 --- a/src/Servers/DataServer.Wpf - Copy/DiagnosticsView.xaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Windows.Controls; - -namespace RxdSolutions.FusionLink.Client -{ - /// - /// Interaction logic for DiagnosticsView.xaml - /// - public partial class DiagnosticsView : UserControl - { - public DiagnosticsView(DiagnosticsViewModel viewModel) - { - InitializeComponent(); - - DataContext = viewModel; - } - } -} diff --git a/src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs b/src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs deleted file mode 100644 index 4c50060..0000000 --- a/src/Servers/DataServer.Wpf - Copy/DiagnosticsViewModel.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Windows; -using RxdSolutions.FusionLink.Properties; - -namespace RxdSolutions.FusionLink.Client -{ - public class DiagnosticsViewModel : INotifyPropertyChanged - { - private int _portfolioSubscriptionCount; - private int _systemSubscriptionCount; - private int _positionSubscriptionCount; - private int _portfolioPropertySubscriptionCount; - private int _clientCount; - private TimeSpan _lastTimeTaken; - private readonly DataServer _dataServer; - - private TimeSpan _refreshTimeTaken; - private int _numberOfRefreshes; - - public DiagnosticsViewModel(DataServer dataServer) - { - _dataServer = dataServer; - _dataServer.OnSubscriptionChanged += OnSubscriptionChanged; - _dataServer.OnClientConnectionChanged += OnClientConnectionChanged; - _dataServer.OnDataReceived += OnDataReceived; - - PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; - SystemSubscriptionCount = _dataServer.SystemValueCount; - PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; - PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; - ClientCount = _dataServer.ClientCount; - - ToggleDataServer = new DelegateCommand(ExecuteToggleDataServer); - } - - public event PropertyChangedEventHandler PropertyChanged; - - public DelegateCommand ToggleDataServer { get; } - - public int ClientCount - { - get { return _clientCount; } - set - { - _clientCount = value; - OnPropertyChanged(); - } - } - - public bool IsRunning - { - get { return _dataServer.IsRunning; } - } - - public TimeSpan LastTimeTaken - { - get { return _lastTimeTaken; } - set - { - _lastTimeTaken = value; - OnPropertyChanged(); - } - } - - public TimeSpan AverageTimeTaken - { - get - { - if (_numberOfRefreshes == 0) - return new TimeSpan(0); - - return new TimeSpan(0, 0, 0, 0, Convert.ToInt32(_lastTimeTaken.TotalMilliseconds / _numberOfRefreshes)); - } - } - - public string ServerUri - { - get { return DataServerHostFactory.GetListeningAddress().ToString(); } - } - - public bool IsTerminalServices - { - get - { - return System.Windows.Forms.SystemInformation.TerminalServerSession; - } - } - - public int PortfolioSubscriptionCount - { - get { return _portfolioSubscriptionCount; } - set - { - _portfolioSubscriptionCount = value; - OnPropertyChanged(); - } - } - - public int SystemSubscriptionCount - { - get { return _systemSubscriptionCount; } - set - { - _systemSubscriptionCount = value; - OnPropertyChanged(); - } - } - - public int PositionSubscriptionCount - { - get { return _positionSubscriptionCount; } - set - { - _positionSubscriptionCount = value; - OnPropertyChanged(); - } - } - - public int PortfolioPropertySubscriptionCount - { - get { return _portfolioPropertySubscriptionCount; } - set - { - _portfolioPropertySubscriptionCount = value; - OnPropertyChanged(); - } - } - - public string ToggleDataServerLabel - { - get { return IsRunning ? Resources.StopButtonText : Resources.StartButtonText; } - } - - private void OnPropertyChanged([CallerMemberName] string callerMemberName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(callerMemberName)); - } - - private void OnDataReceived(object sender, DataAvailableEventArgs e) - { - LastTimeTaken = e.TimeTaken; - _refreshTimeTaken += e.TimeTaken; - _numberOfRefreshes++; - - OnPropertyChanged("AverageTimeTaken"); - } - - private void OnClientConnectionChanged(object sender, ClientConnectionChangedEventArgs e) - { - ClientCount = _dataServer.ClientCount; - } - - private void OnSubscriptionChanged(object sender, EventArgs e) - { - PortfolioSubscriptionCount = _dataServer.PortfolioValueSubscriptionCount; - SystemSubscriptionCount = _dataServer.SystemValueCount; - PositionSubscriptionCount = _dataServer.PositonValueSubscriptionCount; - PortfolioPropertySubscriptionCount = _dataServer.PortfolioPropertySubscriptionCount; - } - - private void ExecuteToggleDataServer(object sender) - { - try - { - if (_dataServer == null) - return; - - if (_dataServer.IsRunning) - _dataServer.Stop(); - else - _dataServer.Start(); - - OnPropertyChanged("IsRunning"); - OnPropertyChanged("ToggleDataServerLabel"); - } - catch (Exception ex) - { - MessageBox.Show(ex.Message, Resources.ErrorCaption, MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } -} diff --git a/src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs b/src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs deleted file mode 100644 index d4c93ff..0000000 --- a/src/Servers/DataServer.Wpf - Copy/Properties/Resources.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace RxdSolutions.FusionLink.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RxdSolutions.FusionLink.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to FusionLink. - /// - internal static string ErrorCaption { - get { - return ResourceManager.GetString("ErrorCaption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Start. - /// - internal static string StartButtonText { - get { - return ResourceManager.GetString("StartButtonText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Stop. - /// - internal static string StopButtonText { - get { - return ResourceManager.GetString("StopButtonText", resourceCulture); - } - } - } -} diff --git a/src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx b/src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx deleted file mode 100644 index 197a197..0000000 --- a/src/Servers/DataServer.Wpf - Copy/Properties/Resources.resx +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - FusionLink - - - Start - - - Stop - - \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf - Copy/app.config b/src/Servers/DataServer.Wpf - Copy/app.config deleted file mode 100644 index 88d3c81..0000000 --- a/src/Servers/DataServer.Wpf - Copy/app.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/Servers/DataServer.Wpf - Copy/mainimage_light.svg b/src/Servers/DataServer.Wpf - Copy/mainimage_light.svg deleted file mode 100644 index 29a2bc8..0000000 --- a/src/Servers/DataServer.Wpf - Copy/mainimage_light.svg +++ /dev/null @@ -1,451 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml b/src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml deleted file mode 100644 index c9388b7..0000000 --- a/src/Servers/DataServer.Wpf - Copy/mainimage_light.xaml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From fcb1e2088f0621c2b65e797fc3a4e07f7a5e7464 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Mon, 1 Jul 2019 22:26:10 +0100 Subject: [PATCH 09/13] Removed the background from the image --- .../DataServer.Wpf/DiagnosticsView.xaml | 21 +------------------ .../RxdSolutionsBackground.xaml | 12 +++-------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml index cd0d705..d9ace2e 100644 --- a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml @@ -77,26 +77,7 @@ - - - - - - - - + - - - - - - - - - + + From 73803033d2014f2be0d0bf664828d20da0b6fb7d Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Mon, 1 Jul 2019 22:26:42 +0100 Subject: [PATCH 10/13] Adding Sophis.Windows to the mocks Adding additional Event/Action listeners --- src/Mocks/Copy71toLib.ps1 | 1 + src/Mocks/Copy72toLib.ps1 | 1 + src/Mocks/Mocks_71.sln | 10 +++ src/Mocks/Mocks_72.sln | 28 +++++--- .../Sophis.Windows/Sophis.Windows.csproj | 49 ++++++++++++++ .../Sophis/Windows/Integration/WPFAdapter.cs | 22 ++++++ .../Sophis/Windows/Integration/WindowKey.cs | 18 +++++ .../sophis/portfolio/CSMPositionAction.cs | 41 ++++++++++++ .../sophis/portfolio/CSMPositionEvent.cs | 41 ++++++++++++ .../sophis/portfolio/CSMTransaction.cs | 30 +++++++++ .../sophis/portfolio/CSMTransactionAction.cs | 67 +++++++++++++++++++ .../sophis/portfolio/CSMTransactionEvent.cs | 47 +++++++++++++ 12 files changed, 346 insertions(+), 9 deletions(-) create mode 100644 src/Mocks/Sophis.Windows/Sophis.Windows.csproj create mode 100644 src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WPFAdapter.cs create mode 100644 src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WindowKey.cs create mode 100644 src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionAction.cs create mode 100644 src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionEvent.cs create mode 100644 src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransaction.cs create mode 100644 src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionAction.cs create mode 100644 src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionEvent.cs diff --git a/src/Mocks/Copy71toLib.ps1 b/src/Mocks/Copy71toLib.ps1 index 322e30c..4fa59fe 100644 --- a/src/Mocks/Copy71toLib.ps1 +++ b/src/Mocks/Copy71toLib.ps1 @@ -1,5 +1,6 @@ Get-ChildItem DevExpress.XtraTreeList.v11.2\bin\Debug\net40\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_1 Get-ChildItem Sophis.API.Configuration\bin\Debug_7_1\net452\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_1 +Get-ChildItem Sophis.Windows\bin\Debug_7_1\net452\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_1 Get-ChildItem Sophis.Windows.Widgets.Core\bin\Debug_7_1\net452\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_1 Get-ChildItem Sophis.Windows.Widgets\bin\Debug_7_1\net452\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_1 Get-ChildItem SophisConfiguration\bin\Debug_7_1\net452\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_1 diff --git a/src/Mocks/Copy72toLib.ps1 b/src/Mocks/Copy72toLib.ps1 index ed71012..69242c7 100644 --- a/src/Mocks/Copy72toLib.ps1 +++ b/src/Mocks/Copy72toLib.ps1 @@ -1,5 +1,6 @@ Get-ChildItem DevExpress.XtraTreeList.v18.1\bin\Debug\net40\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_2 Get-ChildItem Sophis.API.Configuration\bin\Debug_7_2\net462\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_2 +Get-ChildItem Sophis.Windows\bin\Debug_7_2\net462\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_2 Get-ChildItem Sophis.Windows.Widgets.Core\bin\Debug_7_2\net462\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_2 Get-ChildItem Sophis.Windows.Widgets\bin\Debug_7_2\net462\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_2 Get-ChildItem SophisConfiguration\bin\Debug_7_2\net462\*.* | Copy-Item -Destination ..\..\libs\RXDSolutions\7_2 diff --git a/src/Mocks/Mocks_71.sln b/src/Mocks/Mocks_71.sln index 54f0a05..cc3fa52 100644 --- a/src/Mocks/Mocks_71.sln +++ b/src/Mocks/Mocks_71.sln @@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.Windows.Widgets.Core EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.Core.Data", "Sophis.Core.Data\Sophis.Core.Data.csproj", "{BB3F6D14-3787-41D4-BC97-1F73B4357809}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.Windows", "Sophis.Windows\Sophis.Windows.csproj", "{45D3074A-8D08-4E33-AD38-FBCBEAF554E8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_7_1|Any CPU = Debug_7_1|Any CPU @@ -121,6 +123,14 @@ Global {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug|Any CPU.Build.0 = Debug|Any CPU {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Release|Any CPU.ActiveCfg = Release|Any CPU {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Release|Any CPU.Build.0 = Release|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Debug_7_1|Any CPU.ActiveCfg = Debug_7_1|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Debug_7_1|Any CPU.Build.0 = Debug_7_1|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Debug_7_2|Any CPU.ActiveCfg = Debug_7_2|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Debug_7_2|Any CPU.Build.0 = Debug_7_2|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Mocks/Mocks_72.sln b/src/Mocks/Mocks_72.sln index 50f36c8..6b47dd0 100644 --- a/src/Mocks/Mocks_72.sln +++ b/src/Mocks/Mocks_72.sln @@ -25,7 +25,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.DotNetCore", "Sophis EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SophisDotNetValue", "SophisDotNetValue\SophisDotNetValue.csproj", "{4901F790-BAA5-42DC-864D-36742E00F78B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.Core.Data", "Sophis.Core.Data\Sophis.Core.Data.csproj", "{6D6B9642-374E-428A-966E-21757D0F9435}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.Core.Data", "Sophis.Core.Data\Sophis.Core.Data.csproj", "{BB3F6D14-3787-41D4-BC97-1F73B4357809}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sophis.Windows", "Sophis.Windows\Sophis.Windows.csproj", "{8FE33315-871D-4646-A040-CAD8F53B404F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -123,14 +125,22 @@ Global {4901F790-BAA5-42DC-864D-36742E00F78B}.Debug|Any CPU.Build.0 = Debug|Any CPU {4901F790-BAA5-42DC-864D-36742E00F78B}.Release|Any CPU.ActiveCfg = Release|Any CPU {4901F790-BAA5-42DC-864D-36742E00F78B}.Release|Any CPU.Build.0 = Release|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Debug_7_1|Any CPU.ActiveCfg = Debug_7_1|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Debug_7_1|Any CPU.Build.0 = Debug_7_1|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Debug_7_2|Any CPU.ActiveCfg = Debug_7_2|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Debug_7_2|Any CPU.Build.0 = Debug_7_2|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6D6B9642-374E-428A-966E-21757D0F9435}.Release|Any CPU.Build.0 = Release|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug_7_1|Any CPU.ActiveCfg = Debug_7_1|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug_7_1|Any CPU.Build.0 = Debug_7_1|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug_7_2|Any CPU.ActiveCfg = Debug_7_2|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug_7_2|Any CPU.Build.0 = Debug_7_2|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB3F6D14-3787-41D4-BC97-1F73B4357809}.Release|Any CPU.Build.0 = Release|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Debug_7_1|Any CPU.ActiveCfg = Debug_7_1|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Debug_7_1|Any CPU.Build.0 = Debug_7_1|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Debug_7_2|Any CPU.ActiveCfg = Debug_7_2|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Debug_7_2|Any CPU.Build.0 = Debug_7_2|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FE33315-871D-4646-A040-CAD8F53B404F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Mocks/Sophis.Windows/Sophis.Windows.csproj b/src/Mocks/Sophis.Windows/Sophis.Windows.csproj new file mode 100644 index 0000000..4c5d6b2 --- /dev/null +++ b/src/Mocks/Sophis.Windows/Sophis.Windows.csproj @@ -0,0 +1,49 @@ + + + + {45D3074A-8D08-4E33-AD38-FBCBEAF554E8} + Library + Sophis + Sophis.Windows + net452;net462 + true + Debug;Release;Debug_7_1;Debug_7_2 + RXD Solutions + RXD Solutions + FusionLink + https://github.com/RXDSolutions/FusionLink + https://rxdsolutions.github.io/FusionLink/ + Copyright ©2019 RXD Solutions + + + TRACE;V72 + + + + True + + + True + + + + + True + + + True + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WPFAdapter.cs b/src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WPFAdapter.cs new file mode 100644 index 0000000..a2323bb --- /dev/null +++ b/src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WPFAdapter.cs @@ -0,0 +1,22 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using System.Windows; + +namespace Sophis.Windows.Integration +{ + public abstract class WPFAdapter + { + public static WPFAdapter Instance { get; } + + public abstract IntPtr GetActiveWindow(WindowKey wndKey); + + public abstract void ActivateWindow(IntPtr wnd); + + public IntPtr OpenWindow(FrameworkElement fwkElement, string title, WindowKey wndKey, bool enableScroll) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WindowKey.cs b/src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WindowKey.cs new file mode 100644 index 0000000..f719224 --- /dev/null +++ b/src/Mocks/Sophis.Windows/Sophis/Windows/Integration/WindowKey.cs @@ -0,0 +1,18 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +namespace Sophis.Windows.Integration +{ + public class WindowKey + { + public WindowKey(int dialogType, int key, int subKey) + { + } + + public int DialogType { get; set; } + + public int Key { get; set; } + + public int SubKey { get; set; } + } +} \ No newline at end of file diff --git a/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionAction.cs b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionAction.cs new file mode 100644 index 0000000..524157a --- /dev/null +++ b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionAction.cs @@ -0,0 +1,41 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using sophis.tools; + +namespace sophis.portfolio +{ + public class CSMPositionAction : IDisposable + { + public enum eMOrder + { + M_oAfter = 2, + M_oSave = 1, + M_oBefore = 0 + } + + public static void Register(string key, eMOrder e, CSMPositionAction prototype) + { + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public unsafe virtual void NotifyTransferred(CSMPosition position, CSMEventVector message) + { + + } + + public unsafe virtual void NotifyDeleted(CSMPosition position, CSMEventVector message) + { + + } + + public unsafe virtual void NotifyModified(CSMPosition position, CSMEventVector message) + { + } + } +} diff --git a/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionEvent.cs b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionEvent.cs new file mode 100644 index 0000000..896d74d --- /dev/null +++ b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMPositionEvent.cs @@ -0,0 +1,41 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace sophis.portfolio +{ + public class CSMPositionEvent : IDisposable + { + public enum eMOrder + { + M_oAfter = 2, + M_oUpdate = 1, + M_oBefore = 0 + } + + public static void Register(string key, eMOrder e, CSMPositionEvent prototype) + { + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public unsafe virtual bool HasBeenDeleted(CSMPosition position) + { + throw new NotImplementedException(); + } + + public unsafe virtual bool HasBeenModified(CSMPosition position) + { + throw new NotImplementedException(); + } + + public unsafe virtual void HasBeenTransferred(CSMPosition position) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransaction.cs b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransaction.cs new file mode 100644 index 0000000..15f77e8 --- /dev/null +++ b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransaction.cs @@ -0,0 +1,30 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace sophis.portfolio +{ + public class CSMTransaction : IDisposable + { + public unsafe long GetTransactionCode() + { + throw new NotImplementedException(); + } + + public unsafe long GetReference() + { + throw new NotImplementedException(); + } + + public unsafe int GetPositionID() + { + throw new NotImplementedException(); + } + + public void Dispose() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionAction.cs b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionAction.cs new file mode 100644 index 0000000..9918b15 --- /dev/null +++ b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionAction.cs @@ -0,0 +1,67 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; +using sophis.tools; + +namespace sophis.portfolio +{ + public class CSMTransactionAction : IDisposable + { + public static void Register(string key, eMOrder e, CSMTransactionAction prototype) + { + } + + public virtual void NotifyCreated(CSMTransaction transaction, CSMEventVector message, int event_id) + { + + } + + public virtual void NotifyDeleted(CSMTransaction transaction, CSMEventVector message, int event_id) + { + + } + + public virtual void NotifyModified(CSMTransaction original, CSMTransaction transaction, CSMEventVector message, int event_id) + { + + } + + public virtual void UnRegister() + { + + } + + public virtual void VoteForCreation(CSMTransaction transaction, int event_id) + { + + } + + public virtual void VoteForDeletion(CSMTransaction transaction, int event_id) + { + + } + + public virtual void VoteForModification(CSMTransaction original, CSMTransaction transaction, int event_id) + { + + } + + public enum eMOrder + { + M_oBeforeDatabaseSaving = 0, + M_oSavingInDataBase = 1, + M_oBeforeSophisValidation = 2, + M_oFOSophisValidation = 3, + M_oBetweenSophisValidation = 4, + M_oBOSophisValidation = 5, + M_oAfterSophisValidation = 6, + M_oLAST = 7 + } + + public void Dispose() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionEvent.cs b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionEvent.cs new file mode 100644 index 0000000..b8514ad --- /dev/null +++ b/src/Mocks/SophisDotNetToolkit/sophis/portfolio/CSMTransactionEvent.cs @@ -0,0 +1,47 @@ +// Copyright (c) RXD Solutions. All rights reserved. +// FusionLink is licensed under the MIT license. See LICENSE.txt for details. + +using System; + +namespace sophis.portfolio +{ + public class CSMTransactionEvent : IDisposable + { + public static void Register(string key, eMOrder e, CSMTransactionEvent prototype) + { + throw new NotImplementedException(); + } + + public virtual bool HasBeenCreated(CSMTransaction transaction) + { + throw new NotImplementedException(); + } + + public virtual bool HasBeenDeleted(CSMTransaction transaction) + { + throw new NotImplementedException(); + } + + public virtual bool HasBeenModified(CSMTransaction original, CSMTransaction transaction) + { + throw new NotImplementedException(); + } + + public virtual void UnRegister() + { + throw new NotImplementedException(); + } + + public enum eMOrder + { + M_oBefore = 0, + M_oUpdate = 1, + M_oAfter = 2 + } + + public void Dispose() + { + throw new NotImplementedException(); + } + } +} From 2fa7fec979924f7ac9c0616827d3c9cddee888ff Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Mon, 1 Jul 2019 22:27:04 +0100 Subject: [PATCH 11/13] New mock builds for 7.1 & 7.2 --- libs/RXDSolutions/7_1/Sophis.Windows.dll | Bin 0 -> 5120 bytes libs/RXDSolutions/7_1/SophisDotNetToolkit.dll | Bin 19456 -> 21504 bytes libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb | Bin 9064 -> 10212 bytes libs/RXDSolutions/7_2/Sophis.Windows.dll | Bin 0 -> 5120 bytes libs/RXDSolutions/7_2/SophisDotNetToolkit.dll | Bin 19456 -> 21504 bytes libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb | Bin 9280 -> 10424 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 libs/RXDSolutions/7_1/Sophis.Windows.dll create mode 100644 libs/RXDSolutions/7_2/Sophis.Windows.dll diff --git a/libs/RXDSolutions/7_1/Sophis.Windows.dll b/libs/RXDSolutions/7_1/Sophis.Windows.dll new file mode 100644 index 0000000000000000000000000000000000000000..cf5fbe9db2e118ecb50e25cc1c70a460f7767486 GIT binary patch literal 5120 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 diff --git a/libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb b/libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb index fc314b4401d915495d5380da4ec0325f8d72eea1..98798b5020e56e02ef695880b0c8b846bc5de07e 100644 GIT binary patch delta 3179 zcmZ9N3s6*57{|YR*>!heSy+K(S(bglXpR#(QdY*KqNEu@4w}f&m`WOrKnRoC!|N-P zH*`reMqOLv*+Hsd*zwd zq_RiT^dUr%ONp)_Y6y99-olKm`MF4`qpvnSnL3e=VEu{HpPhghWg%)X!{ZiY&w-!+ zc<61{vW_ffiK5JPgJiv|jPVVoHeE11 zhd2Yw^~GK(g!cG`&?=u0N*xqJjfN2V#lY$o+N-tDSdaixw5%3N34WoJ0@A^2;4QEQ zRDf#mq@Inl;#w=Nwc=VUuC-FS)~ZmDjy1z5&KO3CAl1m+Mn4#BbP?PF-9YlUksg$2 zY!rYPqPI~v;weoyE%XnkC14qN7sPqzup`7>7%&VVbR6OhSHMw4PEE0QQM z0>pwzU>bPI#5NK^iGw0&u}=g}BLb(vY7W{ZJE+6tpgW)kw0S>-6B}`2BTkwa;G{$_ z9i)M|;8jpBJ1GY-LF>c`I4NE69-)hxbT0Z8w1J!8fp-K16onj>k|Lji&D{*)Gob7xS=dAyZ;Hw2+L%RO!+4y|U*?a|p7b-%b zNbnNDO9d|zyj*Zq*kFa=%KJiLt>Ehf|3L8df`2IZ2F}?DY#g}32~;_-KvjZQ3;vPd zn@Fc_HH}ds`wyPI;oE4Se~KD^V?X1Zz120Gvu$qYob`8b^RfO;&e@Hw^>76?U>E0X zz;23E=LaMy|34pW)R%ZK`EBhN{D9zJ34T!UuLZ9cyg?BPhXnsd@WXs z;8TtXh2!{%;ZNX%;NJ`0#Jg%X$NO<7s^*yEm4P?X22;L&T(ZAD>{td7RTLo_u{F3081^IJ-IJ^HqV-wvt#v(ygqr-%8xm`h*Vfl+3P<{}DS`|*4Vk>To#mR|+|A`f zddCEDi>TM_mM03bT`zWx+FhBnq_VcB@!YkRJ?W7vM&~{JcH^vSlvarVGdT+8Lpv!aM(!QUSBipY!wp<8x#5QJr z@t|m%QhjJ|uDUT|n5M9^@UD71B3)ZFPb$h(V;mloiVD=3j*Xh)p~W$3wL}OKEZLzhb|oVz)vC>|47qf%I@Q(944rDnLFVo8hoiMvKFTc^I@iDqH7 QXS}AI%8hE1XM;rl0_?$-g#Z8m delta 2123 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 DqII0R delta 7866 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 diff --git a/libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb b/libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb index 70df413bb9bd4d311ab7b5696981109f890a346e..5900aa45f7b3e600b6bc4eba56fe8cc6561c6d1b 100644 GIT binary patch delta 3344 zcmZvd3s6*57{|YRUEBqhT^3j#%d!s^G)7|_E$zXiqNo{S3_2o1QwrKp3M!0dr8m?` z5fJdI>6nj68#7TU%x5M(M|4bk_{>av`N642NfD zIX87bJTh(7*%Z2e>-zG>9Y>3+D#N0>qV^%MDny$ReE^qH->R)X#Z=W0?H~WNdPDb9 zmT8s%gA;1y12S^2(emoXIseuuc339<0 z@G+PH=76Q(WfRYf#I=#QHWJrH;@U{c^^4Rf)X2psN;XGP8ptxUJLo5~gU*1P-~mvA z9ApBs3=Xm)+D#6MMm%DOrg1^hG!c9Pri0|B9!^9jdOFe5Nr!@*bP`+yw*Xn3WVUEd zibN6zI)Nn64-5kDSa=~Zl-4$eK4=qz-H5?%@R*BkSX^`$oNrpZ8w+q_0dDGHbyHuE z1_py{kOQ`>&KnWOcK+f+z#bWz8`kwAi9QCrrQF4F$`9_VSbi_$Eo)Awz z+T!UjI0?>z%b*e51O6fL`ZZ@qxmQ>cea)TI(Y} zf?aOz&qsPxa6W-!f^+@2;9Nf;IM;uZUc{4<|1N*qe^9%Y1#rXDk~c_xM)I?gpBEOy z{VoX33)KD;26)0n!Fj?Z!MT1}aIRkwoarbKPHXt_LXAmizVLfA|800d}+GL6Tbp=NniR{X$5k zW3t%`i3JKxv&JFDH|6i!YAZvB2AIQoWej+;pfIl}uOMHqazMw{D}Ob z#GFEFcAzEH(Y|xnq^Z%Q4eN4!Q(h^0zFWlty?gHR zn&&DKn(h?xq@xWT2aOw+UpOpBFOIQyo>}KHDyO1uoEyI9VZ;86q6zz^C7DhCdaLxl9`4HZD;ueljn*|+EQZQv>R-AR8Oqz2 zchUQ}vpAWhFL$3nGHaAR#q$~`YxG^7R8Ai2VctBoVyv!tI~Xd;E9UEKysvU{Lciv% pRx2y@g|Ueo_Q&=!RNk+Aq(}IMs#Tf#IA2FYRY}!c{R`j1{{ZKlt`7hJ delta 2281 zcmZ9Me@t6d6vxkdyh7=3CT*bw+O?EQM6<+c{E=uNGSHxC$cPY$(J*XZgzdi33R$Fw7p9xg3EAGK8=3x+`zz?O7zdVf?i(lf#0 zFpR6mghktJ`fM82*mQaZ@pZ%%c8%KYIz5f}8e*?Qql`nR!-&TbOPv~3J9T;q@c?2L zaUOBRrO~KMrz42dh<>+5aX0!PzKW>P7LWnE!1G`q&Ol?W`kZDq*-EJm+-&19FHPFK zGzb0y|A0cfmz*GJc&QZ8YxPnXankV7y>=gM0)1czG%fV-Bl^+PkDh+|!tSRTkOilK z#o?zCPzh=sev>XjQV*^L9blD%4^l?g6_(L`1!c4ubQbbhIsM@%r@z5$5qkg!2;cw# zYIFvu1+;-qa0^%s4(b6~huC5X(2bzm!s~)`q$o&Vf!QK6NWUYQUualCRjvw(fjV#@ z*sWL4Wr+2l8MK0S&;`1|TF?VFf(O7>kOUiy3hL!|j4Em0!lqUK-A;&_-63iPd-V{t zUy7vH%@f0P%pImFFau74U%{VX9=JSV3V~&y*czsK&)E7xy~wP@uc`5J;wN|z+sWRm z_-2YO){8N}Pw_1nlh<#R+=~2R$$7tiQySn215!htFeteWACjEw3CX#hl$`4+ARmbT zRypVW%(OJX3o?@P1c7_WE#Ue#6O)p`Ca{C~}DXcbF6jd&?? z&J*}!lXGtPhU8oymz?WwDjU9~_}hx_r<%q7-0y&?3>;K^Lh(b2zb74t8y=RN5BR?1 zT>n6Fu6KPX4e*4IB8_BtTLUOKuYf^3APx75Kffsx)IWPD@a<0!x&h;N9=lY!F zTt7+f#r^oZ*6@=u@U!B-D1J(E-XKfjHt%rYA=^?&1ZdRntVWD2 OlBpe{^4y&TIN85t8v(Qc From 7a2bfcd8e1ace2e5de93ce8e4ddb91dd34ac8f85 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Mon, 1 Jul 2019 22:27:27 +0100 Subject: [PATCH 12/13] Added Preference change listener (no sec event available) --- ...aticComputatingPreferenceChangeListener.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/Servers/FusionLink/Listeners/AutomaticComputatingPreferenceChangeListener.cs diff --git a/src/Servers/FusionLink/Listeners/AutomaticComputatingPreferenceChangeListener.cs b/src/Servers/FusionLink/Listeners/AutomaticComputatingPreferenceChangeListener.cs new file mode 100644 index 0000000..389d48a --- /dev/null +++ b/src/Servers/FusionLink/Listeners/AutomaticComputatingPreferenceChangeListener.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace RxdSolutions.FusionLink.Listeners +{ + public class AutomaticComputatingPreferenceChangeListener + { + private static bool _running; + private static Task _monitor; + private static AutoResetEvent _wait = new AutoResetEvent(true); + private static eMAutomaticComputingType _currentPreference; + private static Dispatcher Dispatcher; + + public static event EventHandler AutomaticComputatingChanged; + + public static void Start() + { + _running = true; + Dispatcher = Dispatcher.CurrentDispatcher; + _currentPreference = CSMPreference.GetAutomaticComputatingType(); + + _monitor = Task.Run(() => + { + while (_running) + { + Dispatcher.InvokeAsync(() => + { + if (CSMPreference.GetAutomaticComputatingType() != _currentPreference) + { + _currentPreference = CSMPreference.GetAutomaticComputatingType(); + + AutomaticComputatingChanged?.Invoke(new object(), new EventArgs()); + } + + }, DispatcherPriority.ApplicationIdle); + + _wait.WaitOne(2000); + } + }); + } + + public static void Stop() + { + _running = false; + _wait.Set(); + _monitor.Wait(); + } + } +} From 30caa0b58eac3b54a018cc67c2b95cba6ad5ad54 Mon Sep 17 00:00:00 2001 From: Ross Saunders Date: Mon, 1 Jul 2019 22:28:11 +0100 Subject: [PATCH 13/13] Namespaces updated to match folders --- .../CopyCellAsRTDFunctionToClipboard.cs | 6 +-- .../Client/CopyRowAsRTDTableToClipboard.cs | 6 +-- src/Servers/FusionLink/Client/ExcelHelper.cs | 2 +- .../Client/OpenFusionLinkExcelScenario.cs | 2 +- .../FusionLink/Client/PortfolioViewHelper.cs | 2 +- .../Client/ShowFusionLinkScenario.cs | 2 +- src/Servers/FusionLink/FusionLink.csproj | 21 ++++++++ .../FusionLink/Helpers/DataTypeExtensions.cs | 2 +- .../Listeners/AggregatePortfolioListener.cs | 2 +- .../Listeners/AggregatePositionListener.cs | 2 +- .../Listeners/AggregateTransactionListener.cs | 2 +- .../Listeners/IPortfolioListener.cs | 2 +- .../FusionLink/Listeners/IPositionListener.cs | 2 +- .../Listeners/ITransactionListener.cs | 2 +- .../Listeners/PortfolioActionListener.cs | 2 +- .../Listeners/PortfolioChangedEventArgs.cs | 2 +- .../Listeners/PortfolioEventListener.cs | 2 +- .../Listeners/PositionActionListener.cs | 2 +- .../Listeners/PositionChangedEventArgs.cs | 2 +- .../Listeners/PositionEventListener.cs | 2 +- .../Listeners/TransactionActionListener.cs | 2 +- .../Listeners/TransactionChangedEventArgs.cs | 2 +- .../Listeners/TransactionEventListener.cs | 8 ++-- src/Servers/FusionLink/Main.cs | 33 ++++++++++++- .../Properties/Resources.Designer.cs | 18 +++++++ .../FusionLink/Properties/Resources.resx | 6 +++ .../FusionLink/Provider/CellValueBase.cs | 2 +- .../Provider/FusionDataServiceProvider.cs | 48 ++++++------------- .../Provider/IsRealTimeEnabledValue.cs | 2 +- .../FusionLink/Provider/PortfolioCellValue.cs | 3 +- .../FusionLink/Provider/PortfolioDateValue.cs | 3 +- .../Provider/PortfolioPropertyValue.cs | 2 +- .../FusionLink/Provider/PositionCellValue.cs | 3 +- .../FusionLink/Provider/Subscriptions.cs | 2 +- .../FusionLink/Provider/SystemValue.cs | 2 +- .../FusionLink/Services/InstrumentService.cs | 1 + 36 files changed, 132 insertions(+), 72 deletions(-) diff --git a/src/Servers/FusionLink/Client/CopyCellAsRTDFunctionToClipboard.cs b/src/Servers/FusionLink/Client/CopyCellAsRTDFunctionToClipboard.cs index 7ecc7e4..f5cdd5e 100644 --- a/src/Servers/FusionLink/Client/CopyCellAsRTDFunctionToClipboard.cs +++ b/src/Servers/FusionLink/Client/CopyCellAsRTDFunctionToClipboard.cs @@ -7,10 +7,10 @@ using System.Windows.Forms; using sophis.portfolio; using sophis.utils; -using static RxdSolutions.FusionLink.ExcelHelper; -using static RxdSolutions.FusionLink.PortfolioViewHelper; +using static RxdSolutions.FusionLink.Client.ExcelHelper; +using static RxdSolutions.FusionLink.Client.PortfolioViewHelper; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Client { public class CopyCellAsRTDFunctionToClipboard : CSMPositionCtxMenu { diff --git a/src/Servers/FusionLink/Client/CopyRowAsRTDTableToClipboard.cs b/src/Servers/FusionLink/Client/CopyRowAsRTDTableToClipboard.cs index 71f88b7..05e001d 100644 --- a/src/Servers/FusionLink/Client/CopyRowAsRTDTableToClipboard.cs +++ b/src/Servers/FusionLink/Client/CopyRowAsRTDTableToClipboard.cs @@ -7,10 +7,10 @@ using System.Windows.Forms; using sophis.portfolio; using sophis.utils; -using static RxdSolutions.FusionLink.ExcelHelper; -using static RxdSolutions.FusionLink.PortfolioViewHelper; +using static RxdSolutions.FusionLink.Client.ExcelHelper; +using static RxdSolutions.FusionLink.Client.PortfolioViewHelper; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Client { public class CopyRowAsRTDTableToClipboard : CSMPositionCtxMenu { diff --git a/src/Servers/FusionLink/Client/ExcelHelper.cs b/src/Servers/FusionLink/Client/ExcelHelper.cs index 8ffe390..bbc111e 100644 --- a/src/Servers/FusionLink/Client/ExcelHelper.cs +++ b/src/Servers/FusionLink/Client/ExcelHelper.cs @@ -1,7 +1,7 @@ // Copyright (c) RXD Solutions. All rights reserved. // FusionLink is licensed under the MIT license. See LICENSE.txt for details. -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Client { public static class ExcelHelper { diff --git a/src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs b/src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs index 3569533..4f91559 100644 --- a/src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs +++ b/src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs @@ -9,7 +9,7 @@ using sophis.scenario; using sophis.utils; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Client { public class OpenFusionLinkExcelScenario : CSMScenario { diff --git a/src/Servers/FusionLink/Client/PortfolioViewHelper.cs b/src/Servers/FusionLink/Client/PortfolioViewHelper.cs index d5c80da..ec55dd6 100644 --- a/src/Servers/FusionLink/Client/PortfolioViewHelper.cs +++ b/src/Servers/FusionLink/Client/PortfolioViewHelper.cs @@ -7,7 +7,7 @@ using Sophis.Data.Utils; using Sophis.Portfolios; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Client { public static class PortfolioViewHelper { diff --git a/src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs b/src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs index db234d6..8583051 100644 --- a/src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs +++ b/src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs @@ -5,7 +5,7 @@ using sophis.scenario; using sophis.utils; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Client { public class ShowFusionLinkScenario : CSMScenario { diff --git a/src/Servers/FusionLink/FusionLink.csproj b/src/Servers/FusionLink/FusionLink.csproj index e4c6548..e596847 100644 --- a/src/Servers/FusionLink/FusionLink.csproj +++ b/src/Servers/FusionLink/FusionLink.csproj @@ -14,6 +14,7 @@ FusionLink https://rxdsolutions.github.io/FusionLink/ https://github.com/RXDSolutions/FusionLink + xcopy "$(TargetDir)*.*" "E:\PRODUCTION_SOPHIS\CLIENT\Toolkits" /y DEBUG;TRACE @@ -94,6 +95,10 @@ ..\..\..\libs\RXDSolutions\7_1\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_1\Sophis.Windows.dll + false + @@ -140,6 +145,10 @@ ..\..\..\libs\RXDSolutions\7_2\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_2\Sophis.Windows.dll + false + @@ -178,6 +187,10 @@ ..\..\..\libs\RXDSolutions\7_1\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_1\Sophis.Windows.dll + false + @@ -224,6 +237,10 @@ ..\..\..\libs\RXDSolutions\7_2\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_2\Sophis.Windows.dll + false + @@ -296,6 +313,10 @@ E:\UAT_SOPHIS_72\CLIENT\Sophis.Windows.Widgets.dll false + + E:\UAT_SOPHIS_72\CLIENT\Sophis.Windows.dll + false + E:\UAT_SOPHIS_72\CLIENT\SophisDotNetTools.dll false diff --git a/src/Servers/FusionLink/Helpers/DataTypeExtensions.cs b/src/Servers/FusionLink/Helpers/DataTypeExtensions.cs index 76f1125..624da4e 100644 --- a/src/Servers/FusionLink/Helpers/DataTypeExtensions.cs +++ b/src/Servers/FusionLink/Helpers/DataTypeExtensions.cs @@ -4,7 +4,7 @@ using System; using sophisTools; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Helpers { public static class DataTypeExtensions { diff --git a/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs b/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs index 9d970dc..87c393d 100644 --- a/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs +++ b/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class AggregatePortfolioListener : IPortfolioListener { diff --git a/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs b/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs index 4ed1375..0b5328c 100644 --- a/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs +++ b/src/Servers/FusionLink/Listeners/AggregatePositionListener.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class AggregatePositionListener : IPositionListener { diff --git a/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs b/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs index 53232a1..9eb9c0c 100644 --- a/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs +++ b/src/Servers/FusionLink/Listeners/AggregateTransactionListener.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class AggregateTransactionListener : ITransactionListener { diff --git a/src/Servers/FusionLink/Listeners/IPortfolioListener.cs b/src/Servers/FusionLink/Listeners/IPortfolioListener.cs index 6f57557..dde8ce6 100644 --- a/src/Servers/FusionLink/Listeners/IPortfolioListener.cs +++ b/src/Servers/FusionLink/Listeners/IPortfolioListener.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public interface IPortfolioListener { diff --git a/src/Servers/FusionLink/Listeners/IPositionListener.cs b/src/Servers/FusionLink/Listeners/IPositionListener.cs index 3973b17..d4acd5d 100644 --- a/src/Servers/FusionLink/Listeners/IPositionListener.cs +++ b/src/Servers/FusionLink/Listeners/IPositionListener.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public interface IPositionListener { diff --git a/src/Servers/FusionLink/Listeners/ITransactionListener.cs b/src/Servers/FusionLink/Listeners/ITransactionListener.cs index 7c0dd84..f7b4dca 100644 --- a/src/Servers/FusionLink/Listeners/ITransactionListener.cs +++ b/src/Servers/FusionLink/Listeners/ITransactionListener.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public interface ITransactionListener { diff --git a/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs b/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs index 7c07563..81a821f 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs @@ -5,7 +5,7 @@ using sophis.portfolio; using sophis.tools; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class PortfolioActionListener : CSMPortfolioAction { diff --git a/src/Servers/FusionLink/Listeners/PortfolioChangedEventArgs.cs b/src/Servers/FusionLink/Listeners/PortfolioChangedEventArgs.cs index ee5d45f..6826c09 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioChangedEventArgs.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioChangedEventArgs.cs @@ -1,7 +1,7 @@ // Copyright (c) RXD Solutions. All rights reserved. // FusionLink is licensed under the MIT license. See LICENSE.txt for details. -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class PortfolioChangedEventArgs { diff --git a/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs b/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs index eb2e3a4..c75b172 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs @@ -4,7 +4,7 @@ using System; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class PortfolioEventListener : CSMPortfolioEvent { diff --git a/src/Servers/FusionLink/Listeners/PositionActionListener.cs b/src/Servers/FusionLink/Listeners/PositionActionListener.cs index db4b9a7..8ec9204 100644 --- a/src/Servers/FusionLink/Listeners/PositionActionListener.cs +++ b/src/Servers/FusionLink/Listeners/PositionActionListener.cs @@ -5,7 +5,7 @@ using sophis.portfolio; using sophis.tools; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class PositionActionListener : CSMPositionAction { diff --git a/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs b/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs index 1cb4503..1d57d3e 100644 --- a/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs +++ b/src/Servers/FusionLink/Listeners/PositionChangedEventArgs.cs @@ -1,7 +1,7 @@ // Copyright (c) RXD Solutions. All rights reserved. // FusionLink is licensed under the MIT license. See LICENSE.txt for details. -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class PositionChangedEventArgs { diff --git a/src/Servers/FusionLink/Listeners/PositionEventListener.cs b/src/Servers/FusionLink/Listeners/PositionEventListener.cs index 2020da1..880fa8b 100644 --- a/src/Servers/FusionLink/Listeners/PositionEventListener.cs +++ b/src/Servers/FusionLink/Listeners/PositionEventListener.cs @@ -4,7 +4,7 @@ using System; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class PositionEventListener : CSMPositionEvent { diff --git a/src/Servers/FusionLink/Listeners/TransactionActionListener.cs b/src/Servers/FusionLink/Listeners/TransactionActionListener.cs index 0c38ff2..23ecb48 100644 --- a/src/Servers/FusionLink/Listeners/TransactionActionListener.cs +++ b/src/Servers/FusionLink/Listeners/TransactionActionListener.cs @@ -5,7 +5,7 @@ using sophis.portfolio; using sophis.tools; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class TransactionActionListener : CSMTransactionAction { diff --git a/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs b/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs index eb65009..2462a2b 100644 --- a/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs +++ b/src/Servers/FusionLink/Listeners/TransactionChangedEventArgs.cs @@ -1,7 +1,7 @@ // Copyright (c) RXD Solutions. All rights reserved. // FusionLink is licensed under the MIT license. See LICENSE.txt for details. -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class TransactionChangedEventArgs { diff --git a/src/Servers/FusionLink/Listeners/TransactionEventListener.cs b/src/Servers/FusionLink/Listeners/TransactionEventListener.cs index e3c4282..4d8e4dd 100644 --- a/src/Servers/FusionLink/Listeners/TransactionEventListener.cs +++ b/src/Servers/FusionLink/Listeners/TransactionEventListener.cs @@ -4,7 +4,7 @@ using System; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { public class TransactionEventListener : CSMTransactionEvent { @@ -12,21 +12,21 @@ public class TransactionEventListener : CSMTransactionEvent public override bool HasBeenCreated(CSMTransaction transaction) { - TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetReference(), transaction.GetPositionID(), false)); + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); return base.HasBeenCreated(transaction); } public override bool HasBeenDeleted(CSMTransaction transaction) { - TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetReference(), transaction.GetPositionID(), false)); + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); return base.HasBeenDeleted(transaction); } public override bool HasBeenModified(CSMTransaction original, CSMTransaction transaction) { - TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetReference(), transaction.GetPositionID(), false)); + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), transaction.GetPositionID(), false)); return base.HasBeenModified(original, transaction); } diff --git a/src/Servers/FusionLink/Main.cs b/src/Servers/FusionLink/Main.cs index 796c544..4257142 100644 --- a/src/Servers/FusionLink/Main.cs +++ b/src/Servers/FusionLink/Main.cs @@ -9,7 +9,9 @@ using System.Windows.Forms; using System.Windows.Threading; using RxdSolutions.FusionLink.Client; +using RxdSolutions.FusionLink.Listeners; using RxdSolutions.FusionLink.Properties; +using RxdSolutions.FusionLink.Provider; using RxdSolutions.FusionLink.Services; using sophis; using sophis.misc; @@ -55,6 +57,8 @@ public void Close() { try { + AutomaticComputatingPreferenceChangeListener.Stop(); + DataServer.OnClientConnectionChanged -= OnClientConnectionChanged; DataServer.Close(); _host?.Close(); @@ -112,6 +116,9 @@ private void RegisterListeners() _transactionActionListener = new TransactionActionListener(); CSMTransactionAction.Register("TransactionActionListener", CSMTransactionAction.eMOrder.M_oSavingInDataBase, _transactionActionListener); + + AutomaticComputatingPreferenceChangeListener.Start(); + AutomaticComputatingPreferenceChangeListener.AutomaticComputatingChanged += AutomaticComputatingChanged; } private void RegisterScenarios() @@ -200,7 +207,6 @@ private void UpdateCaption() { var caption = new StringBuilder(); - //Listening int processId = Process.GetCurrentProcess().Id; string dataServiceIdentifierCaption = string.Format(Resources.ConnectionIdMessage, processId); caption.Append(dataServiceIdentifierCaption); @@ -221,6 +227,22 @@ private void UpdateCaption() caption.Append(clientsConnectedCaption); } + caption.Append(" / "); + switch (CSMPreference.GetAutomaticComputatingType()) + { + case eMAutomaticComputingType.M_acQuotation: + case eMAutomaticComputingType.M_acLast: + case eMAutomaticComputingType.M_acNothing: + caption.Append(Resources.DataRefreshModeManual); + break; + + case eMAutomaticComputingType.M_acPortfolioWithoutPNL: + case eMAutomaticComputingType.M_acPortfolioOnlyPNL: + case eMAutomaticComputingType.M_acFolio: + caption.Append(Resources.DataRefreshModeAutomatic); + break; + } + if (!DataServer.IsRunning) { caption.Append(" / "); @@ -247,5 +269,14 @@ private void OnStatusChanged(object sender, EventArgs e) }, DispatcherPriority.ApplicationIdle); } + + private void AutomaticComputatingChanged(object sender, EventArgs e) + { + _context.InvokeAsync(() => + { + UpdateCaption(); + + }, DispatcherPriority.ApplicationIdle); + } } } diff --git a/src/Servers/FusionLink/Properties/Resources.Designer.cs b/src/Servers/FusionLink/Properties/Resources.Designer.cs index d204074..bd22ce6 100644 --- a/src/Servers/FusionLink/Properties/Resources.Designer.cs +++ b/src/Servers/FusionLink/Properties/Resources.Designer.cs @@ -105,6 +105,24 @@ internal static string DashboardWindowCaption { } } + /// + /// Looks up a localized string similar to Automatic. + /// + internal static string DataRefreshModeAutomatic { + get { + return ResourceManager.GetString("DataRefreshModeAutomatic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to On F9. + /// + internal static string DataRefreshModeManual { + get { + return ResourceManager.GetString("DataRefreshModeManual", resourceCulture); + } + } + /// /// Looks up a localized string similar to Stopped. /// diff --git a/src/Servers/FusionLink/Properties/Resources.resx b/src/Servers/FusionLink/Properties/Resources.resx index 85ef7a1..a17df06 100644 --- a/src/Servers/FusionLink/Properties/Resources.resx +++ b/src/Servers/FusionLink/Properties/Resources.resx @@ -132,6 +132,12 @@ FusionLink Dashboard + + Automatic + + + On F9 + Stopped diff --git a/src/Servers/FusionLink/Provider/CellValueBase.cs b/src/Servers/FusionLink/Provider/CellValueBase.cs index 8983969..b2611e6 100644 --- a/src/Servers/FusionLink/Provider/CellValueBase.cs +++ b/src/Servers/FusionLink/Provider/CellValueBase.cs @@ -4,7 +4,7 @@ using System; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal abstract class CellValueBase : IDisposable { diff --git a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs index e1a6a3f..d3e7cef 100644 --- a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs +++ b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs @@ -7,12 +7,13 @@ using System.Linq; using System.Windows.Threading; using RxdSolutions.FusionLink.Interface; +using RxdSolutions.FusionLink.Listeners; using RxdSolutions.FusionLink.Services; using sophis.instrument; using sophis.portfolio; using sophis.utils; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { public class FusionDataServiceProvider : IDataServerProvider, IDisposable { @@ -34,12 +35,6 @@ public class FusionDataServiceProvider : IDataServerProvider, IDisposable private readonly CSMExtraction _mainExtraction; - //Avoid infinite loops - private int _computeCount; - - //Optimize the data refreshing - private int _dataRefreshRequests = 0; - public bool IsRunning { get; private set; } public event EventHandler DataAvailable; @@ -361,12 +356,7 @@ public void UnsubscribeFromPortfolioProperty(int portfolioId, PortfolioProperty private void GlobalFunctions_PortfolioCalculationEnded(object sender, PortfolioCalculationEndedEventArgs e) { - if (_computeCount > 0) - { - //It was us that triggered this compute - _computeCount--; - return; - } + bool refreshData = false; if (IsRunning && HasSubscriptions) { @@ -374,6 +364,8 @@ private void GlobalFunctions_PortfolioCalculationEnded(object sender, PortfolioC { case sophis.misc.CSMGlobalFunctions.eMPortfolioCalculationType.M_pcFullCalculation: + refreshData = true; + try { ComputePortfolios(e.FolioId); @@ -404,7 +396,7 @@ private void GlobalFunctions_PortfolioCalculationEnded(object sender, PortfolioC if (id == 1) { - _dataRefreshRequests++; + refreshData = true; } } catch (Exception ex) @@ -420,17 +412,17 @@ private void GlobalFunctions_PortfolioCalculationEnded(object sender, PortfolioC case sophis.misc.CSMGlobalFunctions.eMPortfolioCalculationType.M_pcNotInPortfolio: return; - } + } - try - { + try + { + if (refreshData) RefreshData(); - } - catch(Exception ex) - { - CSMLog.Write(_className, "GlobalFunctions_PortfolioCalculationEnded", CSMLog.eMVerbosity.M_error, ex.ToString()); - } + } + catch (Exception ex) + { + CSMLog.Write(_className, "GlobalFunctions_PortfolioCalculationEnded", CSMLog.eMVerbosity.M_error, ex.ToString()); } } @@ -490,25 +482,13 @@ int FindRootLoadedPortfolio(CSMPortfolio portfolio) using (var portfolio = CSMPortfolio.GetCSRPortfolio(id)) { - _computeCount++; - portfolio.Compute(); } } - - _dataRefreshRequests++; } private void RefreshData() { - if (_computeCount > 0) - return; - - if (_dataRefreshRequests == 0) - return; - - _dataRefreshRequests = 0; - var args = new DataAvailableEventArgs(); void RefreshPortfolioCells() diff --git a/src/Servers/FusionLink/Provider/IsRealTimeEnabledValue.cs b/src/Servers/FusionLink/Provider/IsRealTimeEnabledValue.cs index 10d64a0..4db600d 100644 --- a/src/Servers/FusionLink/Provider/IsRealTimeEnabledValue.cs +++ b/src/Servers/FusionLink/Provider/IsRealTimeEnabledValue.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.ExceptionServices; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal class IsRealTimeEnabledValue : SystemValue { diff --git a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs index d88ba4a..47767ad 100644 --- a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs +++ b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs @@ -3,10 +3,11 @@ using System; using System.Runtime.ExceptionServices; +using RxdSolutions.FusionLink.Helpers; using RxdSolutions.FusionLink.Properties; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal class PortfolioCellValue : CellValueBase { diff --git a/src/Servers/FusionLink/Provider/PortfolioDateValue.cs b/src/Servers/FusionLink/Provider/PortfolioDateValue.cs index e941195..aca990f 100644 --- a/src/Servers/FusionLink/Provider/PortfolioDateValue.cs +++ b/src/Servers/FusionLink/Provider/PortfolioDateValue.cs @@ -3,9 +3,10 @@ using System; using System.Runtime.ExceptionServices; +using RxdSolutions.FusionLink.Helpers; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal class PortfolioDateValue : SystemValue { diff --git a/src/Servers/FusionLink/Provider/PortfolioPropertyValue.cs b/src/Servers/FusionLink/Provider/PortfolioPropertyValue.cs index 077e197..21b41b3 100644 --- a/src/Servers/FusionLink/Provider/PortfolioPropertyValue.cs +++ b/src/Servers/FusionLink/Provider/PortfolioPropertyValue.cs @@ -8,7 +8,7 @@ using sophis.portfolio; using sophis.utils; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal class PortfolioPropertyValue : IDisposable { diff --git a/src/Servers/FusionLink/Provider/PositionCellValue.cs b/src/Servers/FusionLink/Provider/PositionCellValue.cs index 14d76a1..936bed1 100644 --- a/src/Servers/FusionLink/Provider/PositionCellValue.cs +++ b/src/Servers/FusionLink/Provider/PositionCellValue.cs @@ -3,10 +3,11 @@ using System; using System.Runtime.ExceptionServices; +using RxdSolutions.FusionLink.Helpers; using RxdSolutions.FusionLink.Properties; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal class PositionCellValue : CellValueBase { diff --git a/src/Servers/FusionLink/Provider/Subscriptions.cs b/src/Servers/FusionLink/Provider/Subscriptions.cs index 2e40264..ab4bd40 100644 --- a/src/Servers/FusionLink/Provider/Subscriptions.cs +++ b/src/Servers/FusionLink/Provider/Subscriptions.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal class Subscriptions where T : class, IDisposable { diff --git a/src/Servers/FusionLink/Provider/SystemValue.cs b/src/Servers/FusionLink/Provider/SystemValue.cs index 2d04c42..172442e 100644 --- a/src/Servers/FusionLink/Provider/SystemValue.cs +++ b/src/Servers/FusionLink/Provider/SystemValue.cs @@ -3,7 +3,7 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Provider { internal abstract class SystemValue { diff --git a/src/Servers/FusionLink/Services/InstrumentService.cs b/src/Servers/FusionLink/Services/InstrumentService.cs index 1b1f053..8462fff 100644 --- a/src/Servers/FusionLink/Services/InstrumentService.cs +++ b/src/Servers/FusionLink/Services/InstrumentService.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using RxdSolutions.FusionLink.Helpers; using RxdSolutions.FusionLink.Interface; using sophis.gui; using sophis.instrument;