Skip to content

Commit

Permalink
Improvements to Altitude Angel integration (ArduPilot#3147)
Browse files Browse the repository at this point in the history
* Fixing integration and login

* Switched constants to config

* Add scopes to user display on settings

* Minor package updates

* Including home location in plan and VTOL capability

* Remove snk

* Adding user agent with version to requests

* User interface improvements

* User interface improvements

* Disable script errors

* Clean up

* External browser login

* Exception handler in cancel

* Features dependent on token scopes actually present

* First start experience check and tweak

* Adding map altitude filtering

* Info panel link in external browser and basic UTM zone

* Tidy

* Adding initial contact number support

* Delay sign in till main window open

* Fix gitignore

* Better panel layout

* Tweak

* Message display improvements

* Don't steal window focus

* Improve info panel with dragging and curl click on plan map

* Ctrl+Click on planning map for info

* About and enable/disable improvements

* Adding Polly retries to some missing places

* Cleanup of unused types

* Rate cards now in map info panel

* Retry signin

* Showing excluded map data

* Fix first start

* One sign in at a time

* Fix readable list

* Support info in About

* Fix token expiry retry, policy retries and write back settings when set

* Fixing client override and enable

* Clickable messages

* Better exception display in wait panel

* Layout improvements

* Correcting rate calc and zone display

* Map loaded messages shorter

* Better message display

* In case JWT isn't valid

* Better sign in handling that does not get in the way when signed out, including better messages

* Removing loading message

* Use Flurl instead of HttpClient

* Settings layout tweak

* Policy tweak

* Telemetry and flight improvements

* Moving state out of main mission planner adapter. New settings.

* New and more consistent messaging. Renaming classes.

* New keyed service locator and better http policy wrapping

* Split out API and flight clients

* Split out auth client and cancellation tokens in flight

* More moving around of client code. Better exception formatting. More precision on altitude.

* Submitting flight plans via flight approvals

* Minor

* Minor message change

* Reorganization of types to simplify project. Adding surveillance client for use later.

* Fixing potential cancel bug

* Fix ctrl+click flight planning map bug

* Fixing dispose (thanks @casrya!)

* Adding missing cancellation tokens

---------

Co-authored-by: rupertbenbrook <[email protected]>
  • Loading branch information
rupertbenbrook and rupertbenbrook-aa authored Aug 12, 2023
1 parent 142a91a commit 9aae69f
Show file tree
Hide file tree
Showing 229 changed files with 5,410 additions and 3,163 deletions.
446 changes: 237 additions & 209 deletions ExtLibs/AltitudeAngelWings.Plugin/AASettings.Designer.cs

Large diffs are not rendered by default.

222 changes: 133 additions & 89 deletions ExtLibs/AltitudeAngelWings.Plugin/AASettings.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Expand Down Expand Up @@ -117,5 +117,4 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</root>
64 changes: 44 additions & 20 deletions ExtLibs/AltitudeAngelWings.Plugin/AltitudeAngelPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using AltitudeAngelWings.Plugin.Properties;
using AltitudeAngelWings.Service;
using MissionPlanner;

namespace AltitudeAngelWings.Plugin
{
Expand All @@ -17,7 +20,7 @@ public class AltitudeAngelPlugin : MissionPlanner.Plugin.Plugin

private bool _enabled;

static internal AltitudeAngelPlugin Instance;
internal static AltitudeAngelPlugin Instance;

public override bool Init()
{
Expand All @@ -32,20 +35,29 @@ public override bool Init()

public override bool Loaded()
{
ServiceLocator.Clear();
if (Host.config.ContainsKey("AA_CheckEnableAltitudeAngel"))
{
_enabled = Host.config.GetBoolean("AA_CheckEnableAltitudeAngel");
}
else
try
{
AskToEnableAltitudeAngel();
ServiceLocator.Clear();
if (Host.config.ContainsKey("AA_CheckEnableAltitudeAngel"))
{
_enabled = Host.config.GetBoolean("AA_CheckEnableAltitudeAngel");
}
else
{
AskToEnableAltitudeAngel();
}
if (_enabled)
{
EnableAltitudeAngel();
}
return true;
}
if (_enabled)
catch (Exception e)
{
EnableAltitudeAngel();
Console.WriteLine(e);
}
return true;

return false;
}

public override bool Exit()
Expand All @@ -65,18 +77,26 @@ private void AskToEnableAltitudeAngel(bool explicitClick = false)
text,
Resources.AskToEnableCaption,
CustomMessageBox.MessageBoxButtons.YesNo) == CustomMessageBox.DialogResult.Yes;
Host.config["AA_CheckEnableAltitudeAngel"] = (_enabled).ToString();
Host.config["AA_CheckEnableAltitudeAngel"] = _enabled.ToString();
Host.config.Save();
}

private void EnableAltitudeAngel()
{
ServiceLocator.Clear();
ConfigureServiceLocator();
var service = ServiceLocator.GetService<IAltitudeAngelService>();
if (!service.IsSignedIn)
Task.Run(() =>
{
service.SignInAsync();
}
Host.MainForm.Invoke(new Action(() =>
{
// Wait for splash screen to be closed before signing in
Program.Splash.Closed += (sender, args) =>
{
service.SignInAsync();
};
}));
});
}

private ToolStripMenuItem CreateSettingsMenuItem()
Expand All @@ -86,7 +106,8 @@ private ToolStripMenuItem CreateSettingsMenuItem()
Name = SettingsMenuItemName,
Text = Resources.SettingsMenuItemText,
Enabled = true,
Visible = true
Visible = true,
Image = Resources.AAIconBlack.ToBitmap()
};
menuItem.Click += OnSettingsClick;
return menuItem;
Expand All @@ -97,17 +118,20 @@ private void OnSettingsClick(object sender, EventArgs e)
if (!_enabled)
{
AskToEnableAltitudeAngel(true);
if (_enabled)
{
EnableAltitudeAngel();
}
}
if (!_enabled) return;
EnableAltitudeAngel();
new AASettings().Show(Host.MainForm);
AASettings.Instance.Show(Host.MainForm);
}

private void ConfigureServiceLocator()
{
ServiceLocator.Clear();
ServiceLocator.Register(l => Host);
ServiceLocator.Configure();
ServiceLocator.ConfigureFromAssembly(Assembly.GetAssembly(typeof(AltitudeAngelPlugin)));
ServiceLocator.ConfigureFromAssembly(Assembly.GetAssembly(typeof(IAltitudeAngelService)));
}
}
}
15 changes: 12 additions & 3 deletions ExtLibs/AltitudeAngelWings.Plugin/AltitudeAngelWings.Plugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@
<NoWarn>1701;1702;NU1605</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotSpatial.Topology" Version="1.9.0" />
<PackageReference Include="MP.DotSpatial.Positioning" Version="4.0.0" />
<PackageReference Include="MP.DotSpatial.Projections" Version="4.0.0" />
<COMReference Include="SHDocVw">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>1</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>eab22ac0-30c1-11cf-a7eb-0000c05bae0b</Guid>
<Lcid>0</Lcid>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MissionPlanner.csproj">
Expand All @@ -44,6 +50,9 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Update="WaitPanel.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
Expand Down
125 changes: 125 additions & 0 deletions ExtLibs/AltitudeAngelWings.Plugin/ControlOverlayMessageDisplay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using AltitudeAngelWings.Model;
using AltitudeAngelWings.Service.Messaging;
using Message = AltitudeAngelWings.Model.Message;

namespace AltitudeAngelWings.Plugin
{
public class ControlOverlayMessageDisplay : IMessageDisplay
{
private const int LeftOffset = 5;

private readonly Control _parent;
private readonly IUiThreadInvoke _uiThreadInvoke;
private readonly int _bottomOffset;

public ControlOverlayMessageDisplay(Control parent, IUiThreadInvoke uiThreadInvoke, int bottomOffset)
{
_parent = parent;
_uiThreadInvoke = uiThreadInvoke;
_bottomOffset = bottomOffset;
}

public void AddMessage(Message message)
{
var label = CreateLabel(message);
_uiThreadInvoke.Invoke(() =>
{
if (!_parent.Visible) return;
_parent.SuspendLayout();
var labels = GetMessageLabels();
if (!string.IsNullOrEmpty(message.Key))
{
var matchingKeys = labels.Where(l => l.Name == message.Key).ToList();
foreach (var remove in matchingKeys)
{
_parent.Controls.Remove(remove);
labels.Remove(remove);
remove.Dispose();
}
}
_parent.Controls.Add(label);
label.BringToFront();
labels.Add(label);
LayoutLabels(labels);
_parent.ResumeLayout();
});
}

public void RemoveMessage(Message message)
{
_uiThreadInvoke.Invoke(() =>
{
if (!_parent.Visible) return;
_parent.SuspendLayout();
var labels = GetMessageLabels();
foreach (var label in labels)
{
if (label.Tag != message) continue;
_parent.Controls.Remove(label);
labels.Remove(label);
label.Dispose();
break;
}
LayoutLabels(labels);
_parent.ResumeLayout();
});
}

private Label CreateLabel(Message message)
{
var label = new Label
{
Tag = message,
Text = message.Content,
Name = message.Key ?? "",
AutoSize = true,
ForeColor = GetColorForMessage(message),
BackColor = Color.Transparent,
Visible = true,
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
TextAlign = ContentAlignment.MiddleLeft,
Padding = new Padding(5, 0, 5, 1),
Font = new Font("Microsoft Sans Serif", 9, FontStyle.Regular)
};
if (message.OnClick == null)
{
return label;
}

label.Cursor = Cursors.Hand;
label.Click += (sender, e) =>
{
message.OnClick();
RemoveMessage(message);
};
return label;
}

private static Color GetColorForMessage(Message message)
{
switch (message.Type)
{
case MessageType.Error:
return Color.Red;
default:
return message.OnClick == null ? Color.White : Color.LawnGreen;
}
}

private void LayoutLabels(ICollection<Label> labels)
{
var totalHeight = 0;
foreach (var label in labels)
{
totalHeight += label.Height;
label.Location = new Point(LeftOffset, _parent.Height - totalHeight - _bottomOffset);
}
}

private IList<Label> GetMessageLabels() => _parent.Controls.OfType<Label>().Where(l => l.Tag is Message).ToList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Threading.Tasks;
using AltitudeAngelWings.Clients;
using AltitudeAngelWings.Clients.Auth;
using AltitudeAngelWings.Clients.Auth.Model;
using AltitudeAngelWings.Model;
using AltitudeAngelWings.Service.Messaging;
using MissionPlanner.Plugin;

namespace AltitudeAngelWings.Plugin
{
public class ExternalWebBrowserAuthorizeCodeProvider : IAuthorizeCodeProvider
{
private readonly ISettings _settings;
private readonly IAuthClient _authClient;
private readonly IMessagesService _messages;
private readonly PluginHost _host;
private readonly IUiThreadInvoke _uiThreadInvoke;
private string _pollId;

public ExternalWebBrowserAuthorizeCodeProvider(ISettings settings, IAuthClient authClient, IMessagesService messages, PluginHost host, IUiThreadInvoke uiThreadInvoke)
{
_settings = settings;
_authClient = authClient;
_messages = messages;
_host = host;
_uiThreadInvoke = uiThreadInvoke;
}

public void GetAuthorizeParameters(NameValueCollection parameters)
{
// Generate a random poll id
_pollId = Guid.NewGuid().ToString("N");

// Add poll id to the parameters
parameters.Add("poll_id", _pollId);
}

public async Task<string> GetAuthorizeCode(Uri authorizeUri)
{
if (string.IsNullOrWhiteSpace(_settings.ClientId) || string.IsNullOrWhiteSpace(_settings.ClientSecret))
{
await _messages.AddMessageAsync(Message.ForAction(
"BadClientCredentials",
"Client ID and Client Secret are not set correctly. Click here to open settings.",
() => AASettings.Instance.Show(_host.MainForm),
() => _settings.TokenResponse.IsValidForAuth()));
return null;
}

return await _uiThreadInvoke.Invoke(() => UiTask.ShowDialog(async cancellationToken =>
{
var tokens = await _authClient.GetTokenFromClientCredentials(cancellationToken);
Process.Start(authorizeUri.ToString());

string code;
do
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
code = await _authClient.GetAuthorizationCode(tokens.AccessToken, _pollId, cancellationToken);
} while (!cancellationToken.IsCancellationRequested && code == null);

return code;
},
"Opening a browser to sign in to Altitude Angel. Please sign in using the browser."));
}
}
}
Loading

0 comments on commit 9aae69f

Please sign in to comment.