diff --git a/libs/RXDSolutions/7_1/Sophis.Windows.dll b/libs/RXDSolutions/7_1/Sophis.Windows.dll new file mode 100644 index 0000000..cf5fbe9 Binary files /dev/null and b/libs/RXDSolutions/7_1/Sophis.Windows.dll differ diff --git a/libs/RXDSolutions/7_1/SophisDotNetToolkit.dll b/libs/RXDSolutions/7_1/SophisDotNetToolkit.dll index f7162e9..ff54077 100644 Binary files a/libs/RXDSolutions/7_1/SophisDotNetToolkit.dll and b/libs/RXDSolutions/7_1/SophisDotNetToolkit.dll differ diff --git a/libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb b/libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb index fc314b4..98798b5 100644 Binary files a/libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb and b/libs/RXDSolutions/7_1/SophisDotNetToolkit.pdb differ diff --git a/libs/RXDSolutions/7_2/Sophis.Windows.dll b/libs/RXDSolutions/7_2/Sophis.Windows.dll new file mode 100644 index 0000000..4bb3051 Binary files /dev/null and b/libs/RXDSolutions/7_2/Sophis.Windows.dll differ diff --git a/libs/RXDSolutions/7_2/SophisDotNetToolkit.dll b/libs/RXDSolutions/7_2/SophisDotNetToolkit.dll index beecbf4..8b5c6f8 100644 Binary files a/libs/RXDSolutions/7_2/SophisDotNetToolkit.dll and b/libs/RXDSolutions/7_2/SophisDotNetToolkit.dll differ diff --git a/libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb b/libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb index 70df413..5900aa4 100644 Binary files a/libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb and b/libs/RXDSolutions/7_2/SophisDotNetToolkit.pdb differ diff --git a/libs/RXDSolutions/MFCCaptionBar10.dll b/libs/RXDSolutions/MFCCaptionBar10.dll index c81eb4d..6d2189e 100644 Binary files a/libs/RXDSolutions/MFCCaptionBar10.dll and b/libs/RXDSolutions/MFCCaptionBar10.dll differ 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..9918a0a --- /dev/null +++ b/src/Clients/ExcelClient/AvailableConnections.cs @@ -0,0 +1,253 @@ +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; +using System.Threading.Tasks; +using RxdSolutions.FusionLink.Interface; + +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; } + + 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) }); + + 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 (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 (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; + } + + var found = false; + + foreach (var knownEndpoint in AvailableEndpoints) + { + if (knownEndpoint.Uri == endPoint.Address.Uri) + { + found = true; + break; + } + } + + if (!found) + { + lock (_availableEndpoints) + { + _availableEndpoints.Add(endPoint.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 + + 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); + } + } + } + } +} diff --git a/src/Clients/ExcelClient/ComAddIn.cs b/src/Clients/ExcelClient/ComAddIn.cs index 39e4bb4..cb59b0f 100644 --- a/src/Clients/ExcelClient/ComAddIn.cs +++ b/src/Clients/ExcelClient/ComAddIn.cs @@ -15,22 +15,23 @@ 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) { AddIn.IsShuttingDown = true; - client.Close(); - 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..3fb1016 100644 --- a/src/Clients/ExcelClient/ConnectionMonitor.cs +++ b/src/Clients/ExcelClient/ConnectionMonitor.cs @@ -5,10 +5,8 @@ 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 { @@ -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) { @@ -271,29 +162,8 @@ 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; + private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) { @@ -301,7 +171,17 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - _announcementServiceHost.Close(); + foreach (var client in _clients) + { + client.Dispose(); + } + + _clients.Clear(); + + _resetEvent.Set(); + _resetEvent.Dispose(); + _monitor.Wait(); + _monitor.Dispose(); } disposedValue = true; @@ -312,7 +192,6 @@ 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/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/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/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 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/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: + }; +} +} -}; 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(); + } + } +} 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/DataServer.Wpf.csproj b/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj new file mode 100644 index 0000000..3b3abef --- /dev/null +++ b/src/Servers/DataServer.Wpf/DataServer.Wpf.csproj @@ -0,0 +1,84 @@ + + + + {4EA8C78B-118D-4FD4-A745-2EBED8F68062} + Library + RxdSolutions.FusionLink + DataServer.Wpf + net472 + 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..d9ace2e --- /dev/null +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml @@ -0,0 +1,1128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FusionLink + + + Subscriptions + + + + + + + + + + + + + + + + + + + + + + + + + + + Clients connected: + + + Listening on: + + + + Terminal Services: + + + Last refresh duration: + + + + Average refresh duration: + + + + Data server running: + + + + + Publish queue length: + + + + + diff --git a/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs new file mode 100644 index 0000000..f2b3284 --- /dev/null +++ b/src/Servers/DataServer.Wpf/DiagnosticsView.xaml.cs @@ -0,0 +1,27 @@ +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 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 new file mode 100644 index 0000000..c620a31 --- /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 readonly DataServer _dataServer; + + private int _portfolioSubscriptionCount; + private int _systemSubscriptionCount; + private int _positionSubscriptionCount; + private int _portfolioPropertySubscriptionCount; + private int _clientCount; + private int _lastTimeTaken; + private long _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 int LastTimeTaken + { + get { return _lastTimeTaken; } + set + { + _lastTimeTaken = value; + OnPropertyChanged(); + } + } + + public long AverageTimeTaken + { + get + { + if (_numberOfRefreshes == 0) + return 0; + + return Convert.ToInt64(_refreshTimeTaken / _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 = Convert.ToInt32(e.TimeTaken.TotalMilliseconds); + _refreshTimeTaken += Convert.ToInt32(e.TimeTaken.TotalMilliseconds); + _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..077b19f --- /dev/null +++ b/src/Servers/DataServer.Wpf/RxdSolutionsBackground.xaml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/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/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..45532b0 100644 --- a/src/Servers/DataServer/DataServer.cs +++ b/src/Servers/DataServer/DataServer.cs @@ -34,7 +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 { @@ -43,7 +46,7 @@ public int ClientCount return _clients.Count; } } - + public string DefaultMessage { get; set; } = Resources.DefaultGettingDataMessage; public bool IsRunning { get; private set; } @@ -59,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; @@ -100,6 +103,8 @@ public void Start() StartPublisher(); _dataServiceProvider.Start(); + + OnStatusChanged?.Invoke(this, new EventArgs()); } SendServiceStatus(); @@ -124,6 +129,8 @@ public void Stop() _dataPublisher = null; _dataServiceProvider.Stop(); + + OnStatusChanged?.Invoke(this, new EventArgs()); } SendServiceStatus(); @@ -154,7 +161,7 @@ public void Register() SendServiceStatus(); } - catch(Exception ex) + catch (Exception ex) { throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); } @@ -174,7 +181,7 @@ public void Unregister() throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); } } - + public ServiceStatus GetServiceStatus() { try @@ -195,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))) { @@ -210,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 @@ -218,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))) { @@ -233,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 @@ -241,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)) { @@ -264,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))) { @@ -279,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 @@ -293,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 @@ -307,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 @@ -335,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 @@ -349,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 }); } @@ -361,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() }); } @@ -420,7 +479,7 @@ public void RequestCalculate() catch (Exception ex) { throw new FaultException(new ErrorFaultContract() { Message = ex.Message }); - } + } } public void LoadPositions() @@ -455,6 +514,10 @@ public int SystemValueCount get => _systemSubscriptions.Count; } + public int PublishQueueCount + { + get => _publishQueue.Count; + } private void StartPublisher() { @@ -468,6 +531,8 @@ private void StartPublisher() return; MergeData(e); + + OnPublishQueueChanged?.Invoke(this, new EventArgs()); } } catch (Exception ex) @@ -528,7 +593,7 @@ private void CheckClientsAlive() private void SendMessageToAllClients(Action send) { - lock(_clients) + lock (_clients) { if (_clients.Count == 0) return; @@ -554,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); @@ -564,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); @@ -574,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); @@ -584,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); @@ -599,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)); @@ -617,7 +686,8 @@ private void Unregister(string sessionId) private void SendServiceStatus() { - SendMessageToAllClients((id, client) => { + SendMessageToAllClients((id, client) => + { client.SendServiceStaus(GetServiceStatus()); @@ -626,7 +696,13 @@ private void SendServiceStatus() private void DataService_DataAvailable(object sender, DataAvailableEventArgs e) { - _publishQueue.Add(e); + OnDataReceived?.Invoke(this, 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..021a758 100644 --- a/src/Servers/DataServer/DataServer.csproj +++ b/src/Servers/DataServer/DataServer.csproj @@ -4,8 +4,8 @@ {1D5FF3ED-123D-41FC-A6ED-C2C763A71168} Library RxdSolutions.FusionLink - FusionLinkDataServer - net452 + DataServer + 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 21d682e..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; @@ -13,9 +15,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) @@ -23,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; @@ -34,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/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/OpenFusionLinkExcelScenario.cs b/src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs similarity index 97% rename from src/Servers/FusionLink/OpenFusionLinkExcelScenario.cs rename to src/Servers/FusionLink/Client/OpenFusionLinkExcelScenario.cs index 3569533..4f91559 100644 --- a/src/Servers/FusionLink/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/ShowDashboardScenario.cs b/src/Servers/FusionLink/Client/ShowDashboardScenario.cs new file mode 100644 index 0000000..bd5a2a9 --- /dev/null +++ b/src/Servers/FusionLink/Client/ShowDashboardScenario.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 ShowDashboardScenario : CSMScenario + { + public override eMProcessingType GetProcessingType() + { + return eMProcessingType.M_pUserPreference; + } + + public override bool AlwaysEnabled() + { + return true; + } + + public override CMString GetName() + { + return Resources.ShowDashboard; + } + + 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, Resources.DashboardWindowCaption, wndKey, false); + } + + base.Run(); + } + } +} diff --git a/src/Servers/FusionLink/ShowFusionLinkScenario.cs b/src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs similarity index 94% rename from src/Servers/FusionLink/ShowFusionLinkScenario.cs rename to src/Servers/FusionLink/Client/ShowFusionLinkScenario.cs index db234d6..8583051 100644 --- a/src/Servers/FusionLink/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 42cef7c..e596847 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 @@ -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 @@ -45,6 +46,7 @@ + @@ -93,6 +95,10 @@ ..\..\..\libs\RXDSolutions\7_1\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_1\Sophis.Windows.dll + false + @@ -139,6 +145,10 @@ ..\..\..\libs\RXDSolutions\7_2\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_2\Sophis.Windows.dll + false + @@ -177,6 +187,10 @@ ..\..\..\libs\RXDSolutions\7_1\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_1\Sophis.Windows.dll + false + @@ -223,6 +237,10 @@ ..\..\..\libs\RXDSolutions\7_2\Sophis.Core.Data.dll false + + ..\..\..\libs\RXDSolutions\7_2\Sophis.Windows.dll + false + @@ -253,6 +271,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 @@ -291,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 @@ -308,11 +334,9 @@ 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/AggregateListener.cs b/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs similarity index 62% rename from src/Servers/FusionLink/Listeners/AggregateListener.cs rename to src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs index 8ac69aa..87c393d 100644 --- a/src/Servers/FusionLink/Listeners/AggregateListener.cs +++ b/src/Servers/FusionLink/Listeners/AggregatePortfolioListener.cs @@ -3,22 +3,22 @@ using System; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { - 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..0b5328c --- /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.Listeners +{ + 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..9eb9c0c --- /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.Listeners +{ + 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/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(); + } + } +} 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 new file mode 100644 index 0000000..d4acd5d --- /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.Listeners +{ + 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..f7b4dca --- /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.Listeners +{ + 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..81a821f 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioActionListener.cs @@ -5,11 +5,11 @@ using sophis.portfolio; using sophis.tools; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { - 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/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 dbca38c..c75b172 100644 --- a/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs +++ b/src/Servers/FusionLink/Listeners/PortfolioEventListener.cs @@ -4,11 +4,11 @@ using System; using sophis.portfolio; -namespace RxdSolutions.FusionLink +namespace RxdSolutions.FusionLink.Listeners { - 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..8ec9204 --- /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.Listeners +{ + public class PositionActionListener : CSMPositionAction + { + public static event EventHandler PositionChanged; + + public override void NotifyDeleted(CSMPosition position, CSMEventVector message) + { + 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(), true)); + + base.NotifyModified(position, message); + } + + public override void NotifyTransferred(CSMPosition position, CSMEventVector message) + { + PositionChanged?.Invoke(this, new PositionChangedEventArgs(position.GetIdentifier(), true)); + + 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..1d57d3e --- /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.Listeners +{ + 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..880fa8b --- /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.Listeners +{ + 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..23ecb48 --- /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.Listeners +{ + 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(), 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(), 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(), true)); + + 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..2462a2b --- /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.Listeners +{ + 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..4d8e4dd --- /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.Listeners +{ + public class TransactionEventListener : CSMTransactionEvent + { + public static event EventHandler TransactionChanged; + + public override bool HasBeenCreated(CSMTransaction transaction) + { + 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.GetTransactionCode(), transaction.GetPositionID(), false)); + + return base.HasBeenDeleted(transaction); + } + + public override bool HasBeenModified(CSMTransaction original, CSMTransaction transaction) + { + TransactionChanged?.Invoke(this, new TransactionChangedEventArgs(transaction.GetTransactionCode(), 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..4257142 100644 --- a/src/Servers/FusionLink/Main.cs +++ b/src/Servers/FusionLink/Main.cs @@ -8,7 +8,10 @@ using System.Threading.Tasks; 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; @@ -21,13 +24,17 @@ 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 bool _displayDebugMessage = false; + private static PositionActionListener _positionActionListener; + private static PositionEventListener _positionEventListener; + private static TransactionActionListener _transactionActionListener; + private static TransactionEventListener _transactionEventListener; + private static ServiceHost _host; + + public static DataServer DataServer; public static CaptionBar CaptionBar; public Dispatcher _context; @@ -38,8 +45,6 @@ public void EntryPoint() { _context = Dispatcher.CurrentDispatcher; - LoadConfig(); - RegisterListeners(); RegisterScenarios(); @@ -52,8 +57,10 @@ public void Close() { try { - _dataServer.OnClientConnectionChanged -= OnClientConnectionChanged; - _dataServer.Close(); + AutomaticComputatingPreferenceChangeListener.Stop(); + + DataServer.OnClientConnectionChanged -= OnClientConnectionChanged; + DataServer.Close(); _host?.Close(); } catch (Exception ex) @@ -97,12 +104,28 @@ 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); + + AutomaticComputatingPreferenceChangeListener.Start(); + AutomaticComputatingPreferenceChangeListener.AutomaticComputatingChanged += AutomaticComputatingChanged; } private void RegisterScenarios() { CSMScenario.Register(Resources.ScenarioShowCaptionBarMessage, new ShowFusionLinkScenario()); CSMScenario.Register(Resources.OpenFusionLinkExcel, new OpenFusionLinkExcelScenario()); + CSMScenario.Register(Resources.ShowDashboard, new ShowDashboardScenario()); } private void RegisterUI() @@ -111,45 +134,22 @@ 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 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 +160,7 @@ private Task RegisterServer() { try { - _host = DataServerHostFactory.Create(_dataServer); + _host = DataServerHostFactory.Create(DataServer); _host.Faulted += Host_Faulted; } catch (AddressAlreadyInUseException) @@ -171,13 +171,11 @@ private Task RegisterServer() _host = null; } - _dataServer = _host.SingletonInstance as DataServer; - - _dataServer.Start(); - _dataServer.OnClientConnectionChanged += OnClientConnectionChanged; + DataServer = _host.SingletonInstance as DataServer; - if(_displayDebugMessage) - _dataServer.OnSubscriptionChanged += OnSubscriptionChanged; + DataServer.Start(); + DataServer.OnClientConnectionChanged += OnClientConnectionChanged; + DataServer.OnStatusChanged += OnStatusChanged; ; }); } @@ -197,50 +195,59 @@ 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; - } - - private void LoadConfig() - { - CSMConfigurationFile.getEntryValue("FusionLink", "ShowDebug", ref _displayDebugMessage, false); + DataServer.DefaultMessage = defaultMessage; } private void UpdateCaption() { var caption = new StringBuilder(); - //Listening int processId = Process.GetCurrentProcess().Id; 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(" / "); caption.Append(clientsConnectedCaption); } - if (_displayDebugMessage) + 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) { - 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(); } @@ -253,5 +260,23 @@ private void OnClientConnectionChanged(object sender, ClientConnectionChangedEve }, DispatcherPriority.ApplicationIdle); } + + private void OnStatusChanged(object sender, EventArgs e) + { + _context.InvokeAsync(() => + { + UpdateCaption(); + + }, 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 b6ee155..bd22ce6 100644 --- a/src/Servers/FusionLink/Properties/Resources.Designer.cs +++ b/src/Servers/FusionLink/Properties/Resources.Designer.cs @@ -96,6 +96,42 @@ 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 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. + /// + internal static string DataServerStopped { + get { + return ResourceManager.GetString("DataServerStopped", resourceCulture); + } + } + /// /// Looks up a localized string similar to Getting data... please wait. /// @@ -196,6 +232,15 @@ internal static string ScenarioShowCaptionBarMessage { } } + /// + /// Looks up a localized string similar to Display FusionLink Dashboard. + /// + internal static string ShowDashboard { + get { + return ResourceManager.GetString("ShowDashboard", 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..a17df06 100644 --- a/src/Servers/FusionLink/Properties/Resources.resx +++ b/src/Servers/FusionLink/Properties/Resources.resx @@ -129,6 +129,18 @@ Copy Row(s) as Excel FusionLink References + + FusionLink Dashboard + + + Automatic + + + On F9 + + + Stopped + Getting data... please wait @@ -163,6 +175,9 @@ Display FusionLink + + Display FusionLink Dashboard + Display FusionLink diff --git a/src/Servers/FusionLink/Provider/CellValueBase.cs b/src/Servers/FusionLink/Provider/CellValueBase.cs index bcb5086..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 { @@ -12,17 +12,21 @@ internal abstract class CellValueBase : IDisposable public SSMCellStyle CellStyle = new SSMCellStyle(); - public CSMPortfolioColumn Column { get; } + public CSMExtraction Extraction { get; } + + public CSMPortfolioColumn Column { get; protected set; } 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..d3e7cef 100644 --- a/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs +++ b/src/Servers/FusionLink/Provider/FusionDataServiceProvider.cs @@ -3,22 +3,27 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +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 + 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,11 +33,7 @@ public class FusionDataServiceProvider : IDataServerProvider private readonly Subscriptions _positionCellSubscriptions; private readonly Dictionary _systemValueSubscriptions; - //Avoid infinite loops - private int _computeCount; - - //Optimize the data refreshing - private int _dataRefreshRequests = 0; + private readonly CSMExtraction _mainExtraction; public bool IsRunning { get; private set; } @@ -42,12 +43,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 +61,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() @@ -345,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) { @@ -358,6 +364,8 @@ private void GlobalFunctions_PortfolioCalculationEnded(object sender, PortfolioC { case sophis.misc.CSMGlobalFunctions.eMPortfolioCalculationType.M_pcFullCalculation: + refreshData = true; + try { ComputePortfolios(e.FolioId); @@ -388,7 +396,7 @@ private void GlobalFunctions_PortfolioCalculationEnded(object sender, PortfolioC if (id == 1) { - _dataRefreshRequests++; + refreshData = true; } } catch (Exception ex) @@ -404,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()); } } @@ -474,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() @@ -519,50 +515,136 @@ void RefreshSystemValues() } } + var sw = Stopwatch.StartNew(); + RefreshPortfolioCells(); RefreshPositionCells(); RefreshSystemValues(); + sw.Stop(); + + args.TimeTaken = sw.Elapsed; + DataAvailable?.Invoke(this, args); } 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 { - if (e.IsLocal) + void NotifyDataChanged() { - //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 _portfolioCellSubscriptions.GetCells().ToList()) { - var args = new DataAvailableEventArgs(); + if (cell.FolioId == portfolioId) + { + _portfolioCellSubscriptions.Remove(cell.FolioId, cell.ColumnName); + _portfolioCellSubscriptions.Add(portfolioId, cell.ColumnName); + } + } - foreach (var value in _portfolioPropertySubscriptions.GetCells()) + foreach (var cell in _portfolioPropertySubscriptions.GetCells().ToList()) + { + if (cell.FolioId == portfolioId) { - args.PortfolioProperties.Add((value.FolioId, value.Property), value.GetValue()); + _portfolioPropertySubscriptions.Remove(cell.FolioId, cell.Property); + _portfolioPropertySubscriptions.Add(portfolioId, cell.Property); } + } + + var args = new DataAvailableEventArgs(); + + 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); + } - DataAvailable?.Invoke(this, args); + if (isLocal) + { + //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(() => + { + NotifyDataChanged(); }); } else { + NotifyDataChanged(); + } + } + catch (Exception ex) + { + CSMLog.Write(_className, "RefreshPortfolio", CSMLog.eMVerbosity.M_error, ex.ToString()); + } + } + + private void RefreshPosition(int positionId, bool isLocal) + { + try + { + void NotifyDataChanged() + { + 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 _portfolioPropertySubscriptions.GetCells()) + foreach (var value in _positionCellSubscriptions.Get(positionId)) { - args.PortfolioProperties.Add((value.FolioId, value.Property), value.GetValue()); + 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) + catch (Exception ex) { - CSMLog.Write(_className, "PortfolioListener_PortfolioChanged", CSMLog.eMVerbosity.M_error, ex.ToString()); + CSMLog.Write(_className, "RefreshPosition", CSMLog.eMVerbosity.M_error, ex.ToString()); } } @@ -632,5 +714,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/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 817bda9..47767ad 100644 --- a/src/Servers/FusionLink/Provider/PortfolioCellValue.cs +++ b/src/Servers/FusionLink/Provider/PortfolioCellValue.cs @@ -3,18 +3,19 @@ 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 { - public CSMPortfolio Portfolio { get; } + public CSMPortfolio Portfolio { get; private set; } 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); @@ -23,9 +24,9 @@ public PortfolioCellValue(int folioId, string column) : base(column) [HandleProcessCorruptedStateExceptions] public override object GetValue() { - try + object GetValueInternal() { - if(Error is object) + if (Error is object) { return Error.Message; } @@ -45,15 +46,33 @@ 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); 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/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 49c87df..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 { @@ -14,7 +15,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); @@ -23,7 +24,7 @@ public PositionCellValue(int positionId, string column) : base(column) [HandleProcessCorruptedStateExceptions] public override object GetValue() { - try + object GetValueInternal() { if (Position is null) { @@ -37,7 +38,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); @@ -48,9 +49,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; + } } } 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; 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))]