From 6e13195f8b041d07308f74f199fb3e1d1cb346cf Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Mon, 27 Apr 2020 12:27:07 +0300 Subject: Fixed issue with firmware upgrade on Machine Studio. --- .../Tango.MachineStudio.UI/ViewModels/FirmwareUpgradeViewVM.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels') diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/FirmwareUpgradeViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/FirmwareUpgradeViewVM.cs index 365c1db49..7d2d8b401 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/FirmwareUpgradeViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/FirmwareUpgradeViewVM.cs @@ -202,6 +202,7 @@ namespace Tango.MachineStudio.UI.ViewModels { IsFree = true; CanClose = true; + _stream?.Dispose(); UpgradeError = ex.FlattenMessage(); CurrentPage = 3; } -- cgit v1.3.1 From 5571ab086f6288b27117e3eaae443415a162403c Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Tue, 5 May 2020 19:24:45 +0300 Subject: Require Safety Level Operations ! --- .../Integration/ExternalBridgeLoginRequest.proto | 2 + .../FSE/Tango.FSE.Common/FSESettings.cs | 1 + .../Connection/DefaultMachineProvider.cs | 2 + .../Dialogs/MachineConnectionSignalRView.xaml | 4 +- .../Dialogs/MachineConnectionWifiView.xaml | 4 +- .../Dialogs/MachineConnectionWifiViewVM.cs | 12 ++ .../RemoteJob/DefaultRemoteJobProvider.cs | 13 +- .../ViewModels/MachineLoginViewVM.cs | 5 + .../ViewModels/MainViewVM.cs | 2 + .../Views/MachineLoginView.xaml | 23 ++- .../SafetyLevelOperationsConfirmationView.xaml | 52 ++++++ .../SafetyLevelOperationsConfirmationView.xaml.cs | 28 +++ .../SafetyLevelOperationsConfirmationViewVM.cs | 82 +++++++++ .../Notifications/DefaultNotificationProvider.cs | 80 +++++--- .../PPC/Tango.PPC.UI/Tango.PPC.UI.csproj | 10 +- .../ViewModels/ExternalBridgeViewVM.cs | 48 ++++- .../ExternalBridgeClientConnectedEventArgs.cs | 19 +- .../ExternalBridge/ExternalBridgeReceiver.cs | 16 ++ .../ExternalBridge/ExternalBridgeService.cs | 27 ++- .../ExternalBridge/ExternalBridgeSignalRClient.cs | 9 +- .../ExternalBridge/ExternalBridgeTcpClient.cs | 9 +- .../Visual_Studio/Tango.PMR/Common/MessageType.cs | 204 +++++++++++---------- .../Integration/ExternalBridgeLoginRequest.cs | 65 ++++++- Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj | 107 ++++++++++- 24 files changed, 648 insertions(+), 176 deletions(-) create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationViewVM.cs (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels') diff --git a/Software/PMR/Messages/Integration/ExternalBridgeLoginRequest.proto b/Software/PMR/Messages/Integration/ExternalBridgeLoginRequest.proto index 8e76542e7..6b4831a10 100644 --- a/Software/PMR/Messages/Integration/ExternalBridgeLoginRequest.proto +++ b/Software/PMR/Messages/Integration/ExternalBridgeLoginRequest.proto @@ -12,4 +12,6 @@ message ExternalBridgeLoginRequest string HostName = 3; string Password = 4; ExternalBridgeLoginIntent Intent = 5; + bool RequireSafetyLevelOperations = 6; + string UserName = 7; } \ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs index 05a7b29f4..dbdd1554f 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSESettings.cs @@ -22,6 +22,7 @@ namespace Tango.FSE.Common { public String SerialNumber { get; set; } public ExternalBridgeLoginIntent Intent { get; set; } + public bool RequireSafetyLevelOperations { get; set; } public String Password { get; set; } } diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs index f4a98e8fc..633ab4436 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Connection/DefaultMachineProvider.cs @@ -219,7 +219,9 @@ namespace Tango.FSE.UI.Connection HostName = Environment.MachineName, Password = (vm as MachineConnectionWifiViewVM).Password, Intent = (vm as MachineConnectionWifiViewVM).SelectedIntent, + RequireSafetyLevelOperations = (vm as MachineConnectionWifiViewVM).RequireSafetyLevelOperations, UserGuid = AuthenticationProvider.CurrentUser.Guid, + UserName = AuthenticationProvider.CurrentUser.Contact.FullName }; LogManager.Log($"Target machine requires a secure connection. Login Request:\n{secureClient.LoginRequest.ToJsonString()}"); diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionSignalRView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionSignalRView.xaml index 832fa73f1..b091516df 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionSignalRView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionSignalRView.xaml @@ -38,8 +38,8 @@ Intent - - + + Require Safety Level Operations diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiView.xaml index 70de8ddea..0d037e939 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiView.xaml +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiView.xaml @@ -38,8 +38,8 @@ Intent - - + + Require Safety Level Operations diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiViewVM.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiViewVM.cs index e70235f93..740e08ff0 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiViewVM.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Dialogs/MachineConnectionWifiViewVM.cs @@ -43,6 +43,16 @@ namespace Tango.FSE.UI.Dialogs /// public List Intents { get; set; } + private bool _requireSafetyLevelOperations; + /// + /// Gets or sets a value indicating whether to require safety level operations on the remote machine. + /// + public bool RequireSafetyLevelOperations + { + get { return _requireSafetyLevelOperations; } + set { _requireSafetyLevelOperations = value; RaisePropertyChangedAuto(); } + } + private ExternalBridgeTcpClient _machine; /// /// Gets or sets the selected machine. @@ -86,6 +96,7 @@ namespace Tango.FSE.UI.Dialogs { Password = EncryptionHelper.Decrypt(connectionSettings.Password); SelectedIntent = connectionSettings.Intent; + RequireSafetyLevelOperations = connectionSettings.RequireSafetyLevelOperations; RememberMachinePassword = true; } } @@ -111,6 +122,7 @@ namespace Tango.FSE.UI.Dialogs connectionSettings.SerialNumber = Machine.SerialNumber; connectionSettings.Password = EncryptionHelper.Encrypt(Password); connectionSettings.Intent = SelectedIntent; + connectionSettings.RequireSafetyLevelOperations = RequireSafetyLevelOperations; } else { diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteJob/DefaultRemoteJobProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteJob/DefaultRemoteJobProvider.cs index 586bf2105..6a5702332 100644 --- a/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteJob/DefaultRemoteJobProvider.cs +++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteJob/DefaultRemoteJobProvider.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Core; using Tango.Core.DI; using Tango.FSE.Common.Connection; using Tango.FSE.Common.Notifications; @@ -15,7 +16,7 @@ using Tango.Transport; namespace Tango.FSE.UI.RemoteJob { [TangoCreateWhenRegistered] - public class DefaultRemoteJobProvider : IRemoteJobProvider + public class DefaultRemoteJobProvider : ExtendedObject, IRemoteJobProvider { private JobHandler _handler; @@ -56,8 +57,12 @@ namespace Tango.FSE.UI.RemoteJob InvalidateJobResponse(response); }, (ex) => { - //Failed - NotificationProvider.PushErrorReportingSnackbar(ex, "Remote Job Module Error", "Remote job display failed to initialize."); + if (!(ex is TransporterDisconnectedException)) + { + //Failed + LogManager.Log(ex, "Error initializing remote job tracking."); + NotificationProvider.PushErrorReportingSnackbar(ex, "Remote Job Module Error", "Remote job tracking failed to initialize."); + } }, () => { //Completed ? (never) @@ -65,7 +70,7 @@ namespace Tango.FSE.UI.RemoteJob } catch (Exception ex) { - NotificationProvider.PushErrorReportingSnackbar(ex, "Remote Job Module Error", "Remote job display failed to initialize."); + NotificationProvider.PushErrorReportingSnackbar(ex, "Remote Job Module Error", "Remote job tracking failed to initialize."); } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MachineLoginViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MachineLoginViewVM.cs index 9bee35697..81d3f4243 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MachineLoginViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MachineLoginViewVM.cs @@ -26,6 +26,11 @@ namespace Tango.MachineStudio.UI.ViewModels /// public ExternalBridgeLoginIntent Intent { get; set; } + /// + /// Gets or sets a value indicating whether to require safety level operations permission from the remote machine. + /// + public bool RequireSafetyOperations { get; set; } + /// /// Gets or sets the login command. /// diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs index 46491c823..0e02d779f 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs @@ -598,6 +598,8 @@ namespace Tango.MachineStudio.UI.ViewModels Password = config.Password, UserGuid = AuthenticationProvider.CurrentUser.Guid, Intent = config.Intent, + UserName = AuthenticationProvider.CurrentUser.Contact.FullName, + RequireSafetyLevelOperations = config.RequireSafetyOperations }); _reconnectionMachine = machine; diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MachineLoginView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MachineLoginView.xaml index 5be9ba089..e4cdfe199 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MachineLoginView.xaml +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MachineLoginView.xaml @@ -17,14 +17,21 @@ - - - - + + + + Require Safety Level Operations + + + + + + + diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml new file mode 100644 index 000000000..e96b39a63 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml @@ -0,0 +1,52 @@ + + + + + DECLINE + + + + + + + APPROVE + + + + Safety Level Access Request + + A remote client is requesting a safety level connection to this machine. + + Once approved, the remote user will be able to perform any mechanical operation remotely. + + + + + Request Information + + Address: + + + Host Name: + + + App ID: + + + User: + + + + + + + diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml.cs new file mode 100644 index 000000000..ef689f1de --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.PPC.UI.Dialogs +{ + /// + /// Interaction logic for SafetyLevelOperationsConfirmationView.xaml + /// + public partial class SafetyLevelOperationsConfirmationView : UserControl + { + public SafetyLevelOperationsConfirmationView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationViewVM.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationViewVM.cs new file mode 100644 index 000000000..f8027b4c2 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Dialogs/SafetyLevelOperationsConfirmationViewVM.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Threading; +using Tango.Integration.ExternalBridge; +using Tango.PMR.Integration; +using Tango.SharedUI; + +namespace Tango.PPC.UI.Dialogs +{ + public class SafetyLevelOperationsConfirmationViewVM : DialogViewVM + { + private DispatcherTimer _timer; + + private int _maxSeconds; + public int MaxSeconds + { + get { return _maxSeconds; } + set { _maxSeconds = value; RaisePropertyChangedAuto(); } + } + + private int _secondsRemaining; + public int SecondsRemaining + { + get { return _secondsRemaining; } + set { _secondsRemaining = value; RaisePropertyChangedAuto(); } + } + + private ExternalBridgeClientConnectedEventArgs _connection; + /// + /// Gets or sets the last client connection event arguments. + /// + public ExternalBridgeClientConnectedEventArgs Connection + { + get { return _connection; } + set { _connection = value; RaisePropertyChangedAuto(); } + } + + public SafetyLevelOperationsConfirmationViewVM(ExternalBridgeClientConnectedEventArgs connection) + { + Connection = connection; + + MaxSeconds = 30; + SecondsRemaining = 30; + + _timer = new DispatcherTimer(DispatcherPriority.Background, Application.Current.Dispatcher); + _timer.Interval = TimeSpan.FromMilliseconds(800); + _timer.Tick += _timer_Tick; + } + + public override void OnShow() + { + base.OnShow(); + _timer.Start(); + } + + protected override void Accept() + { + _timer.Stop(); + base.Accept(); + } + + protected override void Cancel() + { + _timer.Stop(); + base.Cancel(); + } + + private void _timer_Tick(object sender, EventArgs e) + { + SecondsRemaining--; + + if (SecondsRemaining == 0) + { + Cancel(); + } + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Notifications/DefaultNotificationProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/Notifications/DefaultNotificationProvider.cs index a8ac2b24b..3b1e1e2f5 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Notifications/DefaultNotificationProvider.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Notifications/DefaultNotificationProvider.cs @@ -332,29 +332,33 @@ namespace Tango.PPC.UI.Notifications /// public async Task ShowDialog(T datacontext, FrameworkElement view) where T : DialogViewVM { - view.DataContext = datacontext; - - TangoIOC.Default.Inject(datacontext); + TaskCompletionSource source = new TaskCompletionSource(); - view.Loaded += (_, __) => + InvokeUI(() => { view.DataContext = datacontext; - datacontext.OnShow(); - }; - TaskCompletionSource source = new TaskCompletionSource(); + TangoIOC.Default.Inject(datacontext); - datacontext.Accepted += () => { OnDialogClosed(); source.SetResult(datacontext); }; - datacontext.Canceled += () => { OnDialogClosed(); source.SetResult(datacontext); }; + view.Loaded += (_, __) => + { + view.DataContext = datacontext; + datacontext.OnShow(); + }; - if (CurrentDialog == null) - { - CurrentDialog = view; - } - else - { - _pendingDialogs.Enqueue(new PendingNotification(new DialogAndView(datacontext, view), source)); - } + datacontext.Accepted += () => { OnDialogClosed(); source.SetResult(datacontext); }; + datacontext.Canceled += () => { OnDialogClosed(); source.SetResult(datacontext); }; + + if (CurrentDialog == null) + { + CurrentDialog = view; + } + else + { + _pendingDialogs.Enqueue(new PendingNotification(new DialogAndView(datacontext, view), source)); + } + + }); var result = await source.Task; return result as T; @@ -386,23 +390,31 @@ namespace Tango.PPC.UI.Notifications /// public Task ShowDialog(T datacontext) where T : DialogViewVM { - var callingAssembly = datacontext.GetType().Assembly; - String viewName = datacontext.GetType().FullName.Replace("VM", ""); - var viewType = callingAssembly.GetType(viewName); + TaskCompletionSource source = new TaskCompletionSource(); - if (viewType == null) + InvokeUI(async () => { - throw new NullReferenceException("View type for " + datacontext.GetType().Name + " could not be found!"); - } + var callingAssembly = datacontext.GetType().Assembly; + String viewName = datacontext.GetType().FullName.Replace("VM", ""); + var viewType = callingAssembly.GetType(viewName); - var view = Activator.CreateInstance(viewType) as FrameworkElement; + if (viewType == null) + { + throw new NullReferenceException("View type for " + datacontext.GetType().Name + " could not be found!"); + } - if (view == null) - { - throw new NullReferenceException("The view " + viewType.ToString() + " is not of type framework element."); - } + var view = Activator.CreateInstance(viewType) as FrameworkElement; + + if (view == null) + { + throw new NullReferenceException("The view " + viewType.ToString() + " is not of type framework element."); + } - return ShowDialog(datacontext, view); + T result = await ShowDialog(datacontext, view); + source.SetResult(result); + }); + + return source.Task; } /// @@ -414,7 +426,15 @@ namespace Tango.PPC.UI.Notifications /// public Task ShowDialog() where T : DialogViewVM { - return ShowDialog(Activator.CreateInstance()); + TaskCompletionSource source = new TaskCompletionSource(); + + InvokeUI(async () => + { + var result = await ShowDialog(Activator.CreateInstance()); + source.SetResult(result); + }); + + return source.Task; } /// diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj index 9a5824d81..ce0171fc2 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj @@ -136,6 +136,10 @@ InsufficientLiquidQuantityView.xaml + + SafetyLevelOperationsConfirmationView.xaml + + ThreadLoadingView.xaml @@ -254,6 +258,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -697,7 +705,7 @@ if $(ConfigurationName) == Debug copy /Y "$(TargetDir)Packages" "$(TargetDir)" - + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/ExternalBridgeViewVM.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/ExternalBridgeViewVM.cs index 4b7d8978e..538b1f272 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/ExternalBridgeViewVM.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModels/ExternalBridgeViewVM.cs @@ -12,6 +12,7 @@ using Tango.PMR.Integration; using Tango.PPC.Common; using Tango.PPC.Common.ExternalBridge; using Tango.PPC.Common.Navigation; +using Tango.PPC.UI.Dialogs; namespace Tango.PPC.UI.ViewModels { @@ -24,6 +25,10 @@ namespace Tango.PPC.UI.ViewModels { private bool _disconnecting; + //Full Name, Authentication time + private Dictionary _authenticatedSafetyUsers; + private const int KEEP_SAFETY_AUTHENTICATION_MINUTES = 1; + #region Properties private ExternalBridgeClientConnectedEventArgs _connection; @@ -64,6 +69,7 @@ namespace Tango.PPC.UI.ViewModels /// public ExternalBridgeViewVM() { + _authenticatedSafetyUsers = new Dictionary(); CloseSessionCommand = new RelayCommand(CloseSession, () => !_disconnecting); } @@ -133,7 +139,7 @@ namespace Tango.PPC.UI.ViewModels /// /// The sender. /// The instance containing the event data. - private void ExternalBridgeService_ConnectionRequest(object sender, ExternalBridgeClientConnectedEventArgs e) + private async void ExternalBridgeService_ConnectionRequest(object sender, ExternalBridgeClientConnectedEventArgs e) { LogManager.Log($"External bridge connection request received.\n{e.ToJsonString()}"); @@ -142,8 +148,6 @@ namespace Tango.PPC.UI.ViewModels e.ApplicationInformation.Version = ApplicationManager.Version.ToString(); e.ApplicationInformation.StartupDate = ApplicationManager.StartUpDate.ToUniversalTime().ToString(); - e.Confirmed = true; - Connection = e; User = Adapter.Users.SingleOrDefault(x => x.Guid == e.Request.UserGuid); @@ -153,6 +157,43 @@ namespace Tango.PPC.UI.ViewModels LogManager.Log($"External bridge connection user has been identified as {User.Contact.FullName}"); } + if (e.Request.RequireSafetyLevelOperations) + { + DateTime lastAuthenticationTime; + bool bypassSafetyConfirmation = false; + + if (_authenticatedSafetyUsers.TryGetValue(e.Request.UserName, out lastAuthenticationTime)) + { + if (DateTime.Now < lastAuthenticationTime.AddMinutes(KEEP_SAFETY_AUTHENTICATION_MINUTES)) + { + bypassSafetyConfirmation = true; + e.Confirm(); + _authenticatedSafetyUsers[e.Request.UserName] = DateTime.Now; + } + } + + if (!bypassSafetyConfirmation) + { + SafetyLevelOperationsConfirmationViewVM vm = new SafetyLevelOperationsConfirmationViewVM(e); + await NotificationProvider.ShowDialog(vm); + + if (vm.DialogResult) + { + e.Confirm(); + _authenticatedSafetyUsers[e.Request.UserName] = DateTime.Now; + } + else + { + e.Decline("Safety level connection refused by the remote user."); + return; + } + } + } + else + { + e.Confirm(); + } + if (e.Request.Intent == ExternalBridgeLoginIntent.FullControl) { LogManager.Log("Navigating to external bridge view..."); @@ -164,6 +205,7 @@ namespace Tango.PPC.UI.ViewModels } else { + e.Decline("Connection password did not match the machine external bridge password."); LogManager.Log("Connection password did not match the machine external bridge password."); } } diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeClientConnectedEventArgs.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeClientConnectedEventArgs.cs index 7130845cc..e976898ef 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeClientConnectedEventArgs.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeClientConnectedEventArgs.cs @@ -9,17 +9,30 @@ namespace Tango.Integration.ExternalBridge { public class ExternalBridgeClientConnectedEventArgs : EventArgs { + private Action _confirmAction; + private Action _declineAction; + public ExternalBridgeLoginRequest Request { get; set; } public String Address { get; set; } public ApplicationInformation ApplicationInformation { get; set; } - public bool Confirmed { get; set; } - - public ExternalBridgeClientConnectedEventArgs() + public ExternalBridgeClientConnectedEventArgs(Action confirmAction, Action declineAction) { + _confirmAction = confirmAction; + _declineAction = declineAction; ApplicationInformation = new ApplicationInformation(); } + + public void Confirm() + { + _confirmAction?.Invoke(); + } + + public void Decline(String reason) + { + _declineAction?.Invoke(reason); + } } } diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs index 0b63f0be2..9c61f9e8b 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeReceiver.cs @@ -53,6 +53,7 @@ namespace Tango.Integration.ExternalBridge private IMachineOperator _machineOperator; private Dictionary _messageHandlers; + private HashSet _safetyOperations; #region Events @@ -65,6 +66,8 @@ namespace Tango.Integration.ExternalBridge #region Properties + public bool AllowSafetyLevelOperations { get; set; } + public bool RequiresDiagnostics { get; private set; } public bool RequiresDebugLogs { get; private set; } public bool RequiresEventsNotification { get; private set; } @@ -120,6 +123,8 @@ namespace Tango.Integration.ExternalBridge _messageHandlers.Add(MessageType.JobRequest, new MessageHandler(OnJobRequest, ExternalBridgeLoginIntent.Diagnostics)); _messageHandlers.Add(MessageType.StartPowerDownRequest, new MessageHandler(OnStartPowerDownRequest, ExternalBridgeLoginIntent.Diagnostics)); + + _safetyOperations = new HashSet(); } public ExternalBridgeReceiver(TcpClient tcpClient, IMachineOperator machineOperator) : this(machineOperator) @@ -142,6 +147,15 @@ namespace Tango.Integration.ExternalBridge try { + if (!AllowSafetyLevelOperations) + { + if (_safetyOperations.Contains(container.Type)) + { + SendErrorResponse(new AuthenticationException("The specified action requires safety level permission that is not granted for the current session."), container.Token); + return; + } + } + if (_messageHandlers.ContainsKey(container.Type)) { var handler = _messageHandlers[container.Type]; @@ -268,6 +282,8 @@ namespace Tango.Integration.ExternalBridge response.DeviceInformation = deviceInfo; response.ApplicationInformation = applicationInfo; + AllowSafetyLevelOperations = request.Message.RequireSafetyLevelOperations; + SendResponse(response, container.Token); UpdateMachineOperatorStatus((UpdateStatus)_machineOperator.Status); diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs index a2ad9f9c9..6cb758043 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs @@ -372,18 +372,11 @@ namespace Tango.Integration.ExternalBridge return; } - ExternalBridgeClientConnectedEventArgs args = new ExternalBridgeClientConnectedEventArgs(); - args.Request = request; - args.Address = e.Address; - ConnectionRequest?.Invoke(this, args); + ExternalBridgeClientConnectedEventArgs args = null; - var response = new ExternalBridgeLoginResponse(); - response.Authenticated = args.Confirmed; - response.SerialNumber = Machine.SerialNumber; - response.DeviceInformation = MachineOperator.DeviceInformation; - - if (args.Confirmed) + args = new ExternalBridgeClientConnectedEventArgs(() => { + //Confirmed e.Confirm(Machine, MachineOperator.DeviceInformation, args.ApplicationInformation); HasSessions = true; @@ -391,11 +384,15 @@ namespace Tango.Integration.ExternalBridge { FullControlSessionReceiver = sender as ExternalBridgeReceiver; } - } - else - { - e.Decline("Invalid password or intent."); - } + }, (reason) => + { + //Declined + e.Decline(reason); + }); + + args.Request = request; + args.Address = e.Address; + ConnectionRequest?.Invoke(this, args); } private void Receiver_ReceiverRequestReceived(object sender, ExternalBridgeReceiverRequestReceivedEventArgs e) diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs index 343fb302f..51dc49ed0 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeSignalRClient.cs @@ -58,7 +58,14 @@ namespace Tango.Integration.ExternalBridge LogManager.Log("External Bridge SignalR Client Connected..."); - var response = await SendRequest(login, new TransportRequestConfig() { ShouldLog = true }); + TimeSpan? timeout = null; + + if (login.RequireSafetyLevelOperations) + { + timeout = TimeSpan.FromSeconds(30); + } + + var response = await SendRequest(login, new TransportRequestConfig() { ShouldLog = true, Timeout = timeout }); SessionLogger.CreateSession(); diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs index 850adff2d..d066b3be5 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs @@ -132,7 +132,14 @@ namespace Tango.Integration.ExternalBridge LogManager.Log("External Bridge TCP Client Connected..."); - var response = await SendRequest(login, new TransportRequestConfig() { ShouldLog = true }); + TimeSpan? timeout = null; + + if (login.RequireSafetyLevelOperations) + { + timeout = TimeSpan.FromSeconds(30); + } + + var response = await SendRequest(login, new TransportRequestConfig() { ShouldLog = true, Timeout = timeout }); ApplicationInformation = response.Message.ApplicationInformation; diff --git a/Software/Visual_Studio/Tango.PMR/Common/MessageType.cs b/Software/Visual_Studio/Tango.PMR/Common/MessageType.cs index d246a5eb7..e06fafb77 100644 --- a/Software/Visual_Studio/Tango.PMR/Common/MessageType.cs +++ b/Software/Visual_Studio/Tango.PMR/Common/MessageType.cs @@ -22,7 +22,7 @@ namespace Tango.PMR.Common { static MessageTypeReflection() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( - "ChFNZXNzYWdlVHlwZS5wcm90bxIQVGFuZ28uUE1SLkNvbW1vbirTOAoLTWVz", + "ChFNZXNzYWdlVHlwZS5wcm90bxIQVGFuZ28uUE1SLkNvbW1vbiqOOQoLTWVz", "c2FnZVR5cGUSCAoETm9uZRAAEhEKDUVycm9yUmVzcG9uc2UQARIUChBDYWxj", "dWxhdGVSZXF1ZXN0EAMSFQoRQ2FsY3VsYXRlUmVzcG9uc2UQBBITCg9Qcm9n", "cmVzc1JlcXVlc3QQBRIUChBQcm9ncmVzc1Jlc3BvbnNlEAYSHAoYU3R1YkNh", @@ -89,102 +89,104 @@ namespace Tango.PMR.Common { "YlJlc3BvbnNlEGQSFwoTU3R1YkFib3J0Sm9iUmVxdWVzdBBlEhgKFFN0dWJB", "Ym9ydEpvYlJlc3BvbnNlEGYSJAogU3R1Yk1pZFRhbmtQcmVzc3VyZVNlbnNv", "clJlcXVlc3QQZxIlCiFTdHViTWlkVGFua1ByZXNzdXJlU2Vuc29yUmVzcG9u", - "c2UQaBIlCiBFeHRlcm5hbEJyaWRnZVVkcERpc2NvdmVyeVBhY2tldBDoBxIf", - "ChpFeHRlcm5hbEJyaWRnZUxvZ2luUmVxdWVzdBDpBxIgChtFeHRlcm5hbEJy", - "aWRnZUxvZ2luUmVzcG9uc2UQ6gcSIAobRXh0ZXJuYWxCcmlkZ2VMb2dvdXRS", - "ZXF1ZXN0EOsHEiEKHEV4dGVybmFsQnJpZGdlTG9nb3V0UmVzcG9uc2UQ7AcS", - "IQocRGlyZWN0U3luY2hyb25pemF0aW9uUmVxdWVzdBDtBxIiCh1EaXJlY3RT", - "eW5jaHJvbml6YXRpb25SZXNwb25zZRDuBxIcChdPdmVycmlkZURhdGFCYXNl", - "UmVxdWVzdBDvBxIdChhPdmVycmlkZURhdGFCYXNlUmVzcG9uc2UQ8AcSIAob", - "U3RhcnRBcHBsaWNhdGlvbkxvZ3NSZXF1ZXN0EPEHEiEKHFN0YXJ0QXBwbGlj", - "YXRpb25Mb2dzUmVzcG9uc2UQ8gcSHwoaU3RvcEFwcGxpY2F0aW9uTG9nc1Jl", - "cXVlc3QQ8wcSIAobU3RvcEFwcGxpY2F0aW9uTG9nc1Jlc3BvbnNlEPQHEhgK", - "E0NvbG9yUHJvZmlsZVJlcXVlc3QQ9QcSGQoUQ29sb3JQcm9maWxlUmVzcG9u", - "c2UQ9gcSGAoTVXBkYXRlU3RhdHVzUmVxdWVzdBD3BxIZChRVcGRhdGVTdGF0", - "dXNSZXNwb25zZRD4BxITCg5HZW5lcmljUmVxdWVzdBD5BxIUCg9HZW5lcmlj", - "UmVzcG9uc2UQ+gcSHAoXU3RhcnREaWFnbm9zdGljc1JlcXVlc3QQ0A8SHQoY", - "U3RhcnREaWFnbm9zdGljc1Jlc3BvbnNlENEPEhwKF01vdG9yQWJvcnRIb21p", - "bmdSZXF1ZXN0ENIPEh0KGE1vdG9yQWJvcnRIb21pbmdSZXNwb25zZRDTDxIX", - "ChJNb3RvckhvbWluZ1JlcXVlc3QQ1A8SGAoTTW90b3JIb21pbmdSZXNwb25z", - "ZRDVDxIYChNNb3RvckpvZ2dpbmdSZXF1ZXN0ENYPEhkKFE1vdG9ySm9nZ2lu", - "Z1Jlc3BvbnNlENcPEh0KGE1vdG9yQWJvcnRKb2dnaW5nUmVxdWVzdBDYDxIe", - "ChlNb3RvckFib3J0Sm9nZ2luZ1Jlc3BvbnNlENkPEiAKG0Rpc3BlbnNlckFi", - "b3J0SG9taW5nUmVxdWVzdBDaDxIhChxEaXNwZW5zZXJBYm9ydEhvbWluZ1Jl", - "c3BvbnNlENsPEhsKFkRpc3BlbnNlckhvbWluZ1JlcXVlc3QQ3A8SHAoXRGlz", - "cGVuc2VySG9taW5nUmVzcG9uc2UQ3Q8SHAoXRGlzcGVuc2VySm9nZ2luZ1Jl", - "cXVlc3QQ3g8SHQoYRGlzcGVuc2VySm9nZ2luZ1Jlc3BvbnNlEN8PEiEKHERp", - "c3BlbnNlckFib3J0Sm9nZ2luZ1JlcXVlc3QQ4A8SIgodRGlzcGVuc2VyQWJv", - "cnRKb2dnaW5nUmVzcG9uc2UQ4Q8SGQoUU2V0RGlnaXRhbE91dFJlcXVlc3QQ", - "4g8SGgoVU2V0RGlnaXRhbE91dFJlc3BvbnNlEOMPEhkKFFRocmVhZEpvZ2dp", - "bmdSZXF1ZXN0EOQPEhoKFVRocmVhZEpvZ2dpbmdSZXNwb25zZRDlDxIeChlU", - "aHJlYWRBYm9ydEpvZ2dpbmdSZXF1ZXN0EOYPEh8KGlRocmVhZEFib3J0Sm9n", - "Z2luZ1Jlc3BvbnNlEOcPEh0KGFNldENvbXBvbmVudFZhbHVlUmVxdWVzdBDo", - "DxIeChlTZXRDb21wb25lbnRWYWx1ZVJlc3BvbnNlEOkPEhgKE1Jlc29sdmVF", - "dmVudFJlcXVlc3QQ6g8SGQoUUmVzb2x2ZUV2ZW50UmVzcG9uc2UQ6w8SGwoW", - "U3RvcERpYWdub3N0aWNzUmVxdWVzdBDsDxIcChdTdG9wRGlhZ25vc3RpY3NS", - "ZXNwb25zZRDtDxIjCh5TdGFydEV2ZW50c05vdGlmaWNhdGlvblJlcXVlc3QQ", - "7g8SJAofU3RhcnRFdmVudHNOb3RpZmljYXRpb25SZXNwb25zZRDvDxIiCh1T", - "dG9wRXZlbnRzTm90aWZpY2F0aW9uUmVxdWVzdBDwDxIjCh5TdG9wRXZlbnRz", - "Tm90aWZpY2F0aW9uUmVzcG9uc2UQ8Q8SGgoVU2V0SGVhdGVyU3RhdGVSZXF1", - "ZXN0EPIPEhsKFlNldEhlYXRlclN0YXRlUmVzcG9uc2UQ8w8SGgoVU2V0Qmxv", - "d2VyU3RhdGVSZXF1ZXN0EPQPEhsKFlNldEJsb3dlclN0YXRlUmVzcG9uc2UQ", - "9Q8SGQoUU2V0VmFsdmVTdGF0ZVJlcXVlc3QQ9g8SGgoVU2V0VmFsdmVTdGF0", - "ZVJlc3BvbnNlEPcPEiEKHFN0YXJ0Q2FydHJpZGdlc1VwZGF0ZVJlcXVlc3QQ", - "+A8SIgodU3RhcnRDYXJ0cmlkZ2VzVXBkYXRlUmVzcG9uc2UQ+Q8SIAobU3Rv", - "cENhcnRyaWRnZXNVcGRhdGVSZXF1ZXN0EPoPEiEKHFN0b3BDYXJ0cmlkZ2Vz", - "VXBkYXRlUmVzcG9uc2UQ+w8SHwoaQ2FydHJpZGdlVmFsaWRhdGlvblJlcXVl", - "c3QQ/A8SIAobQ2FydHJpZGdlVmFsaWRhdGlvblJlc3BvbnNlEP0PEhsKFkRp", - "c3BlbnNlckVFcHJvbVJlcXVlc3QQ/g8SHAoXRGlzcGVuc2VyRUVwcm9tUmVz", - "cG9uc2UQ/w8SDwoKSm9iUmVxdWVzdBC4FxIQCgtKb2JSZXNwb25zZRC5FxIU", - "Cg9BYm9ydEpvYlJlcXVlc3QQuhcSFQoQQWJvcnRKb2JSZXNwb25zZRC7FxIj", - "Ch5VcGxvYWRQcm9jZXNzUGFyYW1ldGVyc1JlcXVlc3QQvBcSJAofVXBsb2Fk", - "UHJvY2Vzc1BhcmFtZXRlcnNSZXNwb25zZRC9FxIWChFDdXJyZW50Sm9iUmVx", - "dWVzdBC+FxIXChJDdXJyZW50Sm9iUmVzcG9uc2UQvxcSHAoXUmVzdW1lQ3Vy", - "cmVudEpvYlJlcXVlc3QQwBcSHQoYUmVzdW1lQ3VycmVudEpvYlJlc3BvbnNl", - "EMEXEh0KGFN0YXJ0SGVhZENsZWFuaW5nUmVxdWVzdBDCFxIeChlTdGFydEhl", - "YWRDbGVhbmluZ1Jlc3BvbnNlEMMXEh0KGEFib3J0SGVhZENsZWFuaW5nUmVx", - "dWVzdBDEFxIeChlBYm9ydEhlYWRDbGVhbmluZ1Jlc3BvbnNlEMUXEhkKFFN0", - "YXJ0RGVidWdMb2dSZXF1ZXN0EKAfEhoKFVN0YXJ0RGVidWdMb2dSZXNwb25z", - "ZRChHxIYChNTdG9wRGVidWdMb2dSZXF1ZXN0EKIfEhkKFFN0b3BEZWJ1Z0xv", - "Z1Jlc3BvbnNlEKMfEh8KGlNldERlYnVnTG9nQ2F0ZWdvcnlSZXF1ZXN0EKQf", - "EiAKG1NldERlYnVnTG9nQ2F0ZWdvcnlSZXNwb25zZRClHxIhChxTZXR1cERl", - "YnVnRGlzcmlidXRvcnNSZXF1ZXN0EKYfEiIKHVNldHVwRGVidWdEaXNyaWJ1", - "dG9yc1Jlc3BvbnNlEKcfEicKIlVwbG9hZEhhcmR3YXJlQ29uZmlndXJhdGlv", - "blJlcXVlc3QQiCcSKAojVXBsb2FkSGFyZHdhcmVDb25maWd1cmF0aW9uUmVz", - "cG9uc2UQiScSFwoSU3lzdGVtUmVzZXRSZXF1ZXN0EIonEhgKE1N5c3RlbVJl", - "c2V0UmVzcG9uc2UQiycSFQoQS2VlcEFsaXZlUmVxdWVzdBDwLhIWChFLZWVw", - "QWxpdmVSZXNwb25zZRDxLhITCg5Db25uZWN0UmVxdWVzdBDyLhIUCg9Db25u", - "ZWN0UmVzcG9uc2UQ8y4SFgoRRGlzY29ubmVjdFJlcXVlc3QQ9C4SFwoSRGlz", - "Y29ubmVjdFJlc3BvbnNlEPUuEhYKEUZpbGVVcGxvYWRSZXF1ZXN0ENg2EhcK", - "EkZpbGVVcGxvYWRSZXNwb25zZRDZNhIbChZGaWxlQ2h1bmtVcGxvYWRSZXF1", - "ZXN0ENo2EhwKF0ZpbGVDaHVua1VwbG9hZFJlc3BvbnNlENs2EhoKFUV4ZWN1", - "dGVQcm9jZXNzUmVxdWVzdBDcNhIbChZFeGVjdXRlUHJvY2Vzc1Jlc3BvbnNl", - "EN02EhcKEktpbGxQcm9jZXNzUmVxdWVzdBDeNhIYChNLaWxsUHJvY2Vzc1Jl", - "c3BvbnNlEN82EhIKDUNyZWF0ZVJlcXVlc3QQ4DYSEwoOQ3JlYXRlUmVzcG9u", - "c2UQ4TYSEgoNRGVsZXRlUmVxdWVzdBDiNhITCg5EZWxldGVSZXNwb25zZRDj", - "NhIaChVHZXRTdG9yYWdlSW5mb1JlcXVlc3QQ5DYSGwoWR2V0U3RvcmFnZUlu", - "Zm9SZXNwb25zZRDlNhIUCg9HZXRGaWxlc1JlcXVlc3QQ5jYSFQoQR2V0Rmls", - "ZXNSZXNwb25zZRDnNhIYChNGaWxlRG93bmxvYWRSZXF1ZXN0EOg2EhkKFEZp", - "bGVEb3dubG9hZFJlc3BvbnNlEOk2Eh0KGEZpbGVDaHVua0Rvd25sb2FkUmVx", - "dWVzdBDqNhIeChlGaWxlQ2h1bmtEb3dubG9hZFJlc3BvbnNlEOs2EhsKFlZh", - "bGlkYXRlVmVyc2lvblJlcXVlc3QQ7DYSHAoXVmFsaWRhdGVWZXJzaW9uUmVz", - "cG9uc2UQ7TYSGwoWQWN0aXZhdGVWZXJzaW9uUmVxdWVzdBDuNhIcChdBY3Rp", - "dmF0ZVZlcnNpb25SZXNwb25zZRDvNhIZChREaXNwZW5zZXJEYXRhUmVxdWVz", - "dBDAPhIaChVEaXNwZW5zZXJEYXRhUmVzcG9uc2UQwT4SHAoXTWlkVGFua0Rh", - "dGFTZXR1cFJlcXVlc3QQwj4SHQoYTWlkVGFua0RhdGFTZXR1cFJlc3BvbnNl", - "EMM+EiIKHU1hY2hpbmVDYWxpYnJhdGlvbkRhdGFSZXF1ZXN0EMQ+EiMKHk1h", - "Y2hpbmVDYWxpYnJhdGlvbkRhdGFSZXNwb25zZRDFPhIkCh9TdGFydE1hY2hp", - "bmVTdGF0dXNVcGRhdGVSZXF1ZXN0EKhGEiUKIFN0YXJ0TWFjaGluZVN0YXR1", - "c1VwZGF0ZVJlc3BvbnNlEKlGEiMKHlN0b3BNYWNoaW5lU3RhdHVzVXBkYXRl", - "UmVxdWVzdBCqRhIkCh9TdG9wTWFjaGluZVN0YXR1c1VwZGF0ZVJlc3BvbnNl", - "EKtGEhoKFVN0YXJ0UG93ZXJEb3duUmVxdWVzdBCQThIbChZTdGFydFBvd2Vy", - "RG93blJlc3BvbnNlEJFOEhoKFUFib3J0UG93ZXJEb3duUmVxdWVzdBCSThIb", - "ChZBYm9ydFBvd2VyRG93blJlc3BvbnNlEJNOEh4KGVN0YXJ0VGhyZWFkTG9h", - "ZGluZ1JlcXVlc3QQ+FUSHwoaU3RhcnRUaHJlYWRMb2FkaW5nUmVzcG9uc2UQ", - "+VUSIQocQ29udGludWVUaHJlYWRMb2FkaW5nUmVxdWVzdBD6VRIiCh1Db250", - "aW51ZVRocmVhZExvYWRpbmdSZXNwb25zZRD7VRIdChhTdG9wVGhyZWFkTG9h", - "ZGluZ1JlcXVlc3QQ/FUSHgoZU3RvcFRocmVhZExvYWRpbmdSZXNwb25zZRD9", - "VUIcChpjb20udHdpbmUudGFuZ28ucG1yLmNvbW1vbmIGcHJvdG8z")); + "c2UQaBIeChpTdHViRGlzcGVuc2VyRUVwcm9tUmVxdWVzdBBpEh8KG1N0dWJE", + "aXNwZW5zZXJFRXByb21SZXNwb25zZRBqEhgKFFN0dWJXSFNFRXByb21SZXF1", + "ZXN0EGsSGQoVU3R1YldIU0VFcHJvbVJlc3BvbnNlEGwSJQogRXh0ZXJuYWxC", + "cmlkZ2VVZHBEaXNjb3ZlcnlQYWNrZXQQ6AcSHwoaRXh0ZXJuYWxCcmlkZ2VM", + "b2dpblJlcXVlc3QQ6QcSIAobRXh0ZXJuYWxCcmlkZ2VMb2dpblJlc3BvbnNl", + "EOoHEiAKG0V4dGVybmFsQnJpZGdlTG9nb3V0UmVxdWVzdBDrBxIhChxFeHRl", + "cm5hbEJyaWRnZUxvZ291dFJlc3BvbnNlEOwHEiEKHERpcmVjdFN5bmNocm9u", + "aXphdGlvblJlcXVlc3QQ7QcSIgodRGlyZWN0U3luY2hyb25pemF0aW9uUmVz", + "cG9uc2UQ7gcSHAoXT3ZlcnJpZGVEYXRhQmFzZVJlcXVlc3QQ7wcSHQoYT3Zl", + "cnJpZGVEYXRhQmFzZVJlc3BvbnNlEPAHEiAKG1N0YXJ0QXBwbGljYXRpb25M", + "b2dzUmVxdWVzdBDxBxIhChxTdGFydEFwcGxpY2F0aW9uTG9nc1Jlc3BvbnNl", + "EPIHEh8KGlN0b3BBcHBsaWNhdGlvbkxvZ3NSZXF1ZXN0EPMHEiAKG1N0b3BB", + "cHBsaWNhdGlvbkxvZ3NSZXNwb25zZRD0BxIYChNDb2xvclByb2ZpbGVSZXF1", + "ZXN0EPUHEhkKFENvbG9yUHJvZmlsZVJlc3BvbnNlEPYHEhgKE1VwZGF0ZVN0", + "YXR1c1JlcXVlc3QQ9wcSGQoUVXBkYXRlU3RhdHVzUmVzcG9uc2UQ+AcSEwoO", + "R2VuZXJpY1JlcXVlc3QQ+QcSFAoPR2VuZXJpY1Jlc3BvbnNlEPoHEhwKF1N0", + "YXJ0RGlhZ25vc3RpY3NSZXF1ZXN0ENAPEh0KGFN0YXJ0RGlhZ25vc3RpY3NS", + "ZXNwb25zZRDRDxIcChdNb3RvckFib3J0SG9taW5nUmVxdWVzdBDSDxIdChhN", + "b3RvckFib3J0SG9taW5nUmVzcG9uc2UQ0w8SFwoSTW90b3JIb21pbmdSZXF1", + "ZXN0ENQPEhgKE01vdG9ySG9taW5nUmVzcG9uc2UQ1Q8SGAoTTW90b3JKb2dn", + "aW5nUmVxdWVzdBDWDxIZChRNb3RvckpvZ2dpbmdSZXNwb25zZRDXDxIdChhN", + "b3RvckFib3J0Sm9nZ2luZ1JlcXVlc3QQ2A8SHgoZTW90b3JBYm9ydEpvZ2dp", + "bmdSZXNwb25zZRDZDxIgChtEaXNwZW5zZXJBYm9ydEhvbWluZ1JlcXVlc3QQ", + "2g8SIQocRGlzcGVuc2VyQWJvcnRIb21pbmdSZXNwb25zZRDbDxIbChZEaXNw", + "ZW5zZXJIb21pbmdSZXF1ZXN0ENwPEhwKF0Rpc3BlbnNlckhvbWluZ1Jlc3Bv", + "bnNlEN0PEhwKF0Rpc3BlbnNlckpvZ2dpbmdSZXF1ZXN0EN4PEh0KGERpc3Bl", + "bnNlckpvZ2dpbmdSZXNwb25zZRDfDxIhChxEaXNwZW5zZXJBYm9ydEpvZ2dp", + "bmdSZXF1ZXN0EOAPEiIKHURpc3BlbnNlckFib3J0Sm9nZ2luZ1Jlc3BvbnNl", + "EOEPEhkKFFNldERpZ2l0YWxPdXRSZXF1ZXN0EOIPEhoKFVNldERpZ2l0YWxP", + "dXRSZXNwb25zZRDjDxIZChRUaHJlYWRKb2dnaW5nUmVxdWVzdBDkDxIaChVU", + "aHJlYWRKb2dnaW5nUmVzcG9uc2UQ5Q8SHgoZVGhyZWFkQWJvcnRKb2dnaW5n", + "UmVxdWVzdBDmDxIfChpUaHJlYWRBYm9ydEpvZ2dpbmdSZXNwb25zZRDnDxId", + "ChhTZXRDb21wb25lbnRWYWx1ZVJlcXVlc3QQ6A8SHgoZU2V0Q29tcG9uZW50", + "VmFsdWVSZXNwb25zZRDpDxIYChNSZXNvbHZlRXZlbnRSZXF1ZXN0EOoPEhkK", + "FFJlc29sdmVFdmVudFJlc3BvbnNlEOsPEhsKFlN0b3BEaWFnbm9zdGljc1Jl", + "cXVlc3QQ7A8SHAoXU3RvcERpYWdub3N0aWNzUmVzcG9uc2UQ7Q8SIwoeU3Rh", + "cnRFdmVudHNOb3RpZmljYXRpb25SZXF1ZXN0EO4PEiQKH1N0YXJ0RXZlbnRz", + "Tm90aWZpY2F0aW9uUmVzcG9uc2UQ7w8SIgodU3RvcEV2ZW50c05vdGlmaWNh", + "dGlvblJlcXVlc3QQ8A8SIwoeU3RvcEV2ZW50c05vdGlmaWNhdGlvblJlc3Bv", + "bnNlEPEPEhoKFVNldEhlYXRlclN0YXRlUmVxdWVzdBDyDxIbChZTZXRIZWF0", + "ZXJTdGF0ZVJlc3BvbnNlEPMPEhoKFVNldEJsb3dlclN0YXRlUmVxdWVzdBD0", + "DxIbChZTZXRCbG93ZXJTdGF0ZVJlc3BvbnNlEPUPEhkKFFNldFZhbHZlU3Rh", + "dGVSZXF1ZXN0EPYPEhoKFVNldFZhbHZlU3RhdGVSZXNwb25zZRD3DxIhChxT", + "dGFydENhcnRyaWRnZXNVcGRhdGVSZXF1ZXN0EPgPEiIKHVN0YXJ0Q2FydHJp", + "ZGdlc1VwZGF0ZVJlc3BvbnNlEPkPEiAKG1N0b3BDYXJ0cmlkZ2VzVXBkYXRl", + "UmVxdWVzdBD6DxIhChxTdG9wQ2FydHJpZGdlc1VwZGF0ZVJlc3BvbnNlEPsP", + "Eh8KGkNhcnRyaWRnZVZhbGlkYXRpb25SZXF1ZXN0EPwPEiAKG0NhcnRyaWRn", + "ZVZhbGlkYXRpb25SZXNwb25zZRD9DxIPCgpKb2JSZXF1ZXN0ELgXEhAKC0pv", + "YlJlc3BvbnNlELkXEhQKD0Fib3J0Sm9iUmVxdWVzdBC6FxIVChBBYm9ydEpv", + "YlJlc3BvbnNlELsXEiMKHlVwbG9hZFByb2Nlc3NQYXJhbWV0ZXJzUmVxdWVz", + "dBC8FxIkCh9VcGxvYWRQcm9jZXNzUGFyYW1ldGVyc1Jlc3BvbnNlEL0XEhYK", + "EUN1cnJlbnRKb2JSZXF1ZXN0EL4XEhcKEkN1cnJlbnRKb2JSZXNwb25zZRC/", + "FxIcChdSZXN1bWVDdXJyZW50Sm9iUmVxdWVzdBDAFxIdChhSZXN1bWVDdXJy", + "ZW50Sm9iUmVzcG9uc2UQwRcSHQoYU3RhcnRIZWFkQ2xlYW5pbmdSZXF1ZXN0", + "EMIXEh4KGVN0YXJ0SGVhZENsZWFuaW5nUmVzcG9uc2UQwxcSHQoYQWJvcnRI", + "ZWFkQ2xlYW5pbmdSZXF1ZXN0EMQXEh4KGUFib3J0SGVhZENsZWFuaW5nUmVz", + "cG9uc2UQxRcSGQoUU3RhcnREZWJ1Z0xvZ1JlcXVlc3QQoB8SGgoVU3RhcnRE", + "ZWJ1Z0xvZ1Jlc3BvbnNlEKEfEhgKE1N0b3BEZWJ1Z0xvZ1JlcXVlc3QQoh8S", + "GQoUU3RvcERlYnVnTG9nUmVzcG9uc2UQox8SHwoaU2V0RGVidWdMb2dDYXRl", + "Z29yeVJlcXVlc3QQpB8SIAobU2V0RGVidWdMb2dDYXRlZ29yeVJlc3BvbnNl", + "EKUfEiEKHFNldHVwRGVidWdEaXNyaWJ1dG9yc1JlcXVlc3QQph8SIgodU2V0", + "dXBEZWJ1Z0Rpc3JpYnV0b3JzUmVzcG9uc2UQpx8SJwoiVXBsb2FkSGFyZHdh", + "cmVDb25maWd1cmF0aW9uUmVxdWVzdBCIJxIoCiNVcGxvYWRIYXJkd2FyZUNv", + "bmZpZ3VyYXRpb25SZXNwb25zZRCJJxIXChJTeXN0ZW1SZXNldFJlcXVlc3QQ", + "iicSGAoTU3lzdGVtUmVzZXRSZXNwb25zZRCLJxIVChBLZWVwQWxpdmVSZXF1", + "ZXN0EPAuEhYKEUtlZXBBbGl2ZVJlc3BvbnNlEPEuEhMKDkNvbm5lY3RSZXF1", + "ZXN0EPIuEhQKD0Nvbm5lY3RSZXNwb25zZRDzLhIWChFEaXNjb25uZWN0UmVx", + "dWVzdBD0LhIXChJEaXNjb25uZWN0UmVzcG9uc2UQ9S4SFgoRRmlsZVVwbG9h", + "ZFJlcXVlc3QQ2DYSFwoSRmlsZVVwbG9hZFJlc3BvbnNlENk2EhsKFkZpbGVD", + "aHVua1VwbG9hZFJlcXVlc3QQ2jYSHAoXRmlsZUNodW5rVXBsb2FkUmVzcG9u", + "c2UQ2zYSGgoVRXhlY3V0ZVByb2Nlc3NSZXF1ZXN0ENw2EhsKFkV4ZWN1dGVQ", + "cm9jZXNzUmVzcG9uc2UQ3TYSFwoSS2lsbFByb2Nlc3NSZXF1ZXN0EN42EhgK", + "E0tpbGxQcm9jZXNzUmVzcG9uc2UQ3zYSEgoNQ3JlYXRlUmVxdWVzdBDgNhIT", + "Cg5DcmVhdGVSZXNwb25zZRDhNhISCg1EZWxldGVSZXF1ZXN0EOI2EhMKDkRl", + "bGV0ZVJlc3BvbnNlEOM2EhoKFUdldFN0b3JhZ2VJbmZvUmVxdWVzdBDkNhIb", + "ChZHZXRTdG9yYWdlSW5mb1Jlc3BvbnNlEOU2EhQKD0dldEZpbGVzUmVxdWVz", + "dBDmNhIVChBHZXRGaWxlc1Jlc3BvbnNlEOc2EhgKE0ZpbGVEb3dubG9hZFJl", + "cXVlc3QQ6DYSGQoURmlsZURvd25sb2FkUmVzcG9uc2UQ6TYSHQoYRmlsZUNo", + "dW5rRG93bmxvYWRSZXF1ZXN0EOo2Eh4KGUZpbGVDaHVua0Rvd25sb2FkUmVz", + "cG9uc2UQ6zYSGwoWVmFsaWRhdGVWZXJzaW9uUmVxdWVzdBDsNhIcChdWYWxp", + "ZGF0ZVZlcnNpb25SZXNwb25zZRDtNhIbChZBY3RpdmF0ZVZlcnNpb25SZXF1", + "ZXN0EO42EhwKF0FjdGl2YXRlVmVyc2lvblJlc3BvbnNlEO82EhkKFERpc3Bl", + "bnNlckRhdGFSZXF1ZXN0EMA+EhoKFURpc3BlbnNlckRhdGFSZXNwb25zZRDB", + "PhIcChdNaWRUYW5rRGF0YVNldHVwUmVxdWVzdBDCPhIdChhNaWRUYW5rRGF0", + "YVNldHVwUmVzcG9uc2UQwz4SIgodTWFjaGluZUNhbGlicmF0aW9uRGF0YVJl", + "cXVlc3QQxD4SIwoeTWFjaGluZUNhbGlicmF0aW9uRGF0YVJlc3BvbnNlEMU+", + "EiQKH1N0YXJ0TWFjaGluZVN0YXR1c1VwZGF0ZVJlcXVlc3QQqEYSJQogU3Rh", + "cnRNYWNoaW5lU3RhdHVzVXBkYXRlUmVzcG9uc2UQqUYSIwoeU3RvcE1hY2hp", + "bmVTdGF0dXNVcGRhdGVSZXF1ZXN0EKpGEiQKH1N0b3BNYWNoaW5lU3RhdHVz", + "VXBkYXRlUmVzcG9uc2UQq0YSGgoVU3RhcnRQb3dlckRvd25SZXF1ZXN0EJBO", + "EhsKFlN0YXJ0UG93ZXJEb3duUmVzcG9uc2UQkU4SGgoVQWJvcnRQb3dlckRv", + "d25SZXF1ZXN0EJJOEhsKFkFib3J0UG93ZXJEb3duUmVzcG9uc2UQk04SHgoZ", + "U3RhcnRUaHJlYWRMb2FkaW5nUmVxdWVzdBD4VRIfChpTdGFydFRocmVhZExv", + "YWRpbmdSZXNwb25zZRD5VRIhChxDb250aW51ZVRocmVhZExvYWRpbmdSZXF1", + "ZXN0EPpVEiIKHUNvbnRpbnVlVGhyZWFkTG9hZGluZ1Jlc3BvbnNlEPtVEh0K", + "GFN0b3BUaHJlYWRMb2FkaW5nUmVxdWVzdBD8VRIeChlTdG9wVGhyZWFkTG9h", + "ZGluZ1Jlc3BvbnNlEP1VQhwKGmNvbS50d2luZS50YW5nby5wbXIuY29tbW9u", + "YgZwcm90bzM=")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Tango.PMR.Common.MessageType), }, null)); @@ -306,10 +308,14 @@ namespace Tango.PMR.Common { [pbr::OriginalName("StubAbortJobRequest")] StubAbortJobRequest = 101, [pbr::OriginalName("StubAbortJobResponse")] StubAbortJobResponse = 102, [pbr::OriginalName("StubMidTankPressureSensorRequest")] StubMidTankPressureSensorRequest = 103, + [pbr::OriginalName("StubMidTankPressureSensorResponse")] StubMidTankPressureSensorResponse = 104, + [pbr::OriginalName("StubDispenserEEpromRequest")] StubDispenserEepromRequest = 105, + [pbr::OriginalName("StubDispenserEEpromResponse")] StubDispenserEepromResponse = 106, + [pbr::OriginalName("StubWHSEEpromRequest")] StubWhseepromRequest = 107, /// ///------------------------------------ /// - [pbr::OriginalName("StubMidTankPressureSensorResponse")] StubMidTankPressureSensorResponse = 104, + [pbr::OriginalName("StubWHSEEpromResponse")] StubWhseepromResponse = 108, /// ///Integration /// @@ -381,8 +387,6 @@ namespace Tango.PMR.Common { [pbr::OriginalName("StopCartridgesUpdateResponse")] StopCartridgesUpdateResponse = 2043, [pbr::OriginalName("CartridgeValidationRequest")] CartridgeValidationRequest = 2044, [pbr::OriginalName("CartridgeValidationResponse")] CartridgeValidationResponse = 2045, - [pbr::OriginalName("DispenserEEpromRequest")] DispenserEepromRequest = 2046, - [pbr::OriginalName("DispenserEEpromResponse")] DispenserEepromResponse = 2047, /// ///Printing /// diff --git a/Software/Visual_Studio/Tango.PMR/Integration/ExternalBridgeLoginRequest.cs b/Software/Visual_Studio/Tango.PMR/Integration/ExternalBridgeLoginRequest.cs index 85b18e9b2..945cb27ca 100644 --- a/Software/Visual_Studio/Tango.PMR/Integration/ExternalBridgeLoginRequest.cs +++ b/Software/Visual_Studio/Tango.PMR/Integration/ExternalBridgeLoginRequest.cs @@ -24,15 +24,16 @@ namespace Tango.PMR.Integration { string.Concat( "CiBFeHRlcm5hbEJyaWRnZUxvZ2luUmVxdWVzdC5wcm90bxIVVGFuZ28uUE1S", "LkludGVncmF0aW9uGh9FeHRlcm5hbEJyaWRnZUxvZ2luSW50ZW50LnByb3Rv", - "IqMBChpFeHRlcm5hbEJyaWRnZUxvZ2luUmVxdWVzdBINCgVBcHBJRBgBIAEo", + "ItsBChpFeHRlcm5hbEJyaWRnZUxvZ2luUmVxdWVzdBINCgVBcHBJRBgBIAEo", "CRIQCghVc2VyR3VpZBgCIAEoCRIQCghIb3N0TmFtZRgDIAEoCRIQCghQYXNz", "d29yZBgEIAEoCRJACgZJbnRlbnQYBSABKA4yMC5UYW5nby5QTVIuSW50ZWdy", - "YXRpb24uRXh0ZXJuYWxCcmlkZ2VMb2dpbkludGVudEIhCh9jb20udHdpbmUu", - "dGFuZ28ucG1yLmludGVncmF0aW9uYgZwcm90bzM=")); + "YXRpb24uRXh0ZXJuYWxCcmlkZ2VMb2dpbkludGVudBIkChxSZXF1aXJlU2Fm", + "ZXR5TGV2ZWxPcGVyYXRpb25zGAYgASgIEhAKCFVzZXJOYW1lGAcgASgJQiEK", + "H2NvbS50d2luZS50YW5nby5wbXIuaW50ZWdyYXRpb25iBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Tango.PMR.Integration.ExternalBridgeLoginIntentReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Integration.ExternalBridgeLoginRequest), global::Tango.PMR.Integration.ExternalBridgeLoginRequest.Parser, new[]{ "AppID", "UserGuid", "HostName", "Password", "Intent" }, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Integration.ExternalBridgeLoginRequest), global::Tango.PMR.Integration.ExternalBridgeLoginRequest.Parser, new[]{ "AppID", "UserGuid", "HostName", "Password", "Intent", "RequireSafetyLevelOperations", "UserName" }, null, null, null) })); } #endregion @@ -68,6 +69,8 @@ namespace Tango.PMR.Integration { hostName_ = other.hostName_; password_ = other.password_; intent_ = other.intent_; + requireSafetyLevelOperations_ = other.requireSafetyLevelOperations_; + userName_ = other.userName_; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -130,6 +133,28 @@ namespace Tango.PMR.Integration { } } + /// Field number for the "RequireSafetyLevelOperations" field. + public const int RequireSafetyLevelOperationsFieldNumber = 6; + private bool requireSafetyLevelOperations_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool RequireSafetyLevelOperations { + get { return requireSafetyLevelOperations_; } + set { + requireSafetyLevelOperations_ = value; + } + } + + /// Field number for the "UserName" field. + public const int UserNameFieldNumber = 7; + private string userName_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string UserName { + get { return userName_; } + set { + userName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override bool Equals(object other) { return Equals(other as ExternalBridgeLoginRequest); @@ -148,6 +173,8 @@ namespace Tango.PMR.Integration { if (HostName != other.HostName) return false; if (Password != other.Password) return false; if (Intent != other.Intent) return false; + if (RequireSafetyLevelOperations != other.RequireSafetyLevelOperations) return false; + if (UserName != other.UserName) return false; return true; } @@ -159,6 +186,8 @@ namespace Tango.PMR.Integration { if (HostName.Length != 0) hash ^= HostName.GetHashCode(); if (Password.Length != 0) hash ^= Password.GetHashCode(); if (Intent != 0) hash ^= Intent.GetHashCode(); + if (RequireSafetyLevelOperations != false) hash ^= RequireSafetyLevelOperations.GetHashCode(); + if (UserName.Length != 0) hash ^= UserName.GetHashCode(); return hash; } @@ -189,6 +218,14 @@ namespace Tango.PMR.Integration { output.WriteRawTag(40); output.WriteEnum((int) Intent); } + if (RequireSafetyLevelOperations != false) { + output.WriteRawTag(48); + output.WriteBool(RequireSafetyLevelOperations); + } + if (UserName.Length != 0) { + output.WriteRawTag(58); + output.WriteString(UserName); + } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -209,6 +246,12 @@ namespace Tango.PMR.Integration { if (Intent != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Intent); } + if (RequireSafetyLevelOperations != false) { + size += 1 + 1; + } + if (UserName.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(UserName); + } return size; } @@ -232,6 +275,12 @@ namespace Tango.PMR.Integration { if (other.Intent != 0) { Intent = other.Intent; } + if (other.RequireSafetyLevelOperations != false) { + RequireSafetyLevelOperations = other.RequireSafetyLevelOperations; + } + if (other.UserName.Length != 0) { + UserName = other.UserName; + } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -262,6 +311,14 @@ namespace Tango.PMR.Integration { intent_ = (global::Tango.PMR.Integration.ExternalBridgeLoginIntent) input.ReadEnum(); break; } + case 48: { + RequireSafetyLevelOperations = input.ReadBool(); + break; + } + case 58: { + UserName = input.ReadString(); + break; + } } } } diff --git a/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj b/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj index bc37502ec..c9d87f7c1 100644 --- a/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj +++ b/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj @@ -304,7 +304,110 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -334,7 +437,7 @@ - + \ No newline at end of file -- cgit v1.3.1