原始导入

This commit is contained in:
pxzleo 2017-02-27 02:06:48 +08:00
commit 3c52409203
93 changed files with 11266 additions and 0 deletions

View File

@ -0,0 +1,74 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plane.FormationCreator", "Plane.FormationCreator\Plane.FormationCreator.csproj", "{61E2F31E-220A-4E3F-A64D-F7CDC2135008}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plane.Windows", "..\Plane.Libraries\Plane.Windows\Plane.Windows.csproj", "{06848293-9B17-4068-9B35-44D0ED713CD4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plane.Reflection", "..\Plane.Libraries\Plane.Reflection\Plane.Reflection.csproj", "{98755514-C2E9-4ABE-8A25-007804577558}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plane.Windows.Messages", "..\Plane.Libraries\Plane.Windows.Messages\Plane.Windows.Messages.csproj", "{413C18E2-235F-4E15-B5C1-633657DE5D7A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plane.Logging", "..\Plane.Libraries\Plane.Logging\Plane.Logging.csproj", "{9C2CAFDA-CF1D-4565-B797-398376FCD346}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plane", "..\Plane.Libraries\Plane\Plane.csproj", "{6CCE2AEB-3B38-4C00-B32D-433A990AE2AD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlaneGcsSdk_Private_NET46", "..\Plane.Sdk3\PlaneGcsSdk_Private_NET46\PlaneGcsSdk_Private_NET46.csproj", "{0111EB6E-72E3-499C-A3BA-022F5BBC4CAF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlaneGcsSdk.Contract_Private", "..\Plane.Sdk3\PlaneGcsSdk.Contract_Private\PlaneGcsSdk.Contract_Private.csproj", "{47141894-ECE3-48CA-8DCF-CA751BDA231E}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "PlaneGcsSdk_Shared", "..\Plane.Sdk3\PlaneGcsSdk_Shared\PlaneGcsSdk_Shared.shproj", "{2BE393DC-21A4-48B3-83FD-F21CBE8B038B}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "PlaneGcsSdk.Contract_Shared", "..\Plane.Sdk3\PlaneGcsSdk.Contract_Shared\PlaneGcsSdk.Contract_Shared.shproj", "{695733D7-99FF-4707-8C89-474E949CADCB}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\Plane.Sdk3\PlaneGcsSdk_Shared\PlaneGcsSdk_Shared.projitems*{0111eb6e-72e3-499c-a3ba-022f5bbc4caf}*SharedItemsImports = 4
..\Plane.Sdk3\PlaneGcsSdk_Shared\PlaneGcsSdk_Shared.projitems*{2be393dc-21a4-48b3-83fd-f21cbe8b038b}*SharedItemsImports = 13
..\Plane.Sdk3\PlaneGcsSdk.Contract_Shared\PlaneGcsSdk.Contract_Shared.projitems*{47141894-ece3-48ca-8dcf-ca751bda231e}*SharedItemsImports = 4
..\Plane.Sdk3\PlaneGcsSdk.Contract_Shared\PlaneGcsSdk.Contract_Shared.projitems*{695733d7-99ff-4707-8c89-474e949cadcb}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{61E2F31E-220A-4E3F-A64D-F7CDC2135008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61E2F31E-220A-4E3F-A64D-F7CDC2135008}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61E2F31E-220A-4E3F-A64D-F7CDC2135008}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61E2F31E-220A-4E3F-A64D-F7CDC2135008}.Release|Any CPU.Build.0 = Release|Any CPU
{06848293-9B17-4068-9B35-44D0ED713CD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06848293-9B17-4068-9B35-44D0ED713CD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06848293-9B17-4068-9B35-44D0ED713CD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06848293-9B17-4068-9B35-44D0ED713CD4}.Release|Any CPU.Build.0 = Release|Any CPU
{98755514-C2E9-4ABE-8A25-007804577558}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{98755514-C2E9-4ABE-8A25-007804577558}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98755514-C2E9-4ABE-8A25-007804577558}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98755514-C2E9-4ABE-8A25-007804577558}.Release|Any CPU.Build.0 = Release|Any CPU
{413C18E2-235F-4E15-B5C1-633657DE5D7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{413C18E2-235F-4E15-B5C1-633657DE5D7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{413C18E2-235F-4E15-B5C1-633657DE5D7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{413C18E2-235F-4E15-B5C1-633657DE5D7A}.Release|Any CPU.Build.0 = Release|Any CPU
{9C2CAFDA-CF1D-4565-B797-398376FCD346}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C2CAFDA-CF1D-4565-B797-398376FCD346}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C2CAFDA-CF1D-4565-B797-398376FCD346}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C2CAFDA-CF1D-4565-B797-398376FCD346}.Release|Any CPU.Build.0 = Release|Any CPU
{6CCE2AEB-3B38-4C00-B32D-433A990AE2AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CCE2AEB-3B38-4C00-B32D-433A990AE2AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CCE2AEB-3B38-4C00-B32D-433A990AE2AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CCE2AEB-3B38-4C00-B32D-433A990AE2AD}.Release|Any CPU.Build.0 = Release|Any CPU
{0111EB6E-72E3-499C-A3BA-022F5BBC4CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0111EB6E-72E3-499C-A3BA-022F5BBC4CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0111EB6E-72E3-499C-A3BA-022F5BBC4CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0111EB6E-72E3-499C-A3BA-022F5BBC4CAF}.Release|Any CPU.Build.0 = Release|Any CPU
{47141894-ECE3-48CA-8DCF-CA751BDA231E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47141894-ECE3-48CA-8DCF-CA751BDA231E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47141894-ECE3-48CA-8DCF-CA751BDA231E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47141894-ECE3-48CA-8DCF-CA751BDA231E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="Plane.FormationCreator.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Windows.Interactivity" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<userSettings>
<Plane.FormationCreator.Properties.Settings>
<setting name="IPs" serializeAs="String">
<value>192.168.1.50</value>
</setting>
</Plane.FormationCreator.Properties.Settings>
</userSettings>
</configuration>

View File

@ -0,0 +1,49 @@
<Application x:Class="Plane.FormationCreator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Plane.FormationCreator"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d1p1:Ignorable="d"
xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cnv="clr-namespace:Plane.Windows.Converters;assembly=Plane.Windows"
xmlns:lcnv="clr-namespace:Plane.FormationCreator.Converters"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
ShutdownMode="OnMainWindowClose">
<Application.Resources>
<materialDesign:PackIcon x:Key="CheckIcon"
x:Shared="False"
Kind="Check"
Foreground="#2196F3" />
<materialDesign:PackIcon x:Key="CloseIcon"
x:Shared="False"
Kind="Close"
Foreground="White" />
<cnv:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<cnv:BooleanToVisibilityConverter x:Key="InversiveBooleanToVisibilityConverter"
False="Visible"
True="Collapsed" />
<cnv:NullableBooleanToVisibilityConverter x:Key="IsPausedToRunButtonVisibilityConverter"
False="Collapsed"
Null="Visible"
True="Visible" />
<cnv:NullableBooleanToVisibilityConverter x:Key="IsPausedToPauseButtonVisibilityConverter"
False="Visible"
Null="Collapsed"
True="Collapsed" />
<cnv:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<cnv:BooleanToStringConverter x:Key="ShowModifyTaskViewButtonContentConverter"
False="显示任务编辑面板"
True="隐藏任务编辑面板" />
<cnv:BooleanToResourceConverter x:Key="CheckSignConverter"
FalseKey="CloseIcon"
TrueKey="CheckIcon" />
<cnv:BooleanToResourceConverter x:Key="CheckSignConverter2"
FalseKey="CloseIcon"
TrueKey="CheckIcon" />
<lcnv:HeartbeatCountToBrushConverter x:Key="HeartbeatCountToBrushConverter" />
<lcnv:AppModeToVisibilityConverter x:Key="AppModeToVisibilityConverter" />
<lcnv:FlightTaskStatusToFillConverter x:Key="FlightTaskStatusToFillConverter" />
<lcnv:FlightTaskIsSelectedToEffectConverter x:Key="FlightTaskIsSelectedToEffectConverter" />
</Application.Resources>
</Application>

View File

@ -0,0 +1,153 @@
using Plane.Communication;
using Plane.Copters;
using Plane.FormationCreator.Formation;
using Plane.Logging;
using Plane.Windows;
using Plane.Windows.Messages;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Plane.FormationCreator
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
ServiceLocatorConfigurer.Instance.Configure();
_logger = ServiceLocator.Current.GetInstance<ILogger>();
_copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
_formationController = ServiceLocator.Current.GetInstance<FormationController>();
AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
{
var simpleName = new AssemblyName(e.Name).Name;
if (simpleName.EndsWith(".resources"))
{
return null;
}
return LoadAssembly(simpleName);
};
this.DispatcherUnhandledException += async (s, e) =>
{
_logger.Log(e.Exception);
await _formationController.AllStop();
TcpServerConnectionManager.Instance.StopListening();
UdpServerConnectionManager.Instance.StopReceiving();
};
this.Exit += (s, e) =>
{
TcpServerConnectionManager.Instance.StopListening();
UdpServerConnectionManager.Instance.StopReceiving();
};
//new Test().Prepare().Run();
}
private ILogger _logger;
private CopterManager _copterManager;
private FormationController _formationController;
private Assembly LoadAssembly(string simpleName)
{
String resourceName = this.GetType().Namespace + ".AssemblyLoadingAndReflection." + simpleName + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var md = Resources.MergedDictionaries;
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/MahApps.Metro;component/Styles/VS/Colors.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/MahApps.Metro;component/Styles/VS/Styles.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Plane.Windows.Messages;component/Styles.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Styles/Colors.xaml") });
md.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Styles.xaml") });
/*
new UpdateChecker("http://dl.Plane.com/tools/ver.php?app=FormationCreator", _logger)
.CheckAsync(ver =>
{
var currentVersion = this.GetType().Assembly.GetName().Version;
if (currentVersion < ver
&& Alert.Show("检测到新版本,请下载。", "更新提示", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Process.Start("http://dl.Plane.com/tools/FormationCreatorSetup.exe");
this.Shutdown();
}
});
*/
MainWindow = new MainWindow();
MainWindow.Show();
TcpServerConnectionManager.Instance.ConnectionEstablished += ConnectionManager_ConnectionEstablished;
if (!TcpServerConnectionManager.Instance.StartListening())
{
Alert.Show("网络连接不正常,无法启动监听。");
}
UdpServerConnectionManager.Instance.ExceptionThrown += (sender, e1) =>
{
_logger.Log(e1.Exception);
};
UdpServerConnectionManager.Instance.ConnectionEstablished += ConnectionManager_ConnectionEstablished;
UdpServerConnectionManager.Instance.StartReceiving();
}
private async Task AddOrUpdateCopter(string ip, IConnection Connection)
{
var copters = _copterManager.Copters;
var copterStatus = _copterManager.CopterStatus;
var copter = copters.FirstOrDefault(c => c.Id == ip);
if (copter == null)
{
await Dispatcher.BeginInvoke(new Action(() =>
{
copter = new Copter(Connection, SynchronizationContext.Current)
{
Id = ip,
Name = ip.Substring(ip.LastIndexOf('.') + 1)
};
copters.Add(copter);
copterStatus.Add(false);
}));
}
else
{
copter.Connection = Connection;
}
await copter.ConnectAsync().ConfigureAwait(false);
}
private async void ConnectionManager_ConnectionEstablished(object sender, ConnectionEstablishedEventArgs e)
{
await AddOrUpdateCopter(e.RemoteAddress, e.Connection);
}
}
}

View File

@ -0,0 +1,105 @@
using Plane.FormationCreator.Formation;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator
{
class AppConfig
{
public AppConfig(MapManager mapMannager)
{
_mapManager = mapMannager;
_dirPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Plane",
AppDomain.CurrentDomain.FriendlyName,
"Config"
);
_filePath = Path.Combine(
_dirPath,
"Config.json"
);
string json;
try
{
json = File.ReadAllText(_filePath);
}
catch
{
json = "{}";
}
_config = JsonConvert.DeserializeObject(json);
App.Current.Exit += (sender, e) =>
{
this.Center = _mapManager.Center;
this.ZoomLevel = _mapManager.MapView.map.ZoomLevel;
this.Save();
};
}
string _dirPath;
string _filePath;
dynamic _config;
MapManager _mapManager;
public LatLng Center
{
get
{
try
{
var text = (string)_config.Center;
var fields = text.Split(',');
return new LatLng(double.Parse(fields[0]), double.Parse(fields[1]));
}
catch
{
return new LatLng(40.160491667, 116.23426389);
}
}
set
{
_config.Center = $"{value.Lat},{value.Lng}";
}
}
public double ZoomLevel
{
get
{
try
{
var text = (string)_config.ZoomLevel;
return double.Parse(text);
}
catch
{
return 19;
}
}
set
{
_config.ZoomLevel = value;
}
}
public void Save()
{
if (!Directory.Exists(_dirPath))
{
Directory.CreateDirectory(_dirPath);
}
File.WriteAllText(_filePath, JsonConvert.SerializeObject(_config));
}
}
}

View File

@ -0,0 +1,53 @@
using Plane.FormationCreator.Formation;
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator
{
public class AppEx : ObservableObject
{
public static AppEx Current { get; } = new AppEx();
private AppEx() { }
private bool _IsInFastMode = true;
public bool IsInFastMode
{
get { return _IsInFastMode; }
set { Set(nameof(IsInFastMode), ref _IsInFastMode, value); }
}
private AppMode _AppMode = AppMode.ModifyingTask;
public AppMode AppMode
{
get { return _AppMode; }
set
{
bool changed = _AppMode != value;
var oldItem = _AppMode;
Set(nameof(AppMode), ref _AppMode, value);
if (changed)
{
AppModeChanged?.Invoke(this, new PropertyChangedEventArgs<AppMode>
{
OldItem = oldItem,
NewItem = value
});
}
}
}
private bool _ShowModifyTaskView = true;
public bool ShowModifyTaskView
{
get { return _ShowModifyTaskView; }
set { Set(nameof(ShowModifyTaskView), ref _ShowModifyTaskView, value); }
}
public event EventHandler<PropertyChangedEventArgs<AppMode>> AppModeChanged;
}
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram />

View File

@ -0,0 +1,40 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.Views;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace Plane.FormationCreator.Converters
{
public class AppModeToVisibilityConverter : IValueConverter
{
static string[] _viewsForControl = { "ControlPanelView_Formation", "SwitchToPreparedForRunningTasksModeButton" };
static string[] _viewsForTasks = { nameof(ModifyTaskView), nameof(TaskBarView), "SwitchToControllingCoptersModeButton" };
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var mode = (AppMode)value;
var viewName = (string)parameter;
switch (mode)
{
case AppMode.ControllingCopters:
return _viewsForControl.Contains(viewName) ? Visibility.Visible : Visibility.Collapsed;
case AppMode.ModifyingTask:
case AppMode.PreparedForRunningTasks:
case AppMode.RunningTasks:
return _viewsForTasks.Contains(viewName) ? Visibility.Visible : Visibility.Collapsed;
default:
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,36 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.Views;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Effects;
namespace Plane.FormationCreator.Converters
{
public class FlightTaskIsSelectedToEffectConverter : IValueConverter
{
static DropShadowEffect _effect = new DropShadowEffect
{
Color = Colors.LightGray,
// Color = Colors.White,
Direction = 90,
BlurRadius = 10
};
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var selected = (bool)value;
return selected ? _effect : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,30 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.Views;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace Plane.FormationCreator.Converters
{
public class FlightTaskStatusToFillConverter : IValueConverter
{
static SolidColorBrush _normalFill = new SolidColorBrush(Colors.LightGray);
static SolidColorBrush _runningFill = new SolidColorBrush(Colors.OrangeRed);
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var status = (FlightTaskStatus)value;
return status == FlightTaskStatus.Running ? _runningFill : _normalFill;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,34 @@
using Plane.Copters;
using Plane.FormationCreator.Formation;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace Plane.FormationCreator.Converters
{
public class HeartbeatCountToBrushConverter : IMultiValueConverter
{
private static SolidColorBrush _zeroBrush = new SolidColorBrush(Color.FromArgb(125, 125, 125, 125));
private static SolidColorBrush _oneBrush = Copter.DefaultBrush;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] == DependencyProperty.UnsetValue) return _zeroBrush;
var heartbeatCount = (ulong)values[0];
if (heartbeatCount % 2 == 0) return _zeroBrush;
var copter = values[1] as Copter;
return copter == null ? _oneBrush : copter.Brush;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public enum AppMode
{
ControllingCopters,
ModifyingTask,
PreparedForRunningTasks,
RunningTasks,
MeasuringDistance
}
public static class AppModeExtensions
{
public static bool IsForControl(this AppMode mode)
{
return mode == AppMode.ControllingCopters;
}
public static bool IsForTasks(this AppMode mode)
{
return mode != AppMode.ControllingCopters;
}
}
}

View File

@ -0,0 +1,44 @@
using Plane.Copters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Communication;
using System.Threading;
using System.Windows.Media;
namespace Plane.FormationCreator.Formation
{
class Copter : PLCopter
{
public Copter(IConnection Connection, SynchronizationContext uiThreadContext) : base(Connection, uiThreadContext)
{
Brush = _brushes[NextBrushIndex()];
}
static SolidColorBrush[] _brushes = new[]
{
new SolidColorBrush(Color.FromArgb(180, 255, 0, 0)),
new SolidColorBrush(Color.FromArgb(180, 235, 97, 0)),
new SolidColorBrush(Color.FromArgb(180, 255, 255, 0)),
new SolidColorBrush(Color.FromArgb(180, 0, 255, 0)),
new SolidColorBrush(Color.FromArgb(180, 0, 198, 255)),
new SolidColorBrush(Color.FromArgb(180, 0, 122, 204)),
new SolidColorBrush(Color.FromArgb(180, 174, 0, 255))
};
static int _brushIndex = 0;
static int NextBrushIndex()
{
return _brushIndex++ % _brushes.Length;
}
internal static SolidColorBrush DefaultBrush { get; } = new SolidColorBrush(Color.FromRgb(28, 151, 234));
private SolidColorBrush _Brush;
public SolidColorBrush Brush
{
get { return _Brush ?? DefaultBrush; }
set { Set(nameof(Brush), ref _Brush, value); }
}
}
}

View File

@ -0,0 +1,85 @@
using Plane.Copters;
using GalaSoft.MvvmLight;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public class CopterManager : ObservableObject
{
public CopterManager()
{
AppEx.Current.AppModeChanged += (sender, e) =>
{
RaisePropertyChanged(nameof(Copters));
};
App.Current.Exit += (sender, e) =>
{
Task.WhenAll(Copters.Select(c => c.DisconnectAsync()));
};
}
public ObservableCollection<ICopter> Copters { get; } = new ObservableCollection<ICopter>();
public ArrayList CopterStatus = new ArrayList();
//public ObservableCollection<ICopter> Copters
//{
// get
// {
// switch (AppEx.Instance.CurrentMode)
// {
// case AppMode.ControllingCopters:
// default:
// return CoptersForControlling;
// case AppMode.PreparedForRunningTasks:
// case AppMode.ModifyingTask:
// return CoptersForModifyingTask;
// case AppMode.RunningTasks:
// throw new NotImplementedException();
// }
// }
//}
public IEnumerable<ICopter> SelectedCopters { get { return _selectedCoptersGetter().Cast<ICopter>(); } }
/// <summary>
/// 注意:为避免多线程操作出问题,每次使用此属性时都会新建一个 List
/// </summary>
public IEnumerable<ICopter> AcceptingControlCopters { get { return SelectedCopters.ToList(); } }
private Func<IList> _selectedCoptersGetter;
private Action<ICopter> _selectCopterAction;
public void SetSelectionDelegates(Func<IList> selectedCoptersGetter, Action<ICopter> selectCopterAction)
{
_selectedCoptersGetter = selectedCoptersGetter;
_selectCopterAction = selectCopterAction;
}
public event EventHandler<SelectedCoptersChangedEventArgs> SelectedCoptersChanged;
public void RaiseSelectedCoptersChanged(IEnumerable<ICopter> addedCopters, IEnumerable<ICopter> removedCopters)
{
SelectedCoptersChanged?.Invoke(this, new SelectedCoptersChangedEventArgs { AddedCopters = addedCopters, RemovedCopters = removedCopters });
}
public void Select(ICopter copter)
{
_selectCopterAction(copter);
}
}
public class SelectedCoptersChangedEventArgs : EventArgs
{
public IEnumerable<ICopter> AddedCopters { get; set; }
public IEnumerable<ICopter> RemovedCopters { get; set; }
}
}

View File

@ -0,0 +1,61 @@
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public class CopterStatus : ObservableObject
{
private float _Lng;
public float Lng
{
get { return _Lng == 0 ? 116.23426389F : _Lng; }
set { Set(nameof(Lng), ref _Lng, value); }
}
private float _Lat;
public float Lat
{
get { return _Lat == 0 ? 40.160491667F : _Lat; }
set { Set(nameof(Lat), ref _Lat, value); }
}
private float _Height;
public float Height
{
get { return _Height; }
set { Set(nameof(Height), ref _Height, value); }
}
private float _Heading;
public float Heading
{
get { return _Heading; }
set { Set(nameof(Heading), ref _Heading, value); }
}
private float _Pitch;
public float Pitch
{
get { return _Pitch; }
set { Set(nameof(Pitch), ref _Pitch, value); }
}
private float _Roll;
public float Roll
{
get { return _Roll; }
set { Set(nameof(Roll), ref _Roll, value); }
}
private DateTime _Time;
public DateTime Time
{
get { return _Time; }
set { Set(nameof(Time), ref _Time, value); }
}
}
}

View File

@ -0,0 +1,77 @@
using Plane.Copters;
using Plane.Geography;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
static class Extensions
{
public static LatLng? GetCenter(this IEnumerable<ICopter> copters)
{
int count = 0;
double sumLat = 0;
double sumLng = 0;
foreach (var copter in copters)
{
sumLat += copter.Latitude;
sumLng += copter.Longitude;
++count;
}
if (count == 0)
{
return null;
}
return new LatLng
{
Lat = sumLat / count,
Lng = sumLng / count
};
}
public static double InPlaneDirectionToDeg(this ICopter copter, double targetLat, double targetLng)
{
return GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(copter.Latitude, copter.Longitude, targetLat, targetLng));
}
public static double DistanceTo(this ICopter copter, double targetLat, double targetLng, float targetAlt)
{
return GeographyUtils.CalcDistance(copter.Latitude, copter.Longitude, copter.Altitude, targetLat, targetLng, targetAlt);
}
public static double DistanceTo(this ICopter copter, ICopter copter2)
{
return GeographyUtils.CalcDistance(copter.Latitude, copter.Longitude, copter.Altitude, copter2.Latitude, copter2.Longitude, copter2.Altitude);
}
public static double InPlaneDistanceTo(this ICopter copter, double targetLat, double targetLng)
{
return GeographyUtils.CalcDistance2D(copter.Latitude, copter.Longitude, targetLat, targetLng);
}
public static bool ArrivedTarget(this ICopter copter, double targetLat, double targetLng, float targetAlt)
{
return copter.DistanceTo(targetLat, targetLng, targetAlt) < 2;
// return copter.DistanceTo(targetLat, targetLng, targetAlt) < 1.5;
// return copter.DistanceTo(targetLat, targetLng, targetAlt) < 2; // added by ZJF
}
public static bool ArrivedTargetInPlane(this ICopter copter, double targetLat, double targetLng)
{
return copter.InPlaneDistanceTo(targetLat, targetLng) < 1;
}
public static bool ArrivedHeading(this ICopter copter, short targetHeading)
{
return Math.Abs(copter.Heading - targetHeading) < 2;
}
public static bool IsTooCloseTo(this ICopter copter, ICopter copter2)
{
return copter.DistanceTo(copter2) < 2;
}
}
}

View File

@ -0,0 +1,125 @@
using Plane.Copters;
using Plane.Logging;
using GalaSoft.MvvmLight;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask : ObservableObject
{
public FlightTask(FlightTaskType type)
{
TaskType = type;
}
private FlightTaskManager _flightTaskManager = ServiceLocator.Current.GetInstance<FlightTaskManager>();
private ILogger _logger = ServiceLocator.Current.GetInstance<ILogger>();
private FlightTaskStatus _Status = FlightTaskStatus.Created;
public FlightTaskStatus Status
{
get { return _Status; }
set { Set(nameof(Status), ref _Status, value); }
}
private FlightTaskType _TaskType;
public FlightTaskType TaskType
{
get { return _TaskType; }
set
{
if (_TaskType != value)
{
RaisePropertyChanged(nameof(TaskTypeIndex));
}
Set(nameof(TaskType), ref _TaskType, value);
}
}
public int TaskTypeIndex
{
get { return (int)TaskType; }
set
{
TaskType = (FlightTaskType)value;
if ( (value == (int)FlightTaskType.Loiter) || (value == (int)FlightTaskType.Circle) )
{
VerticalLift = true;
}
}
}
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set { Set(nameof(IsSelected), ref _IsSelected, value); }
}
// 右键单击任务,用于隐藏任务图标, added by ZJF
private bool _IsRightSelected;
public bool IsRightSelected
{
get { return _IsRightSelected; }
set { Set(nameof(IsRightSelected), ref _IsRightSelected, value); }
}
public List<FlightTaskSingleCopterInfo> SingleCopterInfos { get; set; } = new List<FlightTaskSingleCopterInfo>();
private FlightTaskSingleCopterInfo _ModifyingSingleCopterInfo;
public FlightTaskSingleCopterInfo ModifyingSingleCopterInfo
{
get { return _ModifyingSingleCopterInfo; }
set { Set(nameof(ModifyingSingleCopterInfo), ref _ModifyingSingleCopterInfo, value); }
}
public async Task RunAsync()
{
switch (TaskType)
{
case FlightTaskType.FlyTo:
await RunFlyToTaskAsync().ConfigureAwait(false);
break;
case FlightTaskType.Turn:
await RunTurnTaskAsync().ConfigureAwait(false);
break;
case FlightTaskType.Circle:
await RunCircleTaskAsync().ConfigureAwait(false);
break;
case FlightTaskType.SimpleCircle:
await RunSimpleCircleTaskAsync().ConfigureAwait(false);
break;
case FlightTaskType.Loiter:
await RunLoiterTimeTaskAsync().ConfigureAwait(false);
break;
case FlightTaskType.TakeOff:
await RunTakeOffTaskAsync().ConfigureAwait(false);
break;
case FlightTaskType.ReturnToLand: // Added by ZJF
await RunReturnToLandTaskAsync().ConfigureAwait(false);
break;
default:
throw new InvalidOperationException();
}
}
}
public enum FlightTaskType
{
FlyTo = 0,
Turn = 1,
Circle = 2,
Loiter = 3, // 加入悬停时间选项
// LEDAction = 4,
ReturnToLand = 4, // added by ZJF. 20160315
SimpleCircle = 5,
TakeOff = 100
}
}

View File

@ -0,0 +1,830 @@
using Plane.Collections;
using Plane.Copters;
using Plane.Geography;
using Plane.Windows.Messages;
using GalaSoft.MvvmLight;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public class FlightTaskManager : ObservableObject
{
public FlightTaskManager(CopterManager copterManager)
{
_copterManager = copterManager;
//AddTakeOffTask(_copterManager.Copters);
_copterManager.Copters.CollectionChanged += (sender, e) =>
{
// TODO: 林俊清, 20150724, 需要改为正确的做法(清除旧飞机的任务,补充新飞机的任务)。
AddTakeOffTask(e.NewItems?.Cast<ICopter>());
};
_copterManager.SelectedCoptersChanged += (sender, e) =>
{
// TODO: 林俊清, 20150803, 处理选中多个飞行器的情况。
if (_copterManager.SelectedCopters.Count() > 1)
{
return;
}
var selectedCopter = _copterManager.SelectedCopters.FirstOrDefault();
foreach (var task in Tasks)
{
foreach (var info in task.SingleCopterInfos)
{
if (info.Copter == selectedCopter)
{
task.ModifyingSingleCopterInfo = info;
break;
}
}
}
};
TaskAdded += (sender, e) =>
{
// TODO: 林俊清, 20150803, 处理选中多个飞行器的情况。
if (_copterManager.SelectedCopters.Count() > 1)
{
return;
}
var selectedCopter = _copterManager.SelectedCopters.FirstOrDefault();
var task = e.AddedTask;
foreach (var info in task.SingleCopterInfos)
{
if (info.Copter == selectedCopter)
{
task.ModifyingSingleCopterInfo = info;
break;
}
}
};
}
private CopterManager _copterManager;
private double _OriginLat;
public double OriginLat
{
get { return _OriginLat; }
set { Set(nameof(OriginLat), ref _OriginLat, value); }
}
private double _OriginLng;
public double OriginLng
{
get { return _OriginLng; }
set { Set(nameof(OriginLng), ref _OriginLng, value); }
}
private void AddTakeOffTask(IEnumerable<ICopter> copters)
{
if (copters == null || !copters.Any()) return;
bool takeOffTaskExisted = Tasks.Count >= 1;
FlightTask takeOffTask;
if (takeOffTaskExisted)
{
takeOffTask = Tasks[0];
}
else
{
takeOffTask = new FlightTask(FlightTaskType.TakeOff);
Tasks.Add(takeOffTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { AddedTask = takeOffTask });
}
foreach (var copter in copters)
{
takeOffTask.SingleCopterInfos.Add(FlightTaskSingleCopterInfo.CreateForTakeOffTask(copter, targetAlt: 10));
}
}
public ObservableCollection<FlightTask> Tasks { get; } = new ObservableCollection<FlightTask>();
private FlightTask _SelectedTask;
public FlightTask SelectedTask
{
get { return _SelectedTask; }
set
{
if (_SelectedTask != value)
{
if (_SelectedTask != null) _SelectedTask.IsSelected = false;
if (value != null) value.IsSelected = true;
}
Set(nameof(SelectedTask), ref _SelectedTask, value);
}
}
private int _SelectedTaskIndex;
public int SelectedTaskIndex
{
get { return _SelectedTaskIndex; }
set { Set(nameof(SelectedTaskIndex), ref _SelectedTaskIndex, value); }
}
// 右键单击任务,用于隐藏任务图标, added by ZJF
private int _RightSelectedTaskIndex;
public int RightSelectedTaskIndex
{
get { return _RightSelectedTaskIndex; }
set { Set(nameof(RightSelectedTaskIndex), ref _RightSelectedTaskIndex, value); }
}
private FlightTask _CurrentRunningTask;
public FlightTask CurrentRunningTask
{
get { return _CurrentRunningTask; }
private set { Set(nameof(CurrentRunningTask), ref _CurrentRunningTask, value); }
}
private int _CurrentRunningTaskIndex;
public int CurrentRunningTaskIndex
{
get { return _CurrentRunningTaskIndex; }
private set { Set(nameof(CurrentRunningTaskIndex), ref _CurrentRunningTaskIndex, value); }
}
public event EventHandler<FlightTaskAddedEventArgs> TaskAdded;
public event EventHandler<SingleCopterInfoChangedEventArgs> SingleCopterInfoChanged;
public void RaiseSingleCopterTaskChanged(FlightTaskSingleCopterInfo singleCopterInfo)
{
SingleCopterInfoChanged?.Invoke(this, new SingleCopterInfoChangedEventArgs(singleCopterInfo));
}
public event EventHandler TasksCleared;
public void AddTask()
{
//if (AppEx.Instance.CurrentMode == AppMode.PreparedForRunningTasks)
{
var copters = _copterManager.Copters;
if (!copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var nullableCenter = copters.GetCenter();
if (nullableCenter == null) return;
var center = nullableCenter.Value;
var newTask = new FlightTask(FlightTaskType.FlyTo);
foreach (var copter in copters)
{
var lastSingleCopterInfo = lastTask.SingleCopterInfos.Find(info => info.Copter == copter);
var direction = GeographyUtils.CalcDirection2D(center.Lat, center.Lng, lastSingleCopterInfo.TargetLat, lastSingleCopterInfo.TargetLng);
var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(lastSingleCopterInfo.TargetLat, lastSingleCopterInfo.TargetLng, (float)GeographyUtils.RadToDeg(direction), 5);
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForFlyToTask(copter, targetLatLng.Item1, targetLatLng.Item2, lastSingleCopterInfo.TargetAlt);
newSingleCopterInfo.TargetHeading = lastSingleCopterInfo.TargetHeading;
newSingleCopterInfo.CenterDirectionDeg = lastSingleCopterInfo.TargetHeading;
newTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(newTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = newTask });
SelectedTask = newTask;
SelectedTaskIndex = Tasks.Count - 1;
}
}
public void ClearTasks()
{
ResetTasks();
this.Tasks.Clear();
SelectedTask = null;
SelectedTaskIndex = 0;
TasksCleared?.Invoke(this, EventArgs.Empty);
AddTakeOffTask(_copterManager.Copters);
}
public void ResetTasks()
{
Pause();
CurrentRunningTaskIndex = 0;
if (CurrentRunningTask != null)
{
CurrentRunningTask.Status = FlightTaskStatus.Saved;
CurrentRunningTask = null;
}
for (int i = 0; i < Tasks.Count; i++)
{
// 将起飞的阶段标志位都置位0
if (Tasks[i].TaskType == FlightTaskType.TakeOff)
{
var infos = Tasks[i].SingleCopterInfos;
for (int j = 0; j < infos.Count; j++)
{
var info = infos[j];
info.takeOffStage = 0;
}
}
// 将降落的阶段标志位都置位0
if (Tasks[i].TaskType == FlightTaskType.ReturnToLand)
{
var infos = Tasks[i].SingleCopterInfos;
for (int j = 0; j < infos.Count; j++)
{
var info = infos[j];
info.RTLStage = 0;
}
}
}
}
public void RestoreFlyToTask(bool staggerRoutes, dynamic singleCopterInfos)
{
var copters = _copterManager.Copters;
if (!copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var nullableCenter = copters.GetCenter();
if (nullableCenter == null) return;
var center = nullableCenter.Value;
var newTask = new FlightTask(FlightTaskType.FlyTo) { StaggerRoutes = staggerRoutes };
// TODO: 林俊清, 20150801, 处理实际飞行器数目与记录中数目不一致的情况。
for (int i = 0; i < copters.Count && i < singleCopterInfos.Count; i++)
{
var copter = copters[i];
var singleCopterInfoObj = singleCopterInfos[i];
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForFlyToTask(copter, new LatLng((double)singleCopterInfoObj.latOffset, (double)singleCopterInfoObj.lngOffset), (float)singleCopterInfoObj.targetAlt);
newTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(newTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = newTask });
SelectedTask = newTask;
SelectedTaskIndex = Tasks.Count - 1;
}
private void RestoreTurnTask(dynamic singleCopterInfos)
{
var copters = _copterManager.Copters;
if (!copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var newTask = new FlightTask(FlightTaskType.Turn);
// TODO: 林俊清, 20150806, 处理实际飞行器数目与记录中数目不一致的情况。
for (int i = 0; i < copters.Count && i < singleCopterInfos.Count; i++)
{
var copter = copters[i];
var singleCopterInfoObj = singleCopterInfos[i];
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForTurnTask(copter, new LatLng((double)singleCopterInfoObj.latOffset, (double)singleCopterInfoObj.lngOffset), (float)singleCopterInfoObj.targetAlt, (short)singleCopterInfoObj.targetHeading);
newTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(newTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = newTask });
SelectedTask = newTask;
SelectedTaskIndex = Tasks.Count - 1;
}
private void RestoreCircleTask(dynamic singleCopterInfos)
{
var copters = _copterManager.Copters;
if (!copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var newTask = new FlightTask(FlightTaskType.Circle);
// TODO: 林俊清, 20150806, 处理实际飞行器数目与记录中数目不一致的情况。
for (int i = 0; i < copters.Count && i < singleCopterInfos.Count; i++)
{
var copter = copters[i];
var singleCopterInfoObj = singleCopterInfos[i];
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForCircleTask(copter, new LatLng((double)singleCopterInfoObj.latOffset, (double)singleCopterInfoObj.lngOffset), (float)singleCopterInfoObj.targetAlt, (short)singleCopterInfoObj.centerDirectionDeg, (int)singleCopterInfoObj.radius, (int)singleCopterInfoObj.rate, (int)singleCopterInfoObj.turns, (ushort)singleCopterInfoObj.channel3);
newTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(newTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = newTask });
SelectedTask = newTask;
SelectedTaskIndex = Tasks.Count - 1;
}
private void RestoreSimpleCircleTask(dynamic singleCopterInfos)
{
var copters = _copterManager.Copters;
if (!copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var newTask = new FlightTask(FlightTaskType.SimpleCircle);
// TODO: 林俊清, 20150806, 处理实际飞行器数目与记录中数目不一致的情况。
for (int i = 0; i < copters.Count && i < singleCopterInfos.Count; i++)
{
var copter = copters[i];
var singleCopterInfoObj = singleCopterInfos[i];
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForSimpleCircleTask(copter, new LatLng((double)singleCopterInfoObj.latOffset, (double)singleCopterInfoObj.lngOffset), (float)singleCopterInfoObj.targetAlt, (int)singleCopterInfoObj.rate, (int)singleCopterInfoObj.turns, (ushort)singleCopterInfoObj.channel3);
newTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(newTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = newTask });
SelectedTask = newTask;
SelectedTaskIndex = Tasks.Count - 1;
}
// added by ZJF
private void RestoreReturnToLandTask(IEnumerable<ICopter> copters)
{
if (copters == null || !copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var RTLTask = new FlightTask(FlightTaskType.ReturnToLand);
foreach (var copter in copters)
{
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForReturnToLandTask(copter, targetAlt:10);
RTLTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(RTLTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = RTLTask });
SelectedTask = RTLTask;
SelectedTaskIndex = Tasks.Count - 1;
}
// added by ZJF
private void RestoreLoiterTimeTask(float LoiteTimeTmp, bool flashCheck, float flashCheckPeriod,
bool oneByOneCheck, float oneByOneCheckPeriod, string flashNameArray, string flashIndexArray,
dynamic singleCopterInfos)
{
var copters = _copterManager.Copters;
if (copters == null || !copters.Any()) return;
AppEx.Current.AppMode = AppMode.ModifyingTask;
var lastTask = Tasks.LastOrDefault();
var loiterTimeTask = new FlightTask(FlightTaskType.Loiter)
{
LoiterTimeAttr = LoiteTimeTmp
};
loiterTimeTask.flashAttr = flashCheck;
loiterTimeTask.flashPeriodAttr = flashCheckPeriod;
loiterTimeTask.oneByOneAttr = oneByOneCheck;
loiterTimeTask.oneByOnePeriodAttr = oneByOneCheckPeriod;
loiterTimeTask.flashCopterNameArray = flashNameArray;
loiterTimeTask.flashCopterIndexArray = flashIndexArray;
// foreach (var copter in copters)
for (int i=0; i < copters.Count && i < singleCopterInfos.Count; i++)
{
var copter = copters[i];
var singleCopterInfoObj = singleCopterInfos[i];
var newSingleCopterInfo = FlightTaskSingleCopterInfo.CreateForLoiterTimeTask(copter, new LatLng((double)singleCopterInfoObj.latOffset, (double)singleCopterInfoObj.lngOffset), (float)singleCopterInfoObj.targetAlt);
loiterTimeTask.SingleCopterInfos.Add(newSingleCopterInfo);
}
Tasks.Add(loiterTimeTask);
TaskAdded?.Invoke(this, new FlightTaskAddedEventArgs { LastTask = lastTask, AddedTask = loiterTimeTask });
SelectedTask = loiterTimeTask;
SelectedTaskIndex = Tasks.Count - 1;
}
public string ExportTasks()
{
// For reference.
//var tasks = new object[]
//{
// new
// {
// type = FlightTaskType.TakeOff,
// },
// new
// {
// type = FlightTaskType.FlyTo,
// staggerRoutes = false,
// singleCopterInfos = new object[] {
// new {
// latOffset = 0.0001,
// lngOffset = 0.0001,
// targetAlt = 10
// },
// new {
// latOffset = 0.0003,
// lngOffset = 0.0003,
// targetAlt = 15
// }
// }
// },
// new
// {
// type = FlightTaskType.Turn,
// singleCopterInfos = new object[] {
// new {
// latOffset = 0.0001,
// lngOffset = 0.0001,
// targetAlt = 10,
// targetHeading = 90
// },
// new {
// latOffset = 0.0001,
// lngOffset = 0.0001,
// targetAlt = 10,
// targetHeading = 270
// }
// }
// },
// new
// {
// type = FlightTaskType.Circle,
// singleCopterInfos = new object[] {
// new {
// latOffset = 0.0001,
// lngOffset = 0.0001,
// targetAlt = 10,
// centerDirectionDeg = 90,
// radius = 1000,
// rate = 20,
// turns = 1,
// channel3 = 1500
// },
// new {
// latOffset = 0.0003,
// lngOffset = 0.0003,
// targetAlt = 15,
// centerDirectionDeg = 270,
// radius = 1000,
// rate = 20,
// turns = 1,
// channel3 = 1500
// }
// }
// },
// new
// {
// type = FlightTaskType.SimpleCircle,
// singleCopterInfos = new object[] {
// new {
// latOffset = 0.0001,
// lngOffset = 0.0001,
// targetAlt = 10,
// rate = 20,
// turns = 1,
// channel3 = 1500
// },
// new {
// latOffset = 0.0003,
// lngOffset = 0.0003,
// targetAlt = 15,
// rate = 20,
// turns = 1,
// channel3 = 1500
// }
// }
// }
//};
var tasks = Tasks.Select<FlightTask, object>(task =>
{
var type = task.TaskType;
switch (type)
{
case FlightTaskType.TakeOff:
return new { type = type };
case FlightTaskType.FlyTo:
return new
{
type = type,
staggerRoutes = task.StaggerRoutes,
singleCopterInfos = task.SingleCopterInfos.Select(info =>
{
var offset = info.LatLngOffset;
return new
{
latOffset = offset.Lat,
lngOffset = offset.Lng,
targetAlt = info.TargetAlt
};
})
};
case FlightTaskType.Turn:
return new
{
type = type,
singleCopterInfos = task.SingleCopterInfos.Select(info =>
{
var offset = info.LatLngOffset;
return new
{
latOffset = offset.Lat,
lngOffset = offset.Lng,
targetAlt = info.TargetAlt,
targetHeading = info.TargetHeading
};
})
};
case FlightTaskType.Loiter: // 导出悬停任务
return new
{
type = type,
loiterTimeAttr = task.LoiterTimeAttr,
flashCheck = task.flashAttr,
flashCheckPeriod = task.flashPeriodAttr,
oneByOneCheck = task.oneByOneAttr,
oneByOneCheckPeriod = task.oneByOnePeriodAttr,
flashNameArray = task.flashCopterNameArray,
flashIndexArray = task.flashCopterIndexArray,
singleCopterInfos = task.SingleCopterInfos.Select(info =>
{
var offset = info.LatLngOffset;
return new
{
latOffset = offset.Lat,
lngOffset = offset.Lng,
targetAlt = info.TargetAlt
};
})
};
case FlightTaskType.Circle:
return new
{
type = type,
singleCopterInfos = task.SingleCopterInfos.Select(info =>
{
var offset = info.LatLngOffset;
return new
{
latOffset = offset.Lat,
lngOffset = offset.Lng,
targetAlt = info.TargetAlt,
centerDirectionDeg = info.CenterDirectionDeg,
radius = info.Radius,
rate = info.Rate,
turns = info.Turns,
channel3 = info.Channel3
};
})
};
case FlightTaskType.SimpleCircle:
return new
{
type = type,
singleCopterInfos = task.SingleCopterInfos.Select(info =>
{
var offset = info.LatLngOffset;
return new
{
latOffset = offset.Lat,
lngOffset = offset.Lng,
targetAlt = info.TargetAlt,
rate = info.Rate,
turns = info.Turns,
channel3 = info.Channel3
};
})
};
case FlightTaskType.ReturnToLand: // added by ZJF
return new
{
type = type
};
default:
throw new NotImplementedException(type + " task export not implemented.");
}
});
return JsonConvert.SerializeObject(tasks);
}
public void ImportTasks(string tasksText)
{
dynamic tasks = JsonConvert.DeserializeObject(tasksText);
var copters = _copterManager.Copters;
foreach (var task in tasks)
{
switch ((FlightTaskType)task.type)
{
case FlightTaskType.TakeOff:
// AddTakeOffTask(copters); // added by ZJF
break;
case FlightTaskType.FlyTo:
RestoreFlyToTask((bool)task.staggerRoutes, task.singleCopterInfos);
break;
case FlightTaskType.Turn:
RestoreTurnTask(task.singleCopterInfos);
break;
case FlightTaskType.Circle:
RestoreCircleTask(task.singleCopterInfos);
break;
case FlightTaskType.Loiter:
RestoreLoiterTimeTask( (float)task.loiterTimeAttr, (bool)task.flashCheck, (float)task.flashCheckPeriod,
(bool)task.oneByOneCheck, (float)task.oneByOneCheckPeriod, (string)task.flashNameArray,
(string)task.flashIndexArray, task.singleCopterInfos);
break;
case FlightTaskType.SimpleCircle:
RestoreSimpleCircleTask(task.singleCopterInfos);
break;
case FlightTaskType.ReturnToLand:
RestoreReturnToLandTask(copters);
break;
}
}
}
public void Select(int taskIndex, ICopter copter)
{
this.SelectedTaskIndex = taskIndex;
this.SelectedTask = Tasks[taskIndex];
// 在悬停任务时左键waypoint为选中
// 在悬停任务模式且LED控制被选中时
if ((this.SelectedTask.TaskType == FlightTaskType.Loiter) && (this.SelectedTask.flashAttr || this.SelectedTask.oneByOneAttr))
{
string copterNameStr = this.SelectedTask.flashCopterNameArray;
string[] copterArray = copterNameStr.Split(',');
bool flag = false;
for (int i = 0; i < copterArray.Length; i++)
{
if (copterArray[i].Equals(""))
continue;
if (copterArray[i].Equals(copter.Name))
{
flag = true;
break;
}
}
if (!flag)
{
if (copterArray[0].Equals(""))
{
this.SelectedTask.flashCopterNameArray += copter.Name;
SelectedTask.flashCopterIndexArray += Name2Index(copter.Name);
}
else
{
this.SelectedTask.flashCopterNameArray += "," + copter.Name;
SelectedTask.flashCopterIndexArray += "," + Name2Index(copter.Name);
}
}
}
}
/**
* waypoint为取消选中
*/
public void RightSelect(int taskIndex, ICopter copter)
{
this.SelectedTaskIndex = taskIndex;
this.SelectedTask = Tasks[taskIndex];
// 在悬停任务模式且LED控制被选中时
if ((this.SelectedTask.TaskType == FlightTaskType.Loiter) && (this.SelectedTask.flashAttr || this.SelectedTask.oneByOneAttr))
{
this.SelectedTask.flashCopterNameArray = DeleteSelectedName(this.SelectedTask.flashCopterNameArray, copter.Name);
this.SelectedTask.flashCopterIndexArray = DeleteSelectedName(this.SelectedTask.flashCopterIndexArray, Name2Index(copter.Name).ToString());
}
}
private int Name2Index(string name) // 获取指定copter名字对应的序号
{
int index = -1;
for (int i = 0; i < SelectedTask.SingleCopterInfos.Count; i++)
{
if (name.Equals(SelectedTask.SingleCopterInfos[i].Copter.Name))
{
return i;
}
}
return index;
}
private string DeleteSelectedName( string nameArray, string name ) // 删除字符串中的指定项
{
string copterStr = nameArray;
if (copterStr.Equals(""))
return "";
string[] copterNameArray = copterStr.Split(',');
copterNameArray = copterNameArray.Where(str => !str.Equals(name)).ToArray();
copterStr = "";
for (int i = 0; i < copterNameArray.Length; i++)
{
copterStr = copterStr + copterNameArray[i];
if (i < (copterNameArray.Length - 1))
{
copterStr = copterStr + ",";
}
}
return copterStr;
}
public void Select(FlightTask flightTask)
{
this.SelectedTaskIndex = Tasks.IndexOf(flightTask);
this.SelectedTask = flightTask;
}
// 右键选中任务
public void RightSelect(FlightTask flightTask)
{
int RightSelectedTaskIndexTmp = Tasks.IndexOf(flightTask);
if (this.RightSelectedTaskIndex == RightSelectedTaskIndexTmp)
{
this.RightSelectedTaskIndex = -RightSelectedTaskIndexTmp;
}
else
{
this.RightSelectedTaskIndex = RightSelectedTaskIndexTmp;
}
}
#region Run and pause.
private bool? _IsPaused;
public bool? IsPaused
{
get { return _IsPaused; }
private set { Set(nameof(IsPaused), ref _IsPaused, value); }
}
public async Task RunAsync()
{
IsPaused = false;
AppEx.Current.AppMode = AppMode.RunningTasks;
StartAvoidingCrash();
for (int i = CurrentRunningTaskIndex; i < Tasks.Count; i++)
{
var task = Tasks[i];
task.Status = FlightTaskStatus.Running;
CurrentRunningTask = task;
CurrentRunningTaskIndex = i;
await task.RunAsync().ConfigureAwait(false);
// 1. 被暂停时,中断 RunAsync。继续运行时将把此时运行了一半的 CurrentRunningTask 重新运行一遍。
if (IsPaused == true) return;
task.Status = FlightTaskStatus.Saved;
}
// 2. 正常结束时,重置 CurrentRunningTask、CurrentRunningTaskIndex 和 IsPaused。
CurrentRunningTask = null;
CurrentRunningTaskIndex = 0;
IsPaused = null;
}
public void Pause()
{
IsPaused = true;
}
#endregion Run and pause.
private async void StartAvoidingCrash()
{
await Task.Factory.StartNew(LoopToAvoidCrash);
}
private async void LoopToAvoidCrash()
{
while (IsPaused == false)
{
try
{
// 在目前的起飞、降落逻辑,如果起飞或最后一个任务是返航降落,不要检查两架飞机距离过近. added by ZJF
bool RTLFlag = ( CurrentRunningTaskIndex == Tasks.Count()-1 ) && ( CurrentRunningTask.TaskType == FlightTaskType.ReturnToLand );
// bool RTLFlag = false;
if ( (CurrentRunningTaskIndex != 0) && !RTLFlag)
{
foreach (var copter in _copterManager.Copters)
{
foreach (var anotherCopter in _copterManager.Copters)
{
if (copter != anotherCopter && copter.IsTooCloseTo(anotherCopter))
{
Pause();
Alert.Show($"{copter.Name} 与 {anotherCopter.Name} 距离过近,已中止任务。");
return;
}
}
}
}
}
catch // 林俊清, 20151102, 通常是“集合已修改”异常。
{
}
finally
{
await Task.Delay(100).ConfigureAwait(false);
}
}
}
}
public class FlightTaskAddedEventArgs : EventArgs
{
public FlightTask LastTask { get; set; }
public FlightTask AddedTask { get; set; }
}
public class SingleCopterInfoChangedEventArgs : EventArgs
{
public SingleCopterInfoChangedEventArgs(FlightTaskSingleCopterInfo changedSingleCopterInfo)
{
this.ChangedSingleCopterInfo = changedSingleCopterInfo;
}
public FlightTaskSingleCopterInfo ChangedSingleCopterInfo { get; set; }
}
}

View File

@ -0,0 +1,72 @@
using Plane.Copters;
using GalaSoft.MvvmLight;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo : ObservableObject
{
public FlightTaskSingleCopterInfo(ICopter copter)
{
this.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case nameof(TargetLat):
case nameof(TargetLng):
case nameof(TargetAlt):
_flightTaskManager.RaiseSingleCopterTaskChanged(this);
break;
default:
break;
}
};
this.Copter = copter;
}
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
private FlightTaskManager _flightTaskManager = ServiceLocator.Current.GetInstance<FlightTaskManager>();
public ICopter Copter { get; set; }
private double? _TargetLat;
public double TargetLat { get { return _TargetLat ?? Copter.Latitude; } set { Set(nameof(TargetLat), ref _TargetLat, value); } }
private double? _TargetLng;
public double TargetLng { get { return _TargetLng ?? Copter.Longitude; } set { Set(nameof(TargetLng), ref _TargetLng, value); } }
private float? _TargetAlt;
public float TargetAlt { get { return _TargetAlt ?? Copter.Altitude; } set { Set(nameof(TargetAlt), ref _TargetAlt, value); } }
public LatLng LatLngOffset
{
get
{
var origin = GetOrigin();
return new LatLng(TargetLat - origin.Lat, TargetLng - origin.Lng);
}
set
{
var origin = GetOrigin();
TargetLat = origin.Lat + value.Lat;
TargetLng = origin.Lng + value.Lng;
}
}
public LatLng GetOrigin()
{
return _copterManager.Copters.GetCenter() ?? LatLng.Empty;
}
public bool isFinished;
public int takeOffStage; // 判断起飞处于哪个阶段. (0 -> 1 ->2, 对应起飞到10m -> 水平飞行 -> 垂直飞行)
public int RTLStage; // 判断降落返航处于哪个阶段. (0->1->2, 对应降落到10m -> 水平飞行 -> 垂直降落)
public int FlyToStage; // 判断FlyTo处于哪个阶段
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Copters;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
public static FlightTaskSingleCopterInfo CreateForCircleTask(ICopter copter, LatLng latLngOffset, float targetAlt, short centerDirectionDeg, int radius, int rate, int turns, ushort channel3)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
LatLngOffset = latLngOffset,
TargetAlt = targetAlt,
CenterDirectionDeg = centerDirectionDeg,
Radius = radius,
Rate = rate,
Turns = turns,
Channel3 = channel3
};
return info;
}
private short _CenterDirectionDeg;
public short CenterDirectionDeg
{
get { return _CenterDirectionDeg; }
set { Set(nameof(CenterDirectionDeg), ref _CenterDirectionDeg, value); }
}
private int _Radius = 1000;
/// <summary>
/// 圆的半径,单位为厘米。
/// </summary>
public int Radius
{
get { return _Radius; }
set { Set(nameof(Radius), ref _Radius, value); }
}
private int _Rate = 20;
/// <summary>
/// 转圈的角速度,单位为 deg/s范围为 -90 到 90。
/// </summary>
public int Rate
{
get { return _Rate; }
set { Set(nameof(Rate), ref _Rate, value); }
}
private int _Turns = 1;
public int Turns
{
get { return _Turns; }
set { Set(nameof(Turns), ref _Turns, value); }
}
private ushort _Channel3 = 1500;
public ushort Channel3
{
get { return _Channel3; }
set { Set(nameof(Channel3), ref _Channel3, value); }
}
}
}

View File

@ -0,0 +1,33 @@
using Plane.Copters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
public static FlightTaskSingleCopterInfo CreateForFlyToTask(ICopter copter, double targetLat, double targetLng, float targetAlt)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
TargetLat = targetLat,
TargetLng = targetLng,
TargetAlt = targetAlt
};
return info;
}
public static FlightTaskSingleCopterInfo CreateForFlyToTask(ICopter copter, LatLng latLngOffset, float targetAlt)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
LatLngOffset = latLngOffset,
TargetAlt = targetAlt
};
return info;
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Copters;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
public static FlightTaskSingleCopterInfo CreateForLoiterTimeTask(ICopter copter, LatLng latLngOffset, float targetAlt)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
LatLngOffset = latLngOffset,
TargetAlt = targetAlt
};
return info;
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Copters;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
// This file is added by ZJF
public static FlightTaskSingleCopterInfo CreateForReturnToLandTask(ICopter copter, float targetAlt)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
TargetAlt = targetAlt
};
return info;
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Copters;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
public static FlightTaskSingleCopterInfo CreateForSimpleCircleTask(ICopter copter, LatLng latLngOffset, float targetAlt, int rate, int turns, ushort channel3)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
LatLngOffset = latLngOffset,
TargetAlt = targetAlt,
Rate = rate,
Turns = turns,
Channel3 = channel3
};
return info;
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Copters;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
public static FlightTaskSingleCopterInfo CreateForTakeOffTask(ICopter copter, float targetAlt)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
TargetAlt = targetAlt
};
return info;
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Copters;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTaskSingleCopterInfo
{
public static FlightTaskSingleCopterInfo CreateForTurnTask(ICopter copter, LatLng latLngOffset, float targetAlt, short targetHeading)
{
var info = new FlightTaskSingleCopterInfo(copter)
{
LatLngOffset = latLngOffset,
TargetAlt = targetAlt,
TargetHeading = targetHeading
};
return info;
}
private short _TargetHeading;
public short TargetHeading
{
get { return _TargetHeading; }
set { Set(nameof(TargetHeading), ref _TargetHeading, value); }
}
}
}

View File

@ -0,0 +1,6 @@
public enum FlightTaskStatus
{
Created,
Saved,
Running
}

View File

@ -0,0 +1,137 @@
using Plane.Copters;
using Plane.Geography;
using Plane.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
public async Task RunCircleTaskAsync()
{
// 林俊清, 20150922, 首先一起调好方向。
await Task.WhenAll(
SingleCopterInfos.Select(info =>
{
int copterIndex = SingleCopterInfos.IndexOf(info);
// 当该飞机被标记时,跳过飞行任务
if ((bool)_copterManager.CopterStatus[copterIndex])
return Task.Run(()=> { });
info.TargetHeading = info.CenterDirectionDeg;
return TurnTaskFlySingleCopterAsync(info);
})
).ConfigureAwait(false);
await Task.WhenAll(
SingleCopterInfos.Select(info => CircleTaskFlySingleCopterAsync(info))
).ConfigureAwait(false);
}
private async Task CircleTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
// 林俊清, 20150907, 目前不对 Circle 进行仿真。
if (info.Copter is FakeCopter)
{
await Task.Delay(1000);
return;
}
int copterIndex = SingleCopterInfos.IndexOf(info);
// 当该飞机被标记时,跳过飞行任务
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
await info.Copter.SetParamAsync("CIRCLE_RADIUS", info.Radius);
await info.Copter.SetParamAsync("CIRCLE_RATE", info.Rate);
var center = GeographyUtils.CalcLatLngSomeMetersAway2D(info.Copter.Latitude, info.Copter.Longitude, info.CenterDirectionDeg, 10);
await info.Copter.CircleAsync();
while (info.Copter.State != Copters.CopterState.Circling)
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
}
await info.Copter.SetChannelsAsync(ch3: info.Channel3);
for (int i = info.Turns; i > 0; i--)
{
//// 等待一段时间,让飞行器飞出一段距离。
//for (int i = 0; i < 3000 / 25; i++)
//{
// if (_flightTaskManager.IsPaused == true)
// {
// info.Copter.Hover();
// return;
// }
// await Task.Delay(25).ConfigureAwait(false);
//}
//var log = new StringBuilder(1024);
//var lastLogTime = DateTime.Now;
bool step1 = false;
while (true)
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
var angle = info.Radius == 0 ? NormalizeAngleDeg(info.Copter.Heading - info.CenterDirectionDeg) : GetNormalizedAngleDeg(info, center);
//log.AppendLine($"{angle} {step1}");
if (!step1)
{
if (angle > 100 && angle < 330)
{
step1 = true;
//log.AppendLine($"step1 is true now.");
}
}
else if (angle > 340 || angle < 5)
{
//log.AppendLine("break");
break;
}
//if (lastLogTime.AddSeconds(10) < DateTime.Now)
//{
// Logger.Instance.Log(log.ToString());
// log.Clear();
// lastLogTime = DateTime.Now;
//}
await Task.Delay(25).ConfigureAwait(false);
}
//Logger.Instance.Log(log.ToString());
}
await info.Copter.HoverAsync();
}
private static double GetNormalizedAngleDeg(FlightTaskSingleCopterInfo info, Tuple<double, double> center)
{
var angle = info.Copter.InPlaneDirectionToDeg(center.Item1, center.Item2) - info.CenterDirectionDeg;
return NormalizeAngleDeg(angle);
}
private static double NormalizeAngleDeg(double angle)
{
while (true)
{
if (angle < 0) angle += 360;
else if (angle >= 360) angle -= 360;
else break;
}
return angle;
}
}
}

View File

@ -0,0 +1,738 @@
using Plane.Copters;
using Plane.Geography;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Windows.Messages;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
private bool _StaggerRoutes = true;
public bool StaggerRoutes
{
get { return _StaggerRoutes; }
set { Set(nameof(StaggerRoutes), ref _StaggerRoutes, value); }
}
private bool _VerticalLift = false;
public bool VerticalLift // 垂直升降标志位,后面需要加入即使拖动地图上的飞机,也不会变化经纬度. added by ZJF
{
get { return _VerticalLift; }
set
{
if (value)
{
int currentIndex = _flightTaskManager.SelectedTaskIndex;
var currentCopterInfos = _flightTaskManager.SelectedTask.SingleCopterInfos;
var previousCopterInfos = _flightTaskManager.Tasks[currentIndex - 1].SingleCopterInfos;
for (int i = 0; i < currentCopterInfos.Count; i++)
{
currentCopterInfos[i].TargetLat = previousCopterInfos[i].TargetLat;
currentCopterInfos[i].TargetLng = previousCopterInfos[i].TargetLng;
}
}
Set(nameof(VerticalLift), ref _VerticalLift, value);
}
}
public async Task RunFollowFourLeadersTaskAsync() // 跟随前面飞机
{
int[] followHeadsIndex = { 0, 0, 0, 0};
int followHeadsNum = 0;
int startTaskIndex = 0;
int endTaskIndex = 0;
int taskIndex = _flightTaskManager.CurrentRunningTaskIndex;
if (taskIndex >= 2 && taskIndex <= 4)
{
followHeadsNum = 1;
followHeadsIndex[0] = 0;
followHeadsIndex[1] = 4;
followHeadsIndex[2] = 8;
// followHeadsIndex[3] = 6;
startTaskIndex = 2;
endTaskIndex = 4;
}
if (taskIndex >= 18 && taskIndex <= 20)
{
followHeadsNum = 3;
followHeadsIndex[0] = 3;
followHeadsIndex[1] = 7;
followHeadsIndex[2] = 11;
// followHeadsIndex[3] = 6;
startTaskIndex = 18;
endTaskIndex = 20;
}
/*
if (taskIndex >= 6 && taskIndex <= 6)
{
followHeadsNum = 2;
followHeadsIndex[0] = 0;
followHeadsIndex[1] = 5;
startTaskIndex = 6;
endTaskIndex = 6;
}
*/
if (StaggerRoutes)
{
var infos = SingleCopterInfos;
if (_flightTaskManager.Tasks.IndexOf(this) == startTaskIndex)
{
var tasks = new Task[infos.Count];
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
var copter = info.Copter;
var i1 = i;
tasks[i1] = await Task.Factory.StartNew(async () =>
{
await FollowFourLeadersTaskFlySingleCopterAsync(info);
});
}
if (startTaskIndex == endTaskIndex)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
}
else
{
for (int i = 0; i < followHeadsNum; i++)
{
await tasks[followHeadsIndex[i]];
}
}
// await Task.WhenAll(tasks[0]).ConfigureAwait(false);
}
else if ((_flightTaskManager.Tasks.IndexOf(this) > startTaskIndex) && (_flightTaskManager.Tasks.IndexOf(this) < endTaskIndex))
{
var tasks = new Task[followHeadsNum];
for (int i = 0; i < followHeadsNum; i++)
{
var info = infos[followHeadsIndex[i]];
var copter = info.Copter;
var i1 = i;
tasks[i1] = await Task.Factory.StartNew(async () =>
{
await FollowFourLeadersTaskFlySingleCopterAsync(info);
});
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
else
//(_flightTaskManager.Tasks.IndexOf(this) == endTaskIndex)
{
var tasks = new Task[followHeadsNum];
for (int i = 0; i < followHeadsNum; i++)
{
var info = infos[followHeadsIndex[i]];
var copter = info.Copter;
var i1 = i;
tasks[i1] = await Task.Factory.StartNew(async () =>
{
await FollowFourLeadersTaskFlySingleCopterAsync(info);
});
}
await Task.WhenAll(tasks).ConfigureAwait(false);
while (true)
{
bool isFinishedFlag = true;
for (int i=0; i<infos.Count(); i++)
{
isFinishedFlag = isFinishedFlag && _flightTaskManager.Tasks[startTaskIndex].SingleCopterInfos[i].isFinished;
}
if (isFinishedFlag)
{
break;
}
}
}
}
else
{
await Task.WhenAll(
SingleCopterInfos.Select(info => FollowFourLeadersTaskFlySingleCopterAsync(info))
).ConfigureAwait(false);
}
}
private async Task FollowFourLeadersTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
var infos = SingleCopterInfos; // 本次任务所有飞机编号
int index = infos.FindIndex(s => s == info); // 本次任务当前飞机编号
int followHead = 0;
int indexHead = 0;
int startTaskIndex = 0;
int endTaskIndex = _flightTaskManager.Tasks.Count();
float directionAngle = 0;
int taskIndex = _flightTaskManager.CurrentRunningTaskIndex;
// 如果当前任务编号属于跟随过程的任务
if (taskIndex >= 2 && taskIndex <= 4)
{
if (index == 0 ) // 当前飞机编号属于领航的飞机
{
followHead = 1;
}
else // 当前飞机编号属于跟随的飞机
{
indexHead = index - 1; // 计算跟随的前方飞机编号
startTaskIndex = 2; // 本次跟随过程开始任务编号
endTaskIndex = 4; // 本次跟随过程结束任务编号
info.isFinished = false;// 赋值结束本次task的初始标志位
directionAngle = 0; // 与前方跟随飞机的角度0为正北方向
}
}
if (taskIndex >= 18 && taskIndex <= 20)
{
if (index == 3 || index == 7 || index == 11)
{
followHead = 1;
}
else
{
indexHead = index + 1;
startTaskIndex = 18;
endTaskIndex = 20;
info.isFinished = false;
directionAngle = 180;
}
}
/*
if (taskIndex >= 6 && taskIndex <= 6)
{
if (index == 0 || index == 5)
{
followHead = 1;
}
else
{
if (index % 2 == 0)
{
indexHead = index - 2;
directionAngle = 90;
}
else
{
indexHead = index + 2;
directionAngle = 270;
}
startTaskIndex = 6;
endTaskIndex = 6;
info.isFinished = false;
}
}
if (taskIndex >= 18 && taskIndex <= 20)
{
if (index == 1 || index == 3 || index == 5 || index == 7)
{
followHead = 1;
}
else
{
indexHead = index + 1;
}
}
if (taskIndex >= 7 && taskIndex <= 9)
{
if (index == 0 || index == 7 )
{
followHead = 1;
}
else
{
indexHead = index - 2;
}
}
if (taskIndex >= 10 && taskIndex <= 12)
{
if (index == 1 || index == 6)
{
followHead = 1;
}
else
{
indexHead = index + 2;
}
}
*/
// 如果不是被跟随的飞机
if (followHead != 1)
{
var infoBefore = infos[indexHead]; // 跟随的飞机编号
DateTime dtNow = DateTime.Now; // 记录当前时间
DateTime dtLastTime = DateTime.Now; // 记录gps上次更新时间
TimeSpan ts = dtNow - dtLastTime; // 记录gps是否更新时间段
int flagRefresh = 1; // gps更新标志
int flagStopTask = 0; // 如果飞机超过5s没有更新gps坐标则认为本次跟随任务结束, 即flagStopTask>=10
double GPSRate = 500; // 500 ms, gps更新时间
Tuple<double, double> targetLatLng = null; // 更新的gps坐标
// int taskIndex = 0;
int stopFlag = 10; // 结束任务标志, 10即10*500ms = 5s
// while (flagStopTask < stopFlag)
while (true)
{
taskIndex = _flightTaskManager.CurrentRunningTaskIndex; // 获取当前任务编号
if (flagRefresh == 1) // 需要更新gps坐标
{
flagRefresh = 0; // 更新标志清零
flagStopTask++; // 结束任务标志加1
// var direction = GeographyUtils.CalcDirection(info.Copter.Latitude, info.Copter.Longitude, infoBefore.Copter.Latitude, infoBefore.Copter.Longitude);
// targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway(infoBefore.Copter.Latitude, infoBefore.Copter.Longitude, (float)( direction), 5);
// 计算目标经纬度
targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(infoBefore.Copter.Latitude, infoBefore.Copter.Longitude, directionAngle, 8);
// 判断飞机当前是否在目标范围内. 球半径1m
if (!info.Copter.ArrivedTarget(targetLatLng.Item1, targetLatLng.Item2, infoBefore.Copter.Altitude))
{
flagStopTask = 0;
for (int j = 0; j < 3; j++)
{
// 发送目标坐标给飞控
await info.Copter.FlyToAsync(targetLatLng.Item1, targetLatLng.Item2, infoBefore.Copter.Altitude);
}
}
}
// 如果没有在目标范围内则一直在循环内。当更新gps标志置位退出循环
// 修改到达目标半径为1m与飞控的判断条件相同, by ZJF
while (!info.Copter.ArrivedTarget(targetLatLng.Item1, targetLatLng.Item2, infoBefore.Copter.Altitude))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > GPSRate)
{
break;
}
}
// 如果1、已经是最后一步任务2、结束任务标志位置位3、跟随的飞机已经结束任务则本飞机结束当前任务且切换到悬停模式
if ((taskIndex == _flightTaskManager.Tasks.Count() - 1) && (flagStopTask >= stopFlag) && infoBefore.isFinished)
// if ((taskIndex == _flightTaskManager.Tasks.Count() - 1) && (flagStopTask >= stopFlag) && _flightTaskManager.Tasks[taskIndex].SingleCopterInfos[indexHead].isFinished)
{
info.isFinished = true;
await info.Copter.HoverAsync();
// return;
}
// 如果1、已经是本次跟随过程的最后一步任务2、结束任务标志位置位3、跟随的飞机已经结束任务则本飞机结束当前任务退出循环
if ((taskIndex == endTaskIndex) && (flagStopTask >= stopFlag) && infoBefore.isFinished)
// if ((taskIndex == endTaskIndex) && (flagStopTask >= stopFlag) && _flightTaskManager.Tasks[taskIndex].SingleCopterInfos[indexHead].isFinished)
{
info.isFinished = true;
break;
// return;
}
// 如果任务暂停,则切换到悬停模式
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(20).ConfigureAwait(false);
// 计算是否更新gps标志位
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > GPSRate)
{
flagRefresh = 1;
dtLastTime = dtNow;
}
}
}
else // 如果是被跟随的飞机
{
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
taskIndex = _flightTaskManager.CurrentRunningTaskIndex;
for (int j = 0; j < 3; j++)
await info.Copter.FlyToAsync(info.TargetLat, info.TargetLng, info.TargetAlt);
while (!info.Copter.ArrivedTarget(info.TargetLat, info.TargetLng, info.TargetAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
// 为了防止3次发送更新gps坐标失败此处添加每隔1s重新发送目标gps坐标
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 1000)
{
for (int j = 0; j < 3; j++)
await info.Copter.FlyToAsync(info.TargetLat, info.TargetLng, info.TargetAlt);
dtLastTime = dtNow;
}
}
info.isFinished = true;
if (taskIndex == _flightTaskManager.Tasks.Count() - 1)
{
await info.Copter.HoverAsync();
}
// await info.Copter.HoverAsync();
}
}
public async Task RunFlyToOnlyAltTaskAsync() // 仅改变高度
{
if (StaggerRoutes)
{
var infos = SingleCopterInfos;
var tasks = new Task[infos.Count];
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
var copter = info.Copter;
var i1 = i;
tasks[i1] = await Task.Factory.StartNew(async () =>
{
if (i1 > 0)
{
var prevCopter = infos[i1 - 1].Copter;
while (CheckCrossing(infos, i1) &&
prevCopter.Altitude - copter.Altitude < 2)
{
await Task.Delay(25).ConfigureAwait(false);
}
}
await FlyToOnlyAltTaskFlySingleCopterAsync(info);
});
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
else
{
await Task.WhenAll(
SingleCopterInfos.Select(info => FlyToOnlyAltTaskFlySingleCopterAsync(info))
).ConfigureAwait(false);
}
}
private async Task FlyToOnlyAltTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info) // 仅高度变化
{
int taskIndex = _flightTaskManager.CurrentRunningTaskIndex;
int copterIndex = SingleCopterInfos.IndexOf(info);
double infoTargetLat = 0.0;
double infoTargetLng = 0.0;
//if (taskIndex > 0 && taskIndex <= 2)
//{
// infoTargetLat = _flightTaskManager.Tasks[0].SingleCopterInfos[copterIndex].TargetLat;
// infoTargetLng = _flightTaskManager.Tasks[0].SingleCopterInfos[copterIndex].TargetLng;
//}
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
infoTargetLat = info.Copter.Latitude;
infoTargetLng = info.Copter.Longitude;
for (int i = 0; i < 3; i++)
{
await info.Copter.FlyToAsync(infoTargetLat, infoTargetLng, info.TargetAlt);
}
while (!info.Copter.ArrivedTarget(infoTargetLat, infoTargetLng, info.TargetAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 2000)
{
for (int i = 0; i < 3; i++)
{
await info.Copter.FlyToAsync(infoTargetLat, infoTargetLng, info.TargetAlt);
}
dtLastTime = dtNow;
}
}
// await info.Copter.HoverAsync();
if (taskIndex == _flightTaskManager.Tasks.Count() - 1)
{
await info.Copter.HoverAsync();
}
}
public async Task RunFlyToTaskAsync() // 全部飞到指定航点
{
if (StaggerRoutes)
{
var infos = SingleCopterInfos;
var tasks = new Task[infos.Count];
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
tasks[i] = await Task.Factory.StartNew(async () =>
{
var internalInfo = info;
//if (i1 > 0)
//{
// var prevCopter = infos[i1 - 1].Copter;
// while (CheckCrossing(infos, i1) &&
// prevCopter.Altitude - copter.Altitude < 2)
// {
// await Task.Delay(25).ConfigureAwait(false);
// }
//}
await FlyToTaskFlySingleCopterAsync(internalInfo);
});
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
else
{
await Task.WhenAll(
SingleCopterInfos.Select(info => FlyToTaskFlySingleCopterAsync(info))
).ConfigureAwait(false);
}
}
/*
private async Task FlyToTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
int taskIndex = _flightTaskManager.CurrentRunningTaskIndex;
int copterIndex = SingleCopterInfos.IndexOf(info);
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
int pointNum = 10;
EHLocation[] targetLanLngArray = new EHLocation[pointNum];
if (taskIndex >= 2)
// if (false)
{
var copterPreviousTaskInfo = _flightTaskManager.Tasks[taskIndex - 1].SingleCopterInfos[copterIndex];
var direction = GeographyUtils.CalcDirection2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, info.TargetLat, info.TargetLng);
var distance = GeographyUtils.CalcDistance(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, copterPreviousTaskInfo.TargetAlt, info.TargetLat, info.TargetLng, info.TargetAlt);
var altDiff = info.TargetAlt - copterPreviousTaskInfo.TargetAlt;
var horizontalDiff = GeographyUtils.CalcDistance2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, info.TargetLat, info.TargetLng);
for (int ii = 0; ii < pointNum; ii++)
{
// var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, (float)(GeographyUtils.RadToDeg(direction)), (float)distance * (ii + 1) / pointNum);
var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, (float)(GeographyUtils.RadToDeg(direction)), (float)horizontalDiff * (ii + 1) / pointNum);
var targetAltTmp = copterPreviousTaskInfo.TargetAlt + altDiff * (ii + 1) / pointNum;
targetLanLngArray[ii] = new EHLocation(targetLatLng.Item1, targetLatLng.Item2, targetAltTmp);
}
}
else
{
for (int ii = 0; ii < pointNum; ii++)
{
targetLanLngArray[ii] = new EHLocation(info.TargetLat,info.TargetLng, info.TargetAlt);
}
}
int pointStage = 0;
while (pointStage < 10)
{
for (int i = 0; i < 3; i++)
{
// await info.Copter.FlyToAsync(info.TargetLat, info.TargetLng, info.TargetAlt);
await info.Copter.FlyToAsync(targetLanLngArray[pointStage].Latitude, targetLanLngArray[pointStage].Longitude, targetLanLngArray[pointStage].Altitude);
await Task.Delay(10).ConfigureAwait(false);
}
while (!info.Copter.ArrivedTarget(targetLanLngArray[pointStage].Latitude, targetLanLngArray[pointStage].Longitude, targetLanLngArray[pointStage].Altitude))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 2000)
{
for (int i = 0; i < 2; i++)
{
// await info.Copter.FlyToAsync(info.TargetLat, info.TargetLng, info.TargetAlt);
await info.Copter.FlyToAsync(targetLanLngArray[pointStage].Latitude, targetLanLngArray[pointStage].Longitude, targetLanLngArray[pointStage].Altitude);
await Task.Delay(10).ConfigureAwait(false);
}
dtLastTime = dtNow;
}
}
pointStage++;
}
// await info.Copter.HoverAsync();
if (taskIndex == _flightTaskManager.Tasks.Count() - 1)
{
await info.Copter.HoverAsync();
}
}
*/
private async Task FlyToTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
int taskIndex = _flightTaskManager.CurrentRunningTaskIndex;
int copterIndex = SingleCopterInfos.IndexOf(info);
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
int pointNum = 10;
PLLocation[] targetLanLngArray = new PLLocation[pointNum];
// if (taskIndex >= 2) // 将地图上两个目标点分成10份
if (false) // 只发送最终目标点
{
var copterPreviousTaskInfo = _flightTaskManager.Tasks[taskIndex - 1].SingleCopterInfos[copterIndex];
var direction = GeographyUtils.CalcDirection2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, info.TargetLat, info.TargetLng);
var distance = GeographyUtils.CalcDistance(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, copterPreviousTaskInfo.TargetAlt, info.TargetLat, info.TargetLng, info.TargetAlt);
var altDiff = info.TargetAlt - copterPreviousTaskInfo.TargetAlt;
var horizontalDiff = GeographyUtils.CalcDistance2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, info.TargetLat, info.TargetLng);
for (int ii = 0; ii < pointNum; ii++)
{
// var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, (float)(GeographyUtils.RadToDeg(direction)), (float)distance * (ii + 1) / pointNum);
var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(copterPreviousTaskInfo.TargetLat, copterPreviousTaskInfo.TargetLng, (float)(GeographyUtils.RadToDeg(direction)), (float)horizontalDiff * (ii + 1) / pointNum);
var targetAltTmp = copterPreviousTaskInfo.TargetAlt + altDiff * (ii + 1) / pointNum;
targetLanLngArray[ii] = new PLLocation(targetLatLng.Item1, targetLatLng.Item2, targetAltTmp);
}
}
else
{
for (int ii = 0; ii < pointNum; ii++)
{
targetLanLngArray[ii] = new PLLocation(info.TargetLat,info.TargetLng, info.TargetAlt);
}
}
int pointStage = 0;
for (int i = 0; i < 5; i++)
{
// await info.Copter.FlyToAsync(info.TargetLat, info.TargetLng, info.TargetAlt);
await info.Copter.FlyToAsync(targetLanLngArray[pointStage].Latitude, targetLanLngArray[pointStage].Longitude, targetLanLngArray[pointStage].Altitude);
await Task.Delay(10).ConfigureAwait(false);
}
pointStage++;
while (!info.Copter.ArrivedTarget(info.TargetLat, info.TargetLng, info.TargetAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 500) // 每200ms发送一遍指点坐标
{
for (int i = 0; i < 2; i++)
{
// await info.Copter.FlyToAsync(info.TargetLat, info.TargetLng, info.TargetAlt);
await info.Copter.FlyToAsync(targetLanLngArray[pointStage].Latitude, targetLanLngArray[pointStage].Longitude, targetLanLngArray[pointStage].Altitude);
await Task.Delay(10).ConfigureAwait(false);
}
pointStage++;
if (pointStage == pointNum)
{
pointStage = pointNum - 1;
}
dtLastTime = dtNow;
}
}
// await info.Copter.HoverAsync();
if (taskIndex == _flightTaskManager.Tasks.Count() - 1)
{
await info.Copter.HoverAsync();
}
}
private static bool CheckCrossing(List<FlightTaskSingleCopterInfo> infos, int currentIndex)
{
var info = infos[currentIndex];
for (int i = 0; i < currentIndex; i++)
{
var nextInfo = infos[i];
if (GeographyUtils.CheckCrossing2D(info.Copter.Latitude, info.Copter.Longitude, info.TargetLat, info.TargetLng,
nextInfo.Copter.Latitude, nextInfo.Copter.Longitude, nextInfo.TargetLat, nextInfo.TargetLng))
{
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,474 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
private float _LoiterTimeAttr = 0.0f;
public float LoiterTimeAttr
{
get { return _LoiterTimeAttr; }
set
{
Set(nameof(LoiterTimeAttr), ref _LoiterTimeAttr, value);
}
}
// LED灯闪烁
private bool _flashAttr = false;
public bool flashAttr
{
get { return _flashAttr; }
set
{
Set(nameof(flashAttr), ref _flashAttr, value);
}
}
// LED闪烁次数
private float _flashPeriodAttr = 1.0f;
public float flashPeriodAttr
{
get { return _flashPeriodAttr; }
set
{
Set(nameof(flashPeriodAttr), ref _flashPeriodAttr, value);
}
}
// 闪烁的飞机名字编号
private string _flashCopterNameArray = "";
public string flashCopterNameArray
{
get { return _flashCopterNameArray; }
set
{
Set(nameof(flashCopterNameArray), ref _flashCopterNameArray, value);
}
}
// 闪烁的飞机序号编号
private string _flashCopterIndexArray = "";
public string flashCopterIndexArray
{
get { return _flashCopterIndexArray; }
set
{
Set(nameof(flashCopterIndexArray), ref _flashCopterIndexArray, value);
}
}
// LED走马灯
private bool _oneByOneAttr = false;
public bool oneByOneAttr
{
get { return _oneByOneAttr; }
set
{
Set(nameof(oneByOneAttr), ref _oneByOneAttr, value);
}
}
// LED走马灯次数
private float _oneByOnePeriodAttr = 1.0f;
public float oneByOnePeriodAttr
{
get { return _oneByOnePeriodAttr; }
set
{
Set(nameof(oneByOnePeriodAttr), ref _oneByOnePeriodAttr, value);
}
}
private int[] columnFirst = {0, 1, 2, 3, 4 };
private int[] columnSecond = { 5, 6, 7, 8, 9 };
private int[] columnThird = { 10, 11, 12, 13, 14 };
private int[] columnFourth = { 15, 16, 17, 18, 19 };
private int[] columnFive = { 20, 21, 22, 23, 24 };
private int[] columnSix = { 25, 26, 27, 28, 29 };
private int[] rowFirst = { 0, 5, 10, 15, 20, 25 };
private int[] rowSecond = { 1, 6, 11, 16, 21, 26 };
private int[] rowThird = { 2, 7, 12, 17, 22, 27 };
private int[] rowFourth = { 3, 8, 13, 18, 23, 28 };
private int[] rowFive = { 4, 9, 14, 19, 24, 29 };
private int[] rowFirstForEight = { 0, 1, 2, 3 };
private int[] rowSecondForEight = { 4, 5, 6, 7 };
private int[] doubleColumnFirst = { 0, 1, 2, 3, 4, 25, 26, 27, 28, 29 };
private int[] doubleColumnSecond = { 5, 6, 7, 8, 9, 20, 21, 22, 23, 24 };
private int[] doubleColumnThird = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
private int[] circleFirst = { 0, 1, 2, 3, 4, 9, 14, 19, 24, 29, 28, 27, 26, 25, 20, 15, 10, 5 };
private int[] circleSecond = { 6, 7, 8, 13, 18, 23, 22, 21, 16, 11 };
private int[] circleThird = { 12, 17 };
private int[] lineFirst = { 4 };
private int[] lineSecond = { 3, 9 };
private int[] lineThird = { 2, 8, 14 };
private int[] lineFourth = { 1, 7, 13, 19 };
private int[] lineFive = { 0, 6, 12, 18, 24 };
private int[] lineSix = { 5, 11, 17, 23, 29 };
private int[] lineSeven = { 10, 16, 22, 28 };
private int[] lineEight = { 15, 21, 27 };
private int[] lineNine = { 20, 26 };
private int[] lineTen = { 25 };
public async Task RunLoiterTimeTaskAsync()
{
// 判断flashPeriodAttr, oneByOnePeriodAttr的值执行不同的闪烁模式
// 当flashPeriodAttr小于10正常闪烁即没有预制闪烁模式
// 当flashPeriodAttr大于等于10小于20执行预制闪烁模式
// 当flashPeriodAttr大于等于20 改变飞控闪烁模式以oneByOnePeriodAttr作为周期值
if ((flashPeriodAttr >= 10.0f) && (flashPeriodAttr < 20.0f))
{
await LEDFlashPlanAsync();
return;
}
if (flashPeriodAttr >= 20.0f)
{
await LEDFlashParaModifyPlanAsync();
return;
}
var infos = SingleCopterInfos;
if (flashAttr) // LED闪烁显示效果
{
if (flashCopterIndexArray.Equals(""))
return;
string[] copterArray = flashCopterIndexArray.Split(',');
var tasks_selected = new Task[copterArray.Length];
// LED灯全灭
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(1000).ConfigureAwait(false);
// 选中的飞机LED灯全亮
for (int i = 0; i < copterArray.Length; i++)
{
int index = int.Parse(copterArray[i]);
var info = infos[index];
tasks_selected[i] = await Task.Factory.StartNew(async () =>
{
var internalInfo = info;
await LEDFlashTaskFlySingleCopterAsync(internalInfo, true);
});
}
await Task.WhenAll(tasks_selected).ConfigureAwait(false);
await Task.Delay(3000).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, true)));
await Task.Delay(1000).ConfigureAwait(false);
}
else if (oneByOneAttr) // LED走马灯显示效果
{
if (flashCopterNameArray.Equals(""))
return;
string[] copterArray = flashCopterIndexArray.Split(',');
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(1000).ConfigureAwait(false);
// LED灯一个接一个全亮
for (int i = 0; i < copterArray.Length; i++)
{
int index = int.Parse(copterArray[i]);
var info = infos[index];
await LEDFlashTaskFlySingleCopterAsync(info, true);
await Task.Delay(200).ConfigureAwait(false);
}
await Task.Delay(1000).ConfigureAwait(false);
// LED灯全亮
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, true)));
await Task.Delay(1000).ConfigureAwait(false);
}
else // 没有LED显示效果
{
await Task.Delay((int)(LoiterTimeAttr * 1000)).ConfigureAwait(false);
}
}
private async Task LEDFlashParaModifyPlanAsync()
{
/*
await LEDFlashParaModifyAsync(rowFirstForEight);
await Task.Delay(500).ConfigureAwait(false);
await LEDFlashParaModifyAsync(rowSecondForEight);
await Task.Delay(500).ConfigureAwait(false);
*/
int copterCount = SingleCopterInfos.Count();
int[] intArray = new int[copterCount];
for (int ii=0; ii<copterCount; ii++)
{
intArray[ii] = ii;
}
await LEDFlashParaModifyAsync(intArray);
/*
await LEDFlashParaModifyAsync(rowFirst);
await Task.Delay(500).ConfigureAwait(false);
await LEDFlashParaModifyAsync(rowSecond);
await Task.Delay(500).ConfigureAwait(false);
await LEDFlashParaModifyAsync(rowThird);
await Task.Delay(500).ConfigureAwait(false);
await LEDFlashParaModifyAsync(rowFourth);
await Task.Delay(500).ConfigureAwait(false);
await LEDFlashParaModifyAsync(rowFive);
await Task.Delay(500).ConfigureAwait(false);
*/
}
private async Task LEDFlashParaModifyAsync(int[] LEDArray)
{
var infos = SingleCopterInfos;
var tasks_selected = new Task[LEDArray.Length];
for (int i = 0; i < LEDArray.Length; i++)
{
var info = infos[LEDArray[i]];
tasks_selected[i] = await Task.Factory.StartNew(async () =>
{
var internalInfo = info;
await LEDParaModifySingleCopterAsync(internalInfo, oneByOnePeriodAttr);
});
}
await Task.WhenAll(tasks_selected).ConfigureAwait(false);
}
private async Task LEDParaModifySingleCopterAsync(FlightTaskSingleCopterInfo info, float count)
{
var copter = info.Copter;
var tasks = new Task[2];
tasks[0] = Task.Run(async () =>
{
try
{
await copter.SetParamAsync("NOTI_GPSLED", count, 5000);
}
catch (TimeoutException ex)
{
_logger.Log($"NOTI_GPSLED 超时, {ex.Message}, CopterId: {copter.Id}。");
}
});
tasks[1] = Task.Run(async () =>
{
try
{
await copter.SetParamAsync("NOTI_ARMLED", count, 5000);
}
catch (TimeoutException ex)
{
_logger.Log($"NOTI_ARMLED 超时, {ex.Message}, CopterId: {copter.Id}。");
}
});
await Task.WhenAll(tasks).ConfigureAwait(false);
}
/**
* isOn为trueisOn为false
*/
private async Task LEDFlashTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info, bool isOn)
{
var copter = info.Copter;
// float gpsLed = await c.GetParamAsync("NOTI_GPSLED");
float ledControl = 0.0f;
if (isOn)
{
ledControl = 1.0f;
}
var tasks = new Task[2];
tasks[0] = Task.Run(async () =>
{
try
{
await copter.SetParamAsync("NOTI_GPSLED", ledControl, 5000);
}
catch (TimeoutException ex)
{
_logger.Log($"NOTI_GPSLED 超时, {ex.Message}, CopterId: {copter.Id}。");
}
});
tasks[1] = Task.Run(async () =>
{
try
{
await copter.SetParamAsync("NOTI_ARMLED", ledControl, 5000);
}
catch (TimeoutException ex)
{
_logger.Log($"NOTI_ARMLED 超时, {ex.Message}, CopterId: {copter.Id}。");
}
});
await Task.WhenAll(tasks).ConfigureAwait(false);
}
private async Task LEDFlashPlanAsync()
{
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(500).ConfigureAwait(false);
await LEDColumnFlashAsync();
await Task.Delay(500).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(500).ConfigureAwait(false);
await LEDDoubleColumnFlashAsync();
await Task.Delay(500).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(500).ConfigureAwait(false);
await LEDLineFlashAsync();
await Task.Delay(500).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(500).ConfigureAwait(false);
await LEDCircleFlashAsync();
await Task.Delay(500).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(200).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, true)));
await Task.Delay(200).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, false)));
await Task.Delay(200).ConfigureAwait(false);
await Task.WhenAll(SingleCopterInfos.Select(info => LEDFlashTaskFlySingleCopterAsync(info, true)));
await Task.Delay(200).ConfigureAwait(false);
}
// 一排一排闪烁
private async Task LEDColumnFlashAsync()
{
await LEDArrayFlashAsync(columnFirst);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(columnSecond);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(columnThird);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(columnFourth);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(columnFive);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(columnSix);
await Task.Delay(200).ConfigureAwait(false);
}
// 以圆圈收缩形式显示
private async Task LEDCircleFlashAsync()
{
await LEDArrayFlashAsync(circleFirst);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(circleSecond);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(circleThird);
await Task.Delay(200).ConfigureAwait(false);
}
// 两列同时闪烁
private async Task LEDDoubleColumnFlashAsync()
{
await LEDArrayFlashAsync(doubleColumnFirst);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(doubleColumnSecond);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(doubleColumnThird);
await Task.Delay(200).ConfigureAwait(false);
}
// 斜线方式闪烁
private async Task LEDLineFlashAsync()
{
await LEDArrayFlashAsync(lineFirst);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineSecond);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineThird);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineFourth);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineFive);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineSix);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineSeven);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineEight);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineNine);
await Task.Delay(200).ConfigureAwait(false);
await LEDArrayFlashAsync(lineTen);
await Task.Delay(200).ConfigureAwait(false);
}
private async Task LEDArrayFlashAsync(int[] LEDArray)
{
var infos = SingleCopterInfos;
var tasks_selected = new Task[LEDArray.Length];
for (int i = 0; i < LEDArray.Length; i++)
{
var info = infos[LEDArray[i]];
tasks_selected[i] = await Task.Factory.StartNew(async () =>
{
var internalInfo = info;
await LEDFlashTaskFlySingleCopterAsync(internalInfo, true);
});
}
await Task.WhenAll(tasks_selected).ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
private float _RTLAlt = 15.0f;
public float RTLAlt
{
get { return _RTLAlt; }
set { Set(nameof(RTLAlt), ref _RTLAlt, value); }
}
public async Task RunReturnToLandTaskAsync()
{
int TaskCount = _flightTaskManager.Tasks.Count();
if (TaskCount > 1)
{
var infos = SingleCopterInfos;
// var infos = SingleCopterInfos.OrderByDescending(i => i.TargetAlt).ToList();
var tasks = new Task[infos.Count];
var tasksTmp = new Task[infos.Count];
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
// var copter = info.Copter;
if (info.RTLStage <= 1) // 当前阶段小于等于1时进入
{
await ReturnToLandTaskFlySingleCopterAsync(info);
}
tasksTmp[i] = ReturnToLandSecondTaskAsync(info);
}
await Task.WhenAll(tasksTmp).ConfigureAwait(false);
if (_flightTaskManager.IsPaused == false)
{
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
info.RTLStage = 0;
}
}
}
}
private async Task ReturnToLandTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
float takeOffAlt = 15.0f;
int TaskCount = _flightTaskManager.Tasks.Count();
int copterIndex = SingleCopterInfos.IndexOf(info);
var copter = info.Copter;
var copterPreviousTask = _flightTaskManager.Tasks[TaskCount-2].SingleCopterInfos[copterIndex]; // 倒数第二步的目标位置
// 当该飞机被标记时,跳过飞行任务
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
// 第一阶段:垂直飞行
if (info.RTLStage == 0)
{
for (int j = 0; j < 3; j++)
{
await info.Copter.FlyToAsync(copterPreviousTask.TargetLat, copterPreviousTask.TargetLng, takeOffAlt);
}
while (!info.Copter.ArrivedTarget(copterPreviousTask.TargetLat, copterPreviousTask.TargetLng, takeOffAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 1000)
{
for (int j = 0; j < 2; j++)
{
await info.Copter.FlyToAsync(copterPreviousTask.TargetLat, copterPreviousTask.TargetLng, takeOffAlt);
}
dtLastTime = dtNow;
}
}
info.RTLStage++;
}
dtNow = DateTime.Now;
dtLastTime = DateTime.Now;
ts = dtNow - dtLastTime;
var copterFirstTask = _flightTaskManager.Tasks[0].SingleCopterInfos[copterIndex]; // 第一步记录的家位置
// 第二阶段:水平飞行
if (info.RTLStage == 1)
{
for (int j = 0; j < 3; j++)
{
await info.Copter.FlyToAsync(copterFirstTask.TargetLat, copterFirstTask.TargetLng, takeOffAlt);
}
while (!info.Copter.ArrivedTarget(copterFirstTask.TargetLat, copterFirstTask.TargetLng, takeOffAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 1000)
{
for (int j = 0; j < 2; j++)
{
await info.Copter.FlyToAsync(copterFirstTask.TargetLat, copterFirstTask.TargetLng, takeOffAlt);
}
dtLastTime = dtNow;
}
}
info.RTLStage++;
}
}
private async Task ReturnToLandSecondTaskAsync(FlightTaskSingleCopterInfo info)
{
// await Task.Run(async () =>
// {
int copterIndex = SingleCopterInfos.IndexOf(info);
// 当该飞机被标记时,跳过飞行任务
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
float landAlt = 4.0f; // 飞机降落到4m再切land模式
var copterFirstTask = _flightTaskManager.Tasks[0].SingleCopterInfos[copterIndex]; // 第一步记录的家位置
// 第三阶段
if (info.RTLStage == 2)
{
for (int j = 0; j < 3; j++)
{
await info.Copter.FlyToAsync(copterFirstTask.TargetLat, copterFirstTask.TargetLng, landAlt);
}
while (!info.Copter.ArrivedTarget(copterFirstTask.TargetLat, copterFirstTask.TargetLng, landAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 1000)
{
for (int j = 0; j < 2; j++)
{
await info.Copter.FlyToAsync(copterFirstTask.TargetLat, copterFirstTask.TargetLng, landAlt);
}
dtLastTime = dtNow;
}
}
info.RTLStage++;
}
// 切到land模式
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
var copter = info.Copter;
if (info.RTLStage == 3) // 下降过程
{
for (int j = 0; j < 5; j++) // added by ZJF
{
// await copter.ReturnToLaunchAsync();
await copter.LandAsync(); // 修改为降落模式
await Task.Delay(10);
}
}
// });
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
public async Task RunSimpleCircleTaskAsync()
{
var center = SingleCopterInfos.Select(info => info.Copter).GetCenter().Value;
// 林俊清, 20150921, 自动计算圆心方向和半径,然后直接调用普通画圈的方法。
foreach (var info in SingleCopterInfos)
{
info.CenterDirectionDeg = (short)info.Copter.InPlaneDirectionToDeg(center.Lat, center.Lng);
info.Radius = (int)(info.Copter.InPlaneDistanceTo(center.Lat, center.Lng) * 100);
}
await RunCircleTaskAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,299 @@
using Plane.Copters;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Plane.Geography;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
public async Task RunTakeOffTaskAsync()
{
// float takeOffAlt = 15;
int TaskCount = _flightTaskManager.Tasks.Count();
if (TaskCount > 1)
{
var infos = SingleCopterInfos;
// var tasks = new Task[infos.Count];
var tasksTmp = new Task[infos.Count];
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
if (info.takeOffStage == 0) // 第一阶段起飞到10m
{
await TakeOffTaskFlySingleCopterAsync(info);
//if (_flightTaskManager.IsPaused == false)
//{
// info.takeOffStage++;
//}
}
tasksTmp[i] = TakeOffSecondTaskAsync(info); // 第二和第三阶段
}
await Task.WhenAll(tasksTmp).ConfigureAwait(false);
if (_flightTaskManager.IsPaused == false)
{
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
info.takeOffStage = 0;
}
}
}
}
// 几架飞机同时起飞参数为takeOffCount
public async Task RunTakeOffTask2Async()
{
int TaskCount = _flightTaskManager.Tasks.Count();
if (TaskCount > 1)
{
var infos = SingleCopterInfos;
int takeOffCount = 5;
int copterCount = infos.Count;
int integerPart = copterCount / takeOffCount;
int residualPart = copterCount % takeOffCount;
// var tasks = new Task[infos.Count];
var tasksTmp = new Task[infos.Count];
for (int i = 0; i < integerPart; i++)
{
var tasksTakeOff = new Task[takeOffCount];
for (int j = takeOffCount * i; j < takeOffCount * (i + 1); j++)
{
var info = infos[j];
int indexTmp = j - takeOffCount * i;
if (info.takeOffStage == 0) // 第一阶段起飞到10m
{
tasksTakeOff[indexTmp] = TakeOffTaskFlySingleCopterAsync(info);
}
else
{
tasksTakeOff[indexTmp] = Task.Run(async () => { await Task.Delay(1).ConfigureAwait(false); });
}
}
await Task.WhenAll(tasksTakeOff).ConfigureAwait(false);
for (int j = takeOffCount * i; j < takeOffCount * (i + 1); j++)
{
var info = infos[j];
tasksTmp[j] = TakeOffSecondTaskAsync(info); // 第二和第三阶段
}
}
// 余数架飞机同时起飞
if (residualPart > 0)
{
var tasksTakeOff = new Task[residualPart];
for (int j = integerPart * takeOffCount; j < takeOffCount * integerPart + residualPart; j++)
{
var info = infos[j];
int indexTmp = j - takeOffCount * integerPart;
if (info.takeOffStage == 0) // 第一阶段起飞到10m
{
tasksTakeOff[indexTmp] = TakeOffTaskFlySingleCopterAsync(info);
}
else
{
tasksTakeOff[indexTmp] = Task.Run(async () => { await Task.Delay(1).ConfigureAwait(false); });
}
}
await Task.WhenAll(tasksTakeOff).ConfigureAwait(false);
for (int j = integerPart * takeOffCount; j < takeOffCount * integerPart + residualPart; j++)
{
var info = infos[j];
tasksTmp[j] = TakeOffSecondTaskAsync(info); // 第二和第三阶段
}
}
await Task.WhenAll(tasksTmp).ConfigureAwait(false);
if (_flightTaskManager.IsPaused == false)
{
for (int i = 0; i < infos.Count; i++)
{
var info = infos[i];
info.takeOffStage = 0;
}
}
}
}
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
private async Task TakeOffTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
int copterIndex = SingleCopterInfos.IndexOf(info);
var copter = info.Copter;
var copterNextTask = _flightTaskManager.Tasks[1].SingleCopterInfos[copterIndex];
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
if (_flightTaskManager.IsPaused == true)
{
return;
}
await copter.UnlockAsync();
for (int i = 0; !copter.IsUnlocked; i++)
{
//if (_flightTaskManager.IsPaused == true)
//{
// return;
//}
if (i % (1000 / 25) == 1000 / 25 - 1)
{
await copter.UnlockAsync(); // 每 1000 毫秒重试一次。
}
await Task.Delay(25).ConfigureAwait(false);
}
// 为了返航,记录家的位置, 应该放在起飞命令
info.TargetLat = info.Copter.Latitude;
info.TargetLng = info.Copter.Longitude;
for (int i = 0; i < 5; i++) // added by ZJF
{
await copter.TakeOffAsync();
await Task.Delay(50).ConfigureAwait(false);
}
// while (copter.Altitude < 4 || copter.State == Copters.CopterState.TakingOff)
while (copter.Altitude < 5) // 修改起飞逻辑当高度达到5米时下一架开始起飞
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(100).ConfigureAwait(false);
}
/* //先不要这个控制看能否正常工作
if (copter.Altitude > 8)
{
await info.Copter.GuidAsync();
return;
}
*/
}
private async Task TakeOffSecondTaskAsync(FlightTaskSingleCopterInfo info)
{
float takeOffAlt = 15;
int copterIndex = SingleCopterInfos.IndexOf(info);
var copterNextTask = _flightTaskManager.Tasks[1].SingleCopterInfos[copterIndex];
// 当该飞机被标记时,跳过飞行任务
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
// await Task.Run(async () =>
// {
// 修改起飞逻辑当高度达到5米时下一架开始起飞
if (info.takeOffStage == 0)
{
while (info.Copter.Altitude < 9)
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(100).ConfigureAwait(false);
}
if (_flightTaskManager.IsPaused == false)
{
info.takeOffStage++;
}
}
DateTime dtNow = DateTime.Now;
DateTime dtLastTime = DateTime.Now;
TimeSpan ts = dtNow - dtLastTime;
// 第二阶段:水平飞行
if (info.takeOffStage == 1)
{
await info.Copter.GuidAsync();
for (int j = 0; j < 3; j++)
{
await info.Copter.FlyToAsync(copterNextTask.TargetLat, copterNextTask.TargetLng, takeOffAlt);
await Task.Delay(10).ConfigureAwait(false);
}
while (!info.Copter.ArrivedTarget(copterNextTask.TargetLat, copterNextTask.TargetLng, takeOffAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 2000)
{
for (int j = 0; j < 2; j++)
{
await info.Copter.FlyToAsync(copterNextTask.TargetLat, copterNextTask.TargetLng, takeOffAlt);
await Task.Delay(10).ConfigureAwait(false);
}
dtLastTime = dtNow;
}
}
if (_flightTaskManager.IsPaused == false)
{
info.takeOffStage++;
}
}
dtNow = DateTime.Now;
dtLastTime = DateTime.Now;
ts = dtNow - dtLastTime;
// 第三阶段:垂直飞行
if (info.takeOffStage == 2)
{
for (int j = 0; j < 3; j++)
{
await info.Copter.FlyToAsync(copterNextTask.TargetLat, copterNextTask.TargetLng, copterNextTask.TargetAlt);
await Task.Delay(10).ConfigureAwait(false);
}
while (!info.Copter.ArrivedTarget(copterNextTask.TargetLat, copterNextTask.TargetLng, copterNextTask.TargetAlt))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
dtNow = DateTime.Now;
ts = dtNow - dtLastTime;
if (ts.TotalMilliseconds > 2000)
{
for (int j = 0; j < 2; j++)
{
await info.Copter.FlyToAsync(copterNextTask.TargetLat, copterNextTask.TargetLng, copterNextTask.TargetAlt);
await Task.Delay(10).ConfigureAwait(false);
}
dtLastTime = dtNow;
}
}
}
// });
}
}
}

View File

@ -0,0 +1,41 @@
using Plane.Copters;
using Plane.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public partial class FlightTask
{
public async Task RunTurnTaskAsync()
{
await Task.WhenAll(
SingleCopterInfos.Select(info => TurnTaskFlySingleCopterAsync(info))
).ConfigureAwait(false);
}
private async Task TurnTaskFlySingleCopterAsync(FlightTaskSingleCopterInfo info)
{
int copterIndex = SingleCopterInfos.IndexOf(info);
// 当该飞机被标记时,跳过飞行任务
if ((bool)_copterManager.CopterStatus[copterIndex])
return;
await info.Copter.SetMobileControlAsync(yaw: info.TargetHeading);
while (!info.Copter.ArrivedHeading(info.TargetHeading))
{
if (_flightTaskManager.IsPaused == true)
{
await info.Copter.HoverAsync();
return;
}
await Task.Delay(25).ConfigureAwait(false);
}
await info.Copter.HoverAsync();
}
}
}

View File

@ -0,0 +1,734 @@
using Plane.Communication;
using Plane.Copters;
using Plane.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using static Plane.Copters.Constants;
using Plane.Geography;
namespace Plane.FormationCreator.Formation
{
public class FormationController
{
public FormationController(ILogger logger, CopterManager copterManager)
{
_logger = logger;
_copterManager = copterManager;
foreach (var copter in _copterManager.Copters)
{
_guides.Add(copter, null);
}
_copterManager.Copters.CollectionChanged += (sender, e) =>
{
if (e.OldItems != null)
{
foreach (ICopter copter in e.OldItems)
{
if (_guides.ContainsKey(copter))
{
_guides.Remove(copter);
}
}
}
if (e.NewItems != null)
{
foreach (ICopter copter in e.NewItems)
{
if (!_guides.ContainsKey(copter))
{
_guides.Add(copter, null);
}
}
}
};
//var thread = new Thread(() =>
//{
// while (true)
// {
// Control();
// Thread.Sleep(50);
// }
//})
//{
// IsBackground = true
//};
//thread.Start();
var timer = new System.Timers.Timer(INTERVAL);
timer.Elapsed += (sender, e) => Control();
timer.Start();
}
private const int INTERVAL = 50;
private bool _shouldStop;
private Dictionary<ICopter, Guide> _guides = new Dictionary<ICopter, Guide>();
private ILogger _logger;
private CopterManager _copterManager;
#region
private bool AnyCopterWithGuide()
{
try
{
return _guides.Any(item => item.Value != null);
}
catch (InvalidOperationException ex) // 集合已修改。
{
_logger.Log(ex.ToString(), Category.Info, Priority.None);
return true; // 安全起见返回 true让调用者再次检查。
}
}
private bool AnyCopterWithGuide(IEnumerable<ICopter> copters)
{
try
{
return copters.Any(c => _guides[c] != null);
}
catch (InvalidCastException ex) // 集合已修改。
{
_logger.Log(ex.ToString(), Category.Info, Priority.None);
return true; // 安全起见返回 true让调用者再次检查。
}
}
private async Task EnsureAllTasksFinishedAsync()
{
await Task.Run(async () =>
{
while (AnyCopterWithGuide())
{
await Task.Delay(INTERVAL).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}
private async Task EnsureCopterTasksFinishedAsync(IEnumerable<ICopter> copters)
{
await Task.Run(async () =>
{
while (AnyCopterWithGuide(copters))
{
await Task.Delay(INTERVAL).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}
private async Task EnsureTaskFinishedAsync(ICopter copter)
{
while (_guides[copter] != null)
{
await Task.Delay(INTERVAL).ConfigureAwait(false);
}
}
#endregion
private void Control()
{
try
{
var copters = _guides.Keys.ToList();
Parallel.ForEach(copters, (copter, loopState) =>
{
try
{
if (_shouldStop)
{
loopState.Stop();
return;
}
var guide = _guides[copter];
if (guide == null)
{
return;
}
if (!guide.IsLoop && guide.FinishPredicate?.Invoke() == true)
{
var finishAction = guide?.FinishAction;
_guides[copter] = null;
copter.HoverAsync().Wait();
finishAction?.Invoke();
}
else
{
guide.UpdateAction?.Invoke();
}
}
catch (Exception ex)
{
_logger.Log(ex);
}
});
if (_shouldStop)
{
Parallel.ForEach(copters, copter =>
{
try
{
var finishAction = _guides[copter]?.FinishAction;
_guides[copter] = null;
copter.HoverAsync().Wait();
finishAction?.Invoke();
}
catch (Exception ex)
{
_logger.Log(ex);
}
});
}
}
catch (Exception ex)
{
_logger.Log(ex);
}
}
public async Task AllStop()
{
Parallel.ForEach(_copterManager.Copters, copter => copter.HoverAsync());
if (!_shouldStop && AnyCopterWithGuide())
{
_shouldStop = true;
while (AnyCopterWithGuide())
{
await Task.Delay(INTERVAL).ConfigureAwait(false);
}
_shouldStop = false;
}
Parallel.ForEach(_copterManager.Copters, copter => copter.HoverAsync().Wait());
}
public async Task TurnAsync(IEnumerable<ICopter> copters, double targetLat, double targetLng)
{
await AllStop();
foreach (var copter in copters)
{
var guide = !_guides.ContainsKey(copter) || _guides[copter] == null ? (_guides[copter] = new Guide()) : _guides[copter];
var targetHeading = (float)GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(copter.Latitude, copter.Longitude, targetLat, targetLng));
guide.FinishPredicate = () => Math.Abs(copter.Heading - targetHeading) < 1;
guide.UpdateAction = () =>
{
copter.MoveToIdealState(new CopterState
{
Heading = targetHeading
});
};
}
await EnsureAllTasksFinishedAsync().ConfigureAwait(false);
}
public async Task FlyToLatLngAsync(ICopter copter, double targetLat, double targetLng)
{
const double DT = INTERVAL / 1000.0;
const double DECELERATE_DISTANCE = 15;
double idealVel = 0;
double idealMovedDistance = 0;
var startLat = copter.Latitude;
var startLng = copter.Longitude;
var startTargetDistance = GeographyUtils.CalcDistance2D(startLat, startLng, targetLat, targetLng);
var idealMoveDirectionRadians = (float)GeographyUtils.CalcDirection2D(startLat, startLng, targetLat, targetLng);
var idealMoveDirectionDegrees = (float)GeographyUtils.RadToDeg(idealMoveDirectionRadians);
var guide = !_guides.ContainsKey(copter) || _guides[copter] == null ? (_guides[copter] = new Guide()) : _guides[copter];
guide.FinishPredicate = () => GeographyUtils.CalcDistance2D(copter.Latitude, copter.Longitude, targetLat, targetLng) <= 0.5;
var log = new List<object>();
guide.UpdateAction = () =>
{
if (idealVel < MAX_VEL)
{
idealVel += MAX_ACCEL * DT;
if (idealVel > MAX_VEL) idealVel = MAX_VEL;
}
var distance = GeographyUtils.CalcDistance2D(copter.Latitude, copter.Longitude, targetLat, targetLng);
if (distance < DECELERATE_DISTANCE) idealVel = MAX_VEL * distance / DECELERATE_DISTANCE;
double idealLat, idealLng;
if (idealMovedDistance < startTargetDistance)
{
idealMovedDistance += idealVel * DT;
if (idealMovedDistance > startTargetDistance)
{
idealLat = targetLat;
idealLng = targetLng;
}
else
{
var idealLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(startLat, startLng, idealMoveDirectionDegrees, (float)idealMovedDistance);
idealLat = idealLatLng.Item1;
idealLng = idealLatLng.Item2;
}
}
else
{
idealLat = targetLat;
idealLng = targetLng;
}
copter.MoveToIdealState(new CopterState
{
Latitude = idealLat,
Longitude = idealLng,
Heading = idealMoveDirectionDegrees,
VelocityDirection = idealMoveDirectionRadians,
Velocity = idealVel
});
log.Add(new
{
time = DateTime.Now,
targetLat = idealLat,
targetLng = idealLng,
lat = copter.Latitude,
lng = copter.Longitude,
alt = copter.Altitude
});
};
guide.FinishAction = () => _logger.Log(JsonConvert.SerializeObject(log));
await EnsureAllTasksFinishedAsync().ConfigureAwait(false);
}
public async Task FlyToLatLngAsync(IEnumerable<ICopter> copters, double targetLat, double targetLng)
{
await AllStop();
if (!copters.Any()) return;
var centerLat = copters.Average(c => c.Latitude);
var centerLng = copters.Average(c => c.Longitude);
var latDelta = targetLat - centerLat;
var lngDelta = targetLng - centerLng;
foreach (var copter in copters)
{
var copterTargetLat = copter.Latitude + latDelta;
var copterTargetLng = copter.Longitude + lngDelta;
var guide = !_guides.ContainsKey(copter) || _guides[copter] == null ? (_guides[copter] = new Guide()) : _guides[copter];
guide.FinishPredicate = () => GeographyUtils.CalcDistance2D(copter.Latitude, copter.Longitude, copterTargetLat, copterTargetLng) <= 0.5;
guide.UpdateAction = () =>
{
copter.MoveToIdealState(new CopterState
{
Latitude = copterTargetLat,
Longitude = copterTargetLng
});
};
}
await EnsureAllTasksFinishedAsync().ConfigureAwait(false);
}
public async Task FlyToAltitudeAsync(ICopter copter, float targetAlt)
{
var guide = _guides[copter] ?? (_guides[copter] = new Guide());
guide.FinishPredicate = () => Math.Abs(copter.Altitude - targetAlt) <= 0.5;
guide.UpdateAction = () =>
{
copter.MoveToIdealState(new CopterState
{
Altitude = targetAlt
});
};
await EnsureTaskFinishedAsync(copter).ConfigureAwait(false);
}
public async Task FlyToAltitudeAsync(IEnumerable<ICopter> copters, float targetAlt)
{
await AllStop();
foreach (var copter in copters)
{
if (!_guides.ContainsKey(copter))
{
_guides.Add(copter, null);
}
var guide = _guides[copter] ?? (_guides[copter] = new Guide());
guide.FinishPredicate = () => Math.Abs(copter.Altitude - targetAlt) <= 0.5;
guide.UpdateAction = () =>
{
copter.MoveToIdealState(new CopterState
{
Altitude = targetAlt
});
};
}
await EnsureAllTasksFinishedAsync().ConfigureAwait(false);
}
public async Task FlyInCircleAsync(IEnumerable<ICopter> copters, float centerDirection, float radius, bool clockwise = true, bool loop = false, double? velLimit = null)
{
await AllStop();
var velMax = velLimit ?? 2.0;
var velMin = -velMax;
var angVelMax = velMax / radius;
var angVelMin = velMin / radius;
var angAccel = (clockwise ? MAX_ACCEL : -MAX_ACCEL) / radius;
Parallel.ForEach(copters, copter =>
{
var guide = _guides[copter] ?? (_guides[copter] = new Guide());
guide.IsLoop = loop;
var centerLat = copter.Latitude + radius * Math.Cos(GeographyUtils.DegToRad(centerDirection)) * GeographyUtils.METERS_TO_LAT_SPAN;
var centerLng = copter.Longitude + radius * Math.Sin(GeographyUtils.DegToRad(centerDirection)) * GeographyUtils.CalcMetersToLngSpan(copter.Latitude);
var startingPointLat = copter.Latitude;
var startingPointLng = copter.Longitude;
var angVel = 0.0;
const double DT = INTERVAL / 1000.0;
var angleTotal = 0.0;
var startAngle = GeographyUtils.CalcDirection2D(centerLat, centerLng, copter.Latitude, copter.Longitude);
guide.FinishPredicate = () => angleTotal >= Math.PI * 2;
var log = new List<object>();
#region guide.UpdateAction
guide.UpdateAction = () =>
{
if (clockwise)
{
if (angVel < angVelMax)
{
angVel += angAccel * DT;
MathUtils.Constrain(ref angVel, 0, angVelMax);
}
}
else
{
if (angVel > angVelMin)
{
angVel += angAccel * DT;
MathUtils.Constrain(ref angVel, angVelMin, 0);
}
}
var angleChange = angVel * DT;
angleTotal += angleChange;
var targetAngle = startAngle + angleTotal;
var targetLat = centerLat + Math.Cos(targetAngle) * radius * GeographyUtils.METERS_TO_LAT_SPAN;
var targetLng = centerLng + Math.Sin(targetAngle) * radius * GeographyUtils.CalcMetersToLngSpan(copter.Latitude);
copter.MoveToIdealState(new CopterState
{
Latitude = targetLat,
Longitude = targetLng,
Heading = (float)GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(targetLat, targetLng, centerLat, centerLng)),
VelocityDirection = GeographyUtils.CalcDirection2D(centerLat, centerLng, targetLat, targetLng)
+ Math.PI / 2,
Velocity = angVel * radius
});
log.Add(new
{
time = DateTime.Now,
targetLat,
targetLng,
lat = copter.Latitude,
lng = copter.Longitude,
alt = copter.Altitude,
angAccel,
angVel,
angleChange,
angleTotal
});
};
#endregion guide.UpdateAction
guide.FinishAction = () =>
{
_logger.Log(JsonConvert.SerializeObject(log));
};
});
await EnsureAllTasksFinishedAsync().ConfigureAwait(false);
}
public async Task FlyAroundCenterOfCoptersAsync(IEnumerable<ICopter> copters, bool clockwise = true, bool loop = false, double? velLimit = null)
{
await AllStop();
var nullableCenter = copters.GetCenter();
if (nullableCenter == null) return;
var center = nullableCenter.Value;
var radiuses = copters.ToDictionary(c => c, c => GeographyUtils.CalcDistance2D(c.Latitude, c.Longitude, center.Lat, center.Lng));
var velMax = velLimit ?? 2.0;
var velMin = -velMax;
var angVelMax = velMax / radiuses.Values.Max();
Parallel.ForEach(copters, copter =>
{
var guide = _guides[copter] ?? (_guides[copter] = new Guide());
guide.IsLoop = loop;
var startingPointLat = copter.Latitude;
var startingPointLng = copter.Longitude;
var radius = radiuses[copter];
var angVelMin = velMin / radius;
var angAccel = (clockwise ? 10 : -10) / radius;
var angVel = 0.0;
const double DT = INTERVAL / 1000.0;
var angleTotal = 0.0;
var startAngle = GeographyUtils.CalcDirection2D(center.Lat, center.Lng, copter.Latitude, copter.Longitude);
guide.FinishPredicate = () => angleTotal >= Math.PI * 2;
var log = new List<object>();
#region guide.UpdateAction
guide.UpdateAction = () =>
{
if (clockwise)
{
if (angVel < angVelMax)
{
angVel += angAccel * DT;
MathUtils.Constrain(ref angVel, 0, angVelMax);
}
}
else
{
if (angVel > angVelMin)
{
angVel += angAccel * DT;
MathUtils.Constrain(ref angVel, angVelMin, 0);
}
}
var angleChange = angVel * DT;
angleTotal += angleChange;
var targetAngle = startAngle + angleTotal;
var targetLat = center.Lat + Math.Cos(targetAngle) * radius * GeographyUtils.METERS_TO_LAT_SPAN;
var targetLng = center.Lng + Math.Sin(targetAngle) * radius * GeographyUtils.CalcMetersToLngSpan(copter.Latitude);
copter.MoveToIdealState(new CopterState
{
Latitude = targetLat,
Longitude = targetLng,
Heading = (float)GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(targetLat, targetLng, center.Lat, center.Lng)),
VelocityDirection = GeographyUtils.CalcDirection2D(center.Lat, center.Lng, targetLat, targetLng)
+ Math.PI / 2,
Velocity = angVel * radius
});
log.Add(new
{
time = DateTime.Now,
targetLat,
targetLng,
lat = copter.Latitude,
lng = copter.Longitude,
alt = copter.Altitude,
angAccel,
angVel,
angleChange,
angleTotal
});
};
#endregion guide.UpdateAction
guide.FinishAction = () =>
{
_logger.Log(JsonConvert.SerializeObject(log));
};
});
await EnsureAllTasksFinishedAsync().ConfigureAwait(false);
}
public Task FlyInRectangleAsync(IEnumerable<ICopter> copters, float startDirection, float sideLength1, float sideLength2, bool clockwise = true, bool loop = false)
{
throw new NotImplementedException();
//await AllStop();
//Parallel.ForEach(copters, copter =>
//{
// const int pointCount = 4;
// var points = new LatLng[pointCount];
// // 把起始点也加进去是为了循环飞行。
// points[0] = new LatLng { Lat = copter.Latitude, Lng = copter.Longitude };
// var directionRad = startDirection * GeographyUtils.DegreesToRad;
// float distance;
// LatLng lastPoint;
// for (int i = 1; i < pointCount; ++i)
// {
// distance = i % 2 != 0 ? sideLength1 : sideLength2;
// lastPoint = points[i - 1];
// points[i] = new LatLng
// {
// Lat = lastPoint.Lat + distance * Math.Cos(directionRad) * GeographyUtils.MetersToLocalLat,
// Lng = lastPoint.Lng + distance * Math.Sin(directionRad) * GeographyUtils.GetMetersToLocalLon(lastPoint.Lat)
// };
// if (clockwise)
// {
// directionRad += Math.PI / 2;
// }
// else
// {
// directionRad -= Math.PI / 2;
// }
// }
// var target = _guides[copter] ?? new Guide();
// target.LatLngs = points;
// target.IsLoop = loop;
// target.CurrentIndex = 0;
// _guides[copter] = target;
// _logger.Log(string.Join(Environment.NewLine, points.Select(p => $"{p.Lat} {p.Lng}")), Category.Debug, Priority.None);
//});
//await Task.Run(() =>
//{
// while (AnyCopterFlyingToLatLng())
// {
// Thread.Sleep(100);
// }
//});
}
public async Task FlyToVerticalLineAndMakeCircle(IEnumerable<ICopter> copters, float centerDirection, float radius, bool clockwise = true, bool loop = false, double? verLimit = null)
{
await AllStop().ConfigureAwait(false);
if (!copters.Any()) return;
// 1. 飞到不同的高度。
int i = 0;
var tasks = copters.Select(copter =>
{
var targetAlt = 40 + 5 * i++;
return FlyToAltitudeAsync(copter, targetAlt);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
// 2. 飞到相同经纬度。
var targetLat = copters.Average(c => c.Latitude);
var targetLng = copters.Average(c => c.Longitude);
tasks = copters.Select(copter =>
{
return FlyToLatLngAsync(copter, targetLat, targetLng);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
// 3. 画圈。
await FlyInCircleAsync(copters, centerDirection, radius, clockwise, loop, verLimit);
}
}
class Guide
{
public bool IsLoop { get; set; }
public Func<bool> FinishPredicate { get; set; }
public Action FinishAction { get; set; }
public Action UpdateAction { get; set; }
}
struct CopterState
{
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public float? Altitude { get; set; }
public float? Heading { get; set; }
public double VelocityDirection { get; set; }
public double Velocity { get; set; }
}
static class ICopterExtensions
{
const double DECELERATE_DISTANCE = 5;
internal static void MoveToIdealState(this ICopter copter, CopterState idealState)
{
ushort channel1, channel2;
CalcChannel1And2(copter, idealState, out channel1, out channel2);
ushort channel3 = CalcChannel3(copter, idealState);
//copter.SetChannels(new ChannelBag
//{
// Channel1 = channel1,
// Channel2 = channel2,
// Channel3 = channel3
//});
copter.SetMobileControlAsync(ch1: channel1, ch2: channel2, ch3: channel3, yaw: idealState.Heading);
}
private static void CalcChannel1And2(ICopter copter, CopterState idealState, out ushort channel1, out ushort channel2)
{
if (idealState.Latitude == null || idealState.Longitude == null)
{
channel1 = channel2 = HOVER_CHANNEL;
return;
}
var distance = GeographyUtils.CalcDistance2D(copter.Latitude, copter.Longitude, idealState.Latitude.Value, idealState.Longitude.Value);
var moveDirection = GeographyUtils.CalcDirection2D(copter.Latitude, copter.Longitude, idealState.Latitude.Value, idealState.Longitude.Value)
- copter.Heading.DegToRad();
var coefficient = distance >= DECELERATE_DISTANCE ? 1.0 : distance / DECELERATE_DISTANCE;
var channelDelta1 = MAX_CHANNEL_DELTA * coefficient;
var ch1Delta1 = channelDelta1 * Math.Sin(moveDirection);
var ch2Delta1 = -channelDelta1 * Math.Cos(moveDirection);
var channelDelta2 = MAX_CHANNEL_DELTA * idealState.Velocity / MAX_VEL;
var idealVelDirectionRelativeToHeading = idealState.VelocityDirection - copter.Heading.DegToRad();
var ch1Delta2 = channelDelta2 * Math.Sin(idealVelDirectionRelativeToHeading);
var ch2Delta2 = -channelDelta2 * Math.Cos(idealVelDirectionRelativeToHeading);
var ch1Delta = ch1Delta1 + ch1Delta2;
var ch2Delta = ch2Delta1 + ch2Delta2;
MathUtils.Constrain(ref ch1Delta, -MAX_CHANNEL_DELTA, MAX_CHANNEL_DELTA);
MathUtils.Constrain(ref ch2Delta, -MAX_CHANNEL_DELTA, MAX_CHANNEL_DELTA);
channel1 = (ushort)(ch1Delta > 0 ? MAX_HOVER_CHANNEL + ch1Delta : MIN_HOVER_CHANNEL + ch1Delta);
channel2 = (ushort)(ch2Delta > 0 ? MAX_HOVER_CHANNEL + ch2Delta : MIN_HOVER_CHANNEL + ch2Delta);
}
private static ushort CalcChannel3(ICopter copter, CopterState idealState)
{
if (idealState.Altitude == null)
{
return HOVER_CHANNEL;
}
var distance = idealState.Altitude - copter.Altitude;
var coefficient = distance >= DECELERATE_DISTANCE ? 1.0 : distance / DECELERATE_DISTANCE;
var ch3Delta = MAX_CHANNEL_DELTA * coefficient;
return (ushort)(ch3Delta > 0 ? MAX_HOVER_CHANNEL + ch3Delta : MIN_HOVER_CHANNEL + ch3Delta);
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
[DebuggerDisplay("Lat={Lat}, Lng={Lng}")]
public struct LatLng
{
public LatLng(double lat, double lng)
{
this.Lat = lat;
this.Lng = lng;
}
public static LatLng Empty { get { return new LatLng(-1, -1); } }
public double Lat { get; set; }
public double Lng { get; set; }
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public class MapManager
{
public LatLng Center
{
get { return _centerGetter.Invoke(); }
}
public Views.MapView MapView { get; set; }
private Func<LatLng> _centerGetter;
public void SetCenterGetter(Func<LatLng> centerGetter)
{
_centerGetter = centerGetter;
}
public void ClearCopters()
{
this.MapView.ClearCopters();
}
}
}

View File

@ -0,0 +1,138 @@
using Plane.Logging;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
namespace Plane.FormationCreator.Formation
{
public class MapServer : IDisposable
{
private HttpListener _listner = new HttpListener();
private Dictionary<string, Stream> _resources;
private int _port;
private static ILogger _logger = ServiceLocator.Current.GetInstance<ILogger>();
public static MapServer Create(Dictionary<string, Stream> resources)
{
const int ORIGINAL_PORT = 10240;
int port = ORIGINAL_PORT;
var r = new Random();
MapServer _earthResourcesServer = null;
for (int i = 0; ; ++i)
{
_earthResourcesServer = new MapServer(resources, port);
try
{
_earthResourcesServer.Start();
_earthResourcesServer._port = port;
if (port != ORIGINAL_PORT)
{
using (var reader = new StreamReader(resources["js/earth.js"]))
{
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
writer.Write(reader.ReadToEnd().Replace(ORIGINAL_PORT.ToString(), port.ToString()));
writer.Flush();
resources["js/earth.js"] = ms; // The old one will be closed when the reader is closed.
}
}
return _earthResourcesServer;
}
catch (HttpListenerException ex)
{
_logger.Log(ex);
if (i >= 100)
{
// Alert.Show("找不到空闲端口,建议重启电脑后再试。", "Message", MessageBoxButton.OK);
App.Current.Shutdown();
}
port = r.Next(ORIGINAL_PORT + 1, 65536);
}
}
}
private MapServer(Dictionary<string, Stream> resources, int port)
{
_resources = resources;
_listner.Prefixes.Add("http://localhost:" + port + "/");
}
private string _HomePageUrl;
public string HomePageUrl { get { return _HomePageUrl ?? (_HomePageUrl = $"http://localhost:{_port}/Cesium-1.9/Apps/HelloWorld.html"); } }
public void Start()
{
_listner.Start();
_listner.BeginGetContext(AfterGetContext, null);
}
private void AfterGetContext(IAsyncResult ar)
{
if (!_listner.IsListening) return;
var context = _listner.EndGetContext(ar);
_listner.BeginGetContext(AfterGetContext, null);
var response = context.Response;
string key = context.Request.Url.LocalPath.TrimStart('/').ToLower();
response.ContentType =
key.EndsWith(".html")
? "text/html"
: key.EndsWith(".css")
? "text/css"
: key.EndsWith(".js")
? "application/javascript"
: key.EndsWith(".gltf")
? "application/json"
: key.EndsWith(".jpg")
? "image/jpeg"
: key.EndsWith(".png")
? "image/png"
: "text/plain";
try
{
if (!_resources.ContainsKey(key))
{
response.StatusCode = 404;
response.StatusDescription = "Not Found";
}
else
{
_resources[key].Position = 0;
_resources[key].CopyTo(response.OutputStream);
response.OutputStream.Flush();
}
}
catch (Exception ex)
{
_logger.Log(ex);
response.StatusCode = 500;
response.StatusDescription = "Internal Server Error";
}
finally
{
response.Close();
}
}
public void Stop()
{
_listner.Stop();
}
public void Close()
{
_listner.Close();
}
void IDisposable.Dispose()
{
Close();
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Formation
{
public class PropertyChangedEventArgs<T> : EventArgs
{
public T OldItem { get; set; }
public T NewItem { get; set; }
}
}

View File

@ -0,0 +1,132 @@
<c:MetroWindow x:Class="Plane.FormationCreator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Plane.FormationCreator"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
xmlns:v="clr-namespace:Plane.FormationCreator.Views"
xmlns:vm="clr-namespace:Plane.FormationCreator.ViewModels"
xmlns:fc="clr-namespace:Plane.FormationCreator"
mc:Ignorable="d"
WindowTransitionsEnabled="False"
FontFamily="Microsoft YaHei"
WindowState="Maximized"
Title="无人机编队控制中心-飞行魔方科技"
PreviewKeyDown="MetroWindow_PreviewKeyDown"
PreviewKeyUp="MetroWindow_PreviewKeyUp"
Style="{StaticResource VSWindowStyleKey}"
Width="800"
Height="600">
<c:MetroWindow.Resources>
<Style TargetType="Separator"
BasedOn="{StaticResource {x:Type Separator}}">
<Setter Property="Background"
Value="#555" />
<Setter Property="Margin"
Value="0,10" />
</Style>
</c:MetroWindow.Resources>
<c:MetroWindow.RightWindowCommands>
<c:WindowCommands>
<Button Content="{Binding AppEx.ShowModifyTaskView, Converter={StaticResource ShowModifyTaskViewButtonContentConverter}}"
Command="{Binding ShowOrHideModifyTaskViewCommand}" />
<Button Content="重启监听"
Command="{Binding RestartListeningCommand}" />
<Button Content="{Binding SwitchVelocityModeButtonContent}"
Command="{Binding SwitchVelocityModeCommand}" />
<!--// 林俊清, 20150930, 不分这些模式了。
<Button Content="进入任务模式"
Command="{Binding SwitchAppModeCommand}"
CommandParameter="{x:Static m:AppMode.PreparedForRunningTasks}"
Visibility="{Binding Source={x:Static local:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=SwitchToPreparedForRunningTasksModeButton}" />
<Button Content="进入控制模式"
Command="{Binding SwitchAppModeCommand}"
CommandParameter="{x:Static m:AppMode.ControllingCopters}"
Visibility="{Binding Source={x:Static local:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=SwitchToControllingCoptersModeButton}" />-->
<Button Name="btnGoHome"
Content="回家"
Click="btnGoHome_Click" />
<Button Name="btnRefreshMap"
Content="刷新地图"
Click="btnRefreshMap_Click" />
<Button Name="btnConnect"
Content="连接"
Click="btnConnect_Click" />
</c:WindowCommands>
</c:MetroWindow.RightWindowCommands>
<Grid>
<Grid.Background>
<ImageBrush ImageSource=".\bg.jpg"/>
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Margin="10,20,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75*" />
<ColumnDefinition Width="25*" MinWidth="360" />
</Grid.ColumnDefinitions>
<Grid Margin="0,0,10,0"
Width="Auto" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<v:MapView x:Name="map"
/>
<v:TaskBarView
VerticalAlignment="Bottom"
Visibility="{Binding Source={x:Static local:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=TaskBarView}" />
</Grid>
<Grid Margin="0,0,0,0"
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<v:CopterListView />
<StackPanel Grid.Row="1">
<Separator />
<v:ControlPanelView />
<StackPanel Visibility="{Binding AppEx.ShowModifyTaskView, Converter={StaticResource BooleanToVisibilityConverter}}">
<Separator />
<v:ModifyTaskView />
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="2">
<Separator Grid.ColumnSpan="2" />
<v:CopterInfoView DataContext="{Binding Path=CopterListViewModel.SelectedCopter}" />
</StackPanel>
</Grid>
</Grid>
<Border Grid.Row="1"
Background="{StaticResource HighlightBrush}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="4"
Text="{Binding Message}" />
<TextBlock Margin="4"
Text="{Binding CopterListViewModel.SelectedCopter.StatusText}" />
</StackPanel>
</Border>
</Grid>
</c:MetroWindow>

View File

@ -0,0 +1,308 @@
using Plane.Copters;
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.ViewModels;
using Plane.FormationCreator.Views;
using Plane.Windows.Messages;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.IO;
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.Shapes;
namespace Plane.FormationCreator
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : MetroWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<MainViewModel>();
//ShowConnectDialog();
//if (_copterManager.CoptersForControlling.Count <= 0)
//{
// //App.Current.Shutdown();
//}
}
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
private void ShowConnectDialog(ConnectWindow loginWindow = null)
{
//var result = await this.ShowLoginAsync("登录", "请输入您的帐户 ID 和密码");
if (loginWindow == null)
{
loginWindow = new ConnectWindow();
}
loginWindow.ShowDialog();
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
var loginWindow = new ConnectWindow
{
WindowTransitionsEnabled = false,
WindowStartupLocation = WindowStartupLocation.Manual
};
var point = PointToScreen(Mouse.GetPosition(this));
point.X = Math.Min(point.X, SystemParameters.WorkArea.Width - loginWindow.Width - 30);
point.Y = Math.Min(point.Y, SystemParameters.WorkArea.Height - loginWindow.Height - 30);
loginWindow.Left = point.X;
loginWindow.Top = point.Y;
ShowConnectDialog(loginWindow);
}
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
var connectWindow = new ConnectWindow
{
WindowTransitionsEnabled = false,
WindowStartupLocation = WindowStartupLocation.Manual
};
var point = PointToScreen(Mouse.GetPosition(this));
point.X = Math.Min(point.X, SystemParameters.WorkArea.Width - connectWindow.Width - 30);
point.Y = Math.Min(point.Y, SystemParameters.WorkArea.Height - connectWindow.Height - 30);
connectWindow.Left = point.X;
connectWindow.Top = point.Y;
ShowConnectDialog(connectWindow);
}
private void btnRefreshMap_Click(object sender, RoutedEventArgs e)
{
map.Refresh();
}
private void btnGoHome_Click(object sender, RoutedEventArgs e)
{
map.GoHome();
}
private async void MetroWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
// 如果正在输入,忽略。
if (e.OriginalSource is TextBox || e.OriginalSource is PasswordBox) return;
var isInFastMode = AppEx.Current.IsInFastMode;
switch (e.Key)
{
case Key.W:
case Key.S:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
if (isInFastMode)
{
await copter.SetChannelsAsync(ch2: e.Key == Key.W ? (ushort)1100 : (ushort)1900);
}
else
{
await copter.SetChannelsAsync(ch2: e.Key == Key.W ? (ushort)1350 : (ushort)1650);
}
})).ConfigureAwait(false);
break;
}
case Key.A:
case Key.D:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
if (isInFastMode)
{
await copter.SetChannelsAsync(ch1: e.Key == Key.A ? (ushort)1100 : (ushort)1900);
}
else
{
await copter.SetChannelsAsync(ch1: e.Key == Key.A ? (ushort)1350 : (ushort)1650);
}
})).ConfigureAwait(false);
break;
}
case Key.I:
case Key.K:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
if (isInFastMode)
{
await copter.SetChannelsAsync(ch3: e.Key == Key.K ? (ushort)1100 : (ushort)1900);
}
else
{
await copter.SetChannelsAsync(ch3: e.Key == Key.K ? (ushort)1350 : (ushort)1650);
}
})).ConfigureAwait(false);
break;
}
case Key.J:
case Key.L:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
//await copter.SetMobileControlAsync(yaw: (copter.Yaw + (e.Key == Key.J ? -10 : 10)) % 360);
await copter.SetChannelsAsync(ch4: e.Key == Key.J ? (ushort)1200 : (ushort)1800);
})).ConfigureAwait(false);
break;
}
case Key.M: // 低速下降。
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.SetChannelsAsync(ch3: 1350);
})).ConfigureAwait(false);
break;
}
case Key.N: // 飞行器着陆后,按此键将油门打到 1100.
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.SetChannelsAsync(ch3: 1100);
})).ConfigureAwait(false);
break;
}
case Key.Back: // 锁定。
{
if (Alert.Show("您确定要上锁吗?飞行器将立即停止运转!", "警告", MessageBoxButton.OKCancel, MessageBoxImage.Warning)
== MessageBoxResult.OK)
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.LockAsync();
})).ConfigureAwait(false);
}
break;
}
case Key.P:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
if (isInFastMode)
{
if (e.Key == Key.P)
{
await copter.SetChannelsAsync(ch6: (ushort)1900);
}
}
else
{
if (e.Key == Key.P)
{
await copter.SetChannelsAsync(ch6: (ushort)1900);
}
}
})).ConfigureAwait(false);
break;
}
case Key.O:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
if (isInFastMode)
{
if (e.Key == Key.O)
{
await copter.SetChannelsAsync(ch7: (ushort)1900);
}
}
else
{
if (e.Key == Key.O)
{
await copter.SetChannelsAsync(ch7: (ushort)1900);
}
}
})).ConfigureAwait(false);
break;
}
}
}
private async void MetroWindow_PreviewKeyUp(object sender, KeyEventArgs e)
{
// 如果正在输入,忽略。
if (e.OriginalSource is TextBox || e.OriginalSource is PasswordBox) return;
switch (e.Key)
{
case Key.W:
case Key.S:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.SetChannelsAsync(ch2: 1500);
})).ConfigureAwait(false);
break;
}
case Key.A:
case Key.D:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.SetChannelsAsync(ch1: 1500);
})).ConfigureAwait(false);
break;
}
case Key.I:
case Key.K:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.SetChannelsAsync(ch3: 1500);
})).ConfigureAwait(false);
break;
}
case Key.J:
case Key.L:
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
//await copter.SetMobileControlAsync(yaw: (copter.Yaw + (e.Key == Key.J ? -10 : 10)) % 360);
await copter.SetChannelsAsync(ch4: 1500);
})).ConfigureAwait(false);
break;
}
case Key.M: // 停止低速下降。
{
var copters = _copterManager.AcceptingControlCopters;
await Task.WhenAll(copters.Select(async copter =>
{
await copter.SetChannelsAsync(ch3: 1500);
})).ConfigureAwait(false);
break;
}
}
}
}
}

View File

@ -0,0 +1,121 @@
<c:MetroWindow x:Class="Plane.FormationCreator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Plane.FormationCreator"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
xmlns:v="clr-namespace:Plane.FormationCreator.Views"
xmlns:vm="clr-namespace:Plane.FormationCreator.ViewModels"
xmlns:fc="clr-namespace:Plane.FormationCreator"
mc:Ignorable="d"
WindowTransitionsEnabled="False"
FontFamily="Microsoft YaHei"
WindowState="Maximized"
Title="无人机编队地面控制中心-飞行魔方科技"
PreviewKeyDown="MetroWindow_PreviewKeyDown"
PreviewKeyUp="MetroWindow_PreviewKeyUp"
Style="{StaticResource VSWindowStyleKey}"
Width="800"
Height="600">
<c:MetroWindow.Resources>
<Style TargetType="Separator"
BasedOn="{StaticResource {x:Type Separator}}">
<Setter Property="Background"
Value="#555" />
<Setter Property="Margin"
Value="0,10" />
</Style>
</c:MetroWindow.Resources>
<c:MetroWindow.RightWindowCommands>
<c:WindowCommands>
<Button Content="{Binding AppEx.ShowModifyTaskView, Converter={StaticResource ShowModifyTaskViewButtonContentConverter}}"
Command="{Binding ShowOrHideModifyTaskViewCommand}" />
<Button Content="重启监听"
Command="{Binding RestartListeningCommand}" />
<Button Content="{Binding SwitchVelocityModeButtonContent}"
Command="{Binding SwitchVelocityModeCommand}" />
<!--// 林俊清, 20150930, 不分这些模式了。
<Button Content="进入任务模式"
Command="{Binding SwitchAppModeCommand}"
CommandParameter="{x:Static m:AppMode.PreparedForRunningTasks}"
Visibility="{Binding Source={x:Static local:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=SwitchToPreparedForRunningTasksModeButton}" />
<Button Content="进入控制模式"
Command="{Binding SwitchAppModeCommand}"
CommandParameter="{x:Static m:AppMode.ControllingCopters}"
Visibility="{Binding Source={x:Static local:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=SwitchToControllingCoptersModeButton}" />-->
<Button Name="btnGoHome"
Content="回家"
Click="btnGoHome_Click" />
<Button Name="btnRefreshMap"
Content="刷新地图"
Click="btnRefreshMap_Click" />
<Button Name="btnConnect"
Content="连接"
Click="btnConnect_Click" />
</c:WindowCommands>
</c:MetroWindow.RightWindowCommands>
<Grid>
<Grid.Background>
<ImageBrush ImageSource=".\bg.jpg"/>
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Margin="30,30,30,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid Margin="0,0,10,0"
MinWidth="320"
MaxWidth="360">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<v:CopterListView />
<StackPanel Grid.Row="1">
<Separator />
<v:ControlPanelView />
<StackPanel Visibility="{Binding AppEx.ShowModifyTaskView, Converter={StaticResource BooleanToVisibilityConverter}}">
<Separator />
<v:ModifyTaskView />
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="2">
<Separator Grid.ColumnSpan="2" />
<v:CopterInfoView DataContext="{Binding Path=CopterListViewModel.SelectedCopter}" />
</StackPanel>
</Grid>
<v:MapView x:Name="map"
Grid.Column="1" />
<v:TaskBarView Grid.Column="1"
VerticalAlignment="Bottom"
Visibility="{Binding Source={x:Static local:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=TaskBarView}" />
</Grid>
<Border Grid.Row="1"
Background="{StaticResource HighlightBrush}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="4"
Text="{Binding Message}" />
<TextBlock Margin="4"
Text="{Binding CopterListViewModel.SelectedCopter.StatusText}" />
</StackPanel>
</Border>
</Grid>
</c:MetroWindow>

View File

@ -0,0 +1,23 @@
using Microsoft.Maps.MapControl.WPF;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Maps
{
public class OpenStreetMapTileLayer : MapTileLayer
{
public OpenStreetMapTileLayer()
{
TileSource = new OpenStreetMapTileSource();
}
public string UriFormat
{
get { return TileSource.UriFormat; }
set { TileSource.UriFormat = value; }
}
}
}

View File

@ -0,0 +1,20 @@
using Microsoft.Maps.MapControl.WPF;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator.Maps
{
public class OpenStreetMapTileSource : TileSource
{
public override Uri GetUri(int x, int y, int zoomLevel)
{
return new Uri(UriFormat.
Replace("{x}", x.ToString()).
Replace("{y}", y.ToString()).
Replace("{z}", zoomLevel.ToString()));
}
}
}

View File

@ -0,0 +1,321 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{61E2F31E-220A-4E3F-A64D-F7CDC2135008}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Plane.FormationCreator</RootNamespace>
<AssemblyName>FGCS</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>gcs.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="GalaSoft.MvvmLight, Version=5.2.0.37222, Culture=neutral, PublicKeyToken=e7570ab207bcb616, processorArchitecture=MSIL">
<HintPath>..\packages\MvvmLightLibs.5.2.0.0\lib\net45\GalaSoft.MvvmLight.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="GalaSoft.MvvmLight.Extras, Version=5.2.0.37222, Culture=neutral, PublicKeyToken=669f0b5e8f868abf, processorArchitecture=MSIL">
<HintPath>..\packages\MvvmLightLibs.5.2.0.0\lib\net45\GalaSoft.MvvmLight.Extras.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="GalaSoft.MvvmLight.Platform, Version=5.2.0.37226, Culture=neutral, PublicKeyToken=5f873c45e98af8a1, processorArchitecture=MSIL">
<HintPath>..\packages\MvvmLightLibs.5.2.0.0\lib\net45\GalaSoft.MvvmLight.Platform.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MahApps.Metro, Version=1.2.2.0, Culture=neutral, PublicKeyToken=f4fb5a3c4d1e5b4f, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.1.2.2.0\lib\net45\MahApps.Metro.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MaterialDesignColors, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MaterialDesignColors.1.1.2\lib\net45\MaterialDesignColors.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MaterialDesignThemes.Wpf, Version=1.4.0.473, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MaterialDesignThemes.1.4.0.473\lib\net45\MaterialDesignThemes.Wpf.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Expression.Drawing, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.Maps.MapControl.WPF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Maps.MapControl.WPF.1.0.0.3\lib\net40-Client\Microsoft.Maps.MapControl.WPF.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Practices.ServiceLocation">
<HintPath>..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationFramework.Aero" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.1.2.2.0\lib\net45\System.Windows.Interactivity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="AppEx.cs" />
<Compile Include="AppConfig.cs" />
<Compile Include="Converters\AppModeToVisibilityConverter.cs" />
<Compile Include="Converters\FlightTaskIsSelectedToEffectConverter.cs" />
<Compile Include="Converters\FlightTaskStatusToFillConverter.cs" />
<Compile Include="Converters\HeartbeatCountToBrushConverter.cs" />
<Compile Include="Formation\AppMode.cs" />
<Compile Include="Formation\Copter.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_LoiterTime.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_ReturnToLand.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_SimpleCircle.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_Turn.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_TakeOff.cs" />
<Compile Include="Formation\FlightTask_Circle.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_Circle.cs" />
<Compile Include="Formation\Extensions.cs" />
<Compile Include="Formation\FlightTask.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo.cs" />
<Compile Include="Formation\FlightTaskStatus.cs" />
<Compile Include="Formation\FlightTask_FlyTo.cs" />
<Compile Include="Formation\FlightTaskSingleCopterInfo_FlyTo.cs" />
<Compile Include="Formation\FlightTask_LoiterTime.cs" />
<Compile Include="Formation\FlightTask_ReturnToLand.cs" />
<Compile Include="Formation\FlightTask_SimpleCircle.cs" />
<Compile Include="Formation\FlightTask_Turn.cs" />
<Compile Include="Formation\FormationController.cs" />
<Compile Include="Formation\CopterManager.cs" />
<Compile Include="Formation\LatLng.cs" />
<Compile Include="Formation\MapManager.cs" />
<Compile Include="Formation\MapServer.cs" />
<Compile Include="Formation\CopterStatus.cs" />
<Compile Include="Formation\FlightTaskManager.cs" />
<Compile Include="Formation\PropertyChangedEventArgs.cs" />
<Compile Include="Formation\FlightTask_TakeOff.cs" />
<Compile Include="Maps\OpenStreetMapTileLayer.cs" />
<Compile Include="Maps\OpenStreetMapTileSource.cs" />
<Compile Include="ServiceLocatorConfigurer.cs" />
<Compile Include="Test.cs" />
<Compile Include="ViewModels\ConnectViewModel.cs" />
<Compile Include="ViewModels\ControlPanelViewModel.cs" />
<Compile Include="ViewModels\CopterListViewModel.cs" />
<Compile Include="ViewModels\MainViewModel.cs" />
<Compile Include="ViewModels\MapViewModel.cs" />
<Compile Include="ViewModels\ModifyTaskViewModel.cs" />
<Compile Include="ViewModels\TaskBarViewModel.cs" />
<Compile Include="Views\ControlPanelView.xaml.cs">
<DependentUpon>ControlPanelView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CopterInfoView.xaml.cs">
<DependentUpon>CopterInfoView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CopterListView.xaml.cs">
<DependentUpon>CopterListView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ConnectWindow.xaml.cs">
<DependentUpon>ConnectWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Views\MapView.xaml.cs">
<DependentUpon>MapView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\MapView_CopterDrawing.cs" />
<Compile Include="Views\ModifyTaskView.xaml.cs">
<DependentUpon>ModifyTaskView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TaskBarView.xaml.cs">
<DependentUpon>TaskBarView.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="ClassDiagram1.cd" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Page Include="MainWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\Button.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\Colors.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\ComboBox.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\ProgressButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\TabControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\TextBox.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\Window.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ControlPanelView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\CopterInfoView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\CopterListView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ConnectWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\MapView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModifyTaskView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\TaskBarView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Plane.Libraries\Plane.Logging\Plane.Logging.csproj">
<Project>{9c2cafda-cf1d-4565-b797-398376fcd346}</Project>
<Name>Plane.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plane.Libraries\Plane.Reflection\Plane.Reflection.csproj">
<Project>{98755514-c2e9-4abe-8a25-007804577558}</Project>
<Name>Plane.Reflection</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plane.Libraries\Plane.Windows.Messages\Plane.Windows.Messages.csproj">
<Project>{413C18E2-235F-4E15-B5C1-633657DE5D7A}</Project>
<Name>Plane.Windows.Messages</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plane.Libraries\Plane.Windows\Plane.Windows.csproj">
<Project>{06848293-9b17-4068-9b35-44d0ed713cd4}</Project>
<Name>Plane.Windows</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plane.Libraries\Plane\Plane.csproj">
<Project>{6cce2aeb-3b38-4c00-b32d-433a990ae2ad}</Project>
<Name>Plane</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plane.Sdk3\PlaneGcsSdk.Contract_Private\PlaneGcsSdk.Contract_Private.csproj">
<Project>{47141894-ece3-48ca-8dcf-ca751bda231e}</Project>
<Name>PlaneGcsSdk.Contract_Private</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plane.Sdk3\PlaneGcsSdk_Private_NET46\PlaneGcsSdk_Private_NET46.csproj">
<Project>{0111eb6e-72e3-499c-a3ba-022f5bbc4caf}</Project>
<Name>PlaneGcsSdk_Private_NET46</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="gcs.ico" />
</ItemGroup>
<ItemGroup>
<SplashScreen Include="SplashScreen1.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="bg.jpg" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GroupGCSMain")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GroupGCS")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace Plane.FormationCreator.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.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() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[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("Plane.FormationCreator.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 使用此强类型资源类,为所有资源查找
/// 重写当前线程的 CurrentUICulture 属性。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,38 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace Plane.FormationCreator.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("192.168.1.50")]
public string IPs {
get {
return ((string)(this["IPs"]));
}
set {
this["IPs"] = value;
}
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Plane.FormationCreator.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="IPs" Type="System.String" Scope="User">
<Value Profile="(Default)">192.168.1.50</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@ -0,0 +1,47 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.ViewModels;
using Plane.Logging;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator
{
public class ServiceLocatorConfigurer
{
public static ServiceLocatorConfigurer Instance { get; } = new ServiceLocatorConfigurer();
private ServiceLocatorConfigurer()
{
}
private SimpleIoc _container = SimpleIoc.Default;
public void Configure()
{
ServiceLocator.SetLocatorProvider(() => _container);
_container.Register<MainViewModel>();
_container.Register<ConnectViewModel>();
_container.Register<MapViewModel>();
_container.Register<ControlPanelViewModel>();
_container.Register<CopterListViewModel>();
_container.Register<TaskBarViewModel>();
_container.Register<ModifyTaskViewModel>();
_container.Register<ILogger>(() => new LocalFileLogger(new DebugLogger()));
_container.Register<CopterManager>();
_container.Register<MapManager>();
_container.Register<FlightTaskManager>();
_container.Register<FormationController>();
_container.Register<AppConfig>();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -0,0 +1,47 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Plane.FormationCreator"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:ec="clr-namespace:Plane.Windows.Controls;assembly=Plane.Windows">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/Colors.xaml" />
<ResourceDictionary Source="/Styles/Button.xaml" />
<ResourceDictionary Source="/Styles/ProgressButton.xaml" />
<ResourceDictionary Source="/Styles/TextBox.xaml" />
<ResourceDictionary Source="/Styles/TabControl.xaml" />
<ResourceDictionary Source="/Styles/Window.xaml" />
<ResourceDictionary Source="/Styles/ComboBox.xaml" />
</ResourceDictionary.MergedDictionaries>
<FontFamily x:Key="AlertFontFamily">Microsoft YaHei</FontFamily>
<Style TargetType="PasswordBox"
BasedOn="{StaticResource {x:Type PasswordBox}}">
<Setter Property="Padding"
Value="2" />
</Style>
<Style x:Key="MetroComboBoxItem"
TargetType="{x:Type ComboBoxItem}"
BasedOn="{StaticResource MetroComboBoxItem}">
<Style.Resources>
<SolidColorBrush x:Key="AccentColorBrush"
Color="{StaticResource HighlightColor}" />
<SolidColorBrush x:Key="AccentColorBrush2"
Color="#3F3F46" />
</Style.Resources>
</Style>
<Style TargetType="c:MetroProgressBar"
BasedOn="{StaticResource {x:Type c:MetroProgressBar}}">
<Setter Property="Foreground"
Value="#007ACC" />
</Style>
<Style TargetType="CheckBox"
BasedOn="{StaticResource {x:Type CheckBox}}">
<Style.Resources>
<SolidColorBrush x:Key="CheckBoxBrush"
Color="Gray" />
</Style.Resources>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,89 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button"
BasedOn="{StaticResource StandardButton}">
<Setter Property="Visibility"
Value="Visible" />
<Setter Property="Foreground"
Value="{StaticResource Foreground}" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushNormal}" />
<Setter Property="Padding"
Value="20,5" />
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border SnapsToDevicePixels="True"
BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
ContentTemplate="{TemplateBinding ContentTemplate}"
RecognizesAccessKey="True" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushHighlighted}" />
<Setter Property="Background"
Value="{StaticResource BackgroundHighlighted}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter Property="Background"
Value="{StaticResource BackgroundNormal}" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushNormal}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
<Trigger Property="IsFocused"
Value="true">
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushHighlighted}" />
<Setter Property="Background"
Value="{StaticResource BackgroundHighlighted}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="LinkButton"
TargetType="Button">
<Setter Property="Cursor"
Value="Hand" />
<Setter Property="Foreground"
Value="{StaticResource LinkButtonForeground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock><ContentPresenter /></TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Foreground"
Value="{StaticResource LinkButtonForegroundHighlighted}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,194 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:options="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="options">
<!--Background-->
<SolidColorBrush x:Key="Background"
Color="#1C1C1C" />
<SolidColorBrush x:Key="LightBackground"
Color="#2D2D30" />
<Color x:Key="AccentColor">Transparent</Color>
<Color x:Key="BlackColor">#FFFFFFFF</Color>
<Color x:Key="WhiteColor">#2D2D30</Color>
<Color x:Key="HighlightColor">Transparent</Color>
<!-- re-set brushes too -->
<SolidColorBrush x:Key="HighlightBrush"
Color="{StaticResource HighlightColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush"
Color="{StaticResource AccentColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush2"
Color="{StaticResource AccentColor2}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush3"
Color="{StaticResource AccentColor3}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush4"
Color="{StaticResource AccentColor4}"
options:Freeze="True" />
<SolidColorBrush x:Key="WindowTitleColorBrush"
Color="{StaticResource AccentColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="NonActiveWindowTitleBrush"
Color="Transparent"
options:Freeze="True" />
<SolidColorBrush x:Key="BlackBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="LabelTextBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="BlackColorBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBoxMouseOverInnerBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBoxFocusBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ButtonMouseOverBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ButtonMouseOverInnerBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ComboBoxMouseOverBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ComboBoxMouseOverInnerBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ControlBackgroundBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="WhiteBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="WhiteColorBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="DisabledWhiteBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="WindowBackgroundBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="{x:Static SystemColors.WindowBrushKey}"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<LinearGradientBrush x:Key="CheckBoxBackgroundBrush"
EndPoint="0.5,1"
StartPoint="0.5,0"
options:Freeze="True">
<GradientStop Color="{StaticResource Gray7}"
Offset="0" />
<GradientStop Color="{StaticResource WhiteColor}"
Offset="1" />
</LinearGradientBrush>
<!--Foreground-->
<SolidColorBrush x:Key="Foreground"
Color="#FFFFFF"
options:Freeze="True" />
<SolidColorBrush x:Key="WindowTitleForeground"
Color="#999988"
options:Freeze="True" />
<!--General-->
<SolidColorBrush x:Key="BackgroundHighlighted"
Color="#54545C"
options:Freeze="True" />
<SolidColorBrush x:Key="BorderBrushHighlighted"
Color="#6A6A75"
options:Freeze="True" />
<SolidColorBrush x:Key="BackgroundSelected"
Color="#007ACC"
options:Freeze="True" />
<SolidColorBrush x:Key="BorderBrushSelected"
Color="#1C97EA"
options:Freeze="True" />
<SolidColorBrush x:Key="BackgroundNormal"
Color="#3F3F46"
options:Freeze="True" />
<SolidColorBrush x:Key="BorderBrushNormal"
Color="#54545C"
options:Freeze="True" />
<!--<SolidColorBrush x:Key="WindowGlowBrush" Color="#017ACC" options:Freeze="True" />-->
<SolidColorBrush x:Key="WindowGlowBrush"
Color="#007ACC"
options:Freeze="True" />
<!--Text Box-->
<SolidColorBrush x:Key="TextBoxBackground"
Color="#333337"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBoxBackgroundSelected"
Color="#3F3F46"
options:Freeze="True" />
<!--Search Text Box-->
<SolidColorBrush x:Key="SearchTextForeground"
Color="#999999"
options:Freeze="True" />
<!--Link Button-->
<SolidColorBrush x:Key="LinkButtonForeground"
Color="#1297FB"
options:Freeze="True" />
<SolidColorBrush x:Key="LinkButtonForegroundHighlighted"
Color="#55AAFF"
options:Freeze="True" />
<!--Close Button-->
<SolidColorBrush x:Key="CloseButtonBackgroundHighlighted"
Color="#39ADFB"
options:Freeze="True" />
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
Color="#084E7D"
options:Freeze="True" />
<SolidColorBrush x:Key="CloseButtonStroke"
Color="#AAFFFFFF"
options:Freeze="True" />
<SolidColorBrush x:Key="CloseButtonStrokeHighlighted"
Color="#FFFFFF"
options:Freeze="True" />
<!--Menu-->
<SolidColorBrush x:Key="MenuSeparatorBorderBrush"
Color="#333337"
options:Freeze="True" />
<SolidColorBrush x:Key="MenuItemHighlightedBackground"
Color="#3E3E40"
options:Freeze="True" />
<SolidColorBrush x:Key="SubmenuItemBackground"
Color="#1B1B1C"
options:Freeze="True" />
<SolidColorBrush x:Key="SubmenuItemBackgroundHighlighted"
Color="#333334"
options:Freeze="True" />
<SolidColorBrush x:Key="MenuDisabledForeground"
Color="#656565"
options:Freeze="True" />
<!--Scroll Bar-->
<SolidColorBrush x:Key="ScrollBarPageButtonBackgroundHighlighted"
Color="#05FFFFFF"
options:Freeze="True" />
</ResourceDictionary>

View File

@ -0,0 +1,328 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls">
<Style TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Background"
Value="#252525" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushNormal}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Grid.Resources>
<Style x:Key="comboToggleStyle"
TargetType="{x:Type ToggleButton}">
<Setter Property="Foreground"
Value="{DynamicResource TextBrush}" />
<Setter Property="Background"
Value="{DynamicResource TextBoxBackground}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrushNormal}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Padding"
Value="3" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="MouseOverBorder">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="PressedBorder">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="FocusRectangle">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Background"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<Rectangle x:Name="DisabledVisualElement"
Fill="{DynamicResource WhiteBrush}"
IsHitTestVisible="false"
Opacity="0" />
<Border x:Name="MouseOverBorder"
Background="{DynamicResource TextBoxBackgroundSelected}"
BorderThickness="{TemplateBinding BorderThickness}"
Opacity="0" />
<Border x:Name="PressedBorder"
Background="{DynamicResource TextBoxBackgroundSelected}"
BorderThickness="{TemplateBinding BorderThickness}"
Opacity="0" />
<Rectangle x:Name="FocusRectangle"
Stroke="{DynamicResource BorderBrushHighlighted}"
Opacity="0" />
<Rectangle x:Name="FocusVisualElement"
IsHitTestVisible="false"
Margin="1"
Stroke="{DynamicResource HighlightBrush}"
StrokeThickness="1"
Visibility="Collapsed" />
<Grid Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_ClearText"
Grid.Column="1"
FontSize="16"
Style="{DynamicResource ChromelessButtonStyle}"
Foreground="{TemplateBinding Foreground}"
FontFamily="Marlett"
Content="r"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}, Path=(c:TextBoxHelper.ClearTextButton), Converter={StaticResource BooleanToVisibilityConverter}}"
IsTabStop="False" />
<Rectangle x:Name="BtnArrowBackground"
Grid.Column="2"
Fill="Transparent"
StrokeThickness="0" />
<Path x:Name="BtnArrow"
Grid.Column="2"
IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "
HorizontalAlignment="Center"
Height="4"
Stretch="Uniform"
Width="8"
Fill="{DynamicResource GrayBrush1}" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="BtnArrowBackground"
Property="IsMouseOver"
Value="True">
<Setter TargetName="BtnArrowBackground"
Property="Fill"
Value="{DynamicResource GrayBrush2}" />
</Trigger>
<Trigger SourceName="FocusVisualElement"
Property="Visibility"
Value="Visible">
<Setter TargetName="BtnArrowBackground"
Property="Fill"
Value="{DynamicResource HighlightBrush}" />
</Trigger>
<Trigger SourceName="PART_ClearText"
Property="IsMouseOver"
Value="True">
<Setter TargetName="PART_ClearText"
Property="Background"
Value="{DynamicResource GrayBrush8}" />
<Setter TargetName="PART_ClearText"
Property="Foreground"
Value="{DynamicResource AccentColorBrush}" />
</Trigger>
<Trigger SourceName="PART_ClearText"
Property="IsPressed"
Value="True">
<Setter TargetName="PART_ClearText"
Property="Background"
Value="{DynamicResource BlackBrush}" />
<Setter TargetName="PART_ClearText"
Property="Foreground"
Value="{DynamicResource WhiteBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="DisabledVisualElement">
<SplineDoubleKeyFrame KeyTime="00:00:00"
Value="0.45" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="FocusRectangle">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="FocusedDropDown">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="00:00:00"
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="PopupBorder">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
</Grid.ColumnDefinitions>
<ToggleButton x:Name="PART_DropDownToggle"
Grid.ColumnSpan="3"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Margin="0"
Style="{DynamicResource comboToggleStyle}"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Stretch"
KeyboardNavigation.IsTabStop="False" />
<Border x:Name="EditableTextBoxBorder"
Visibility="Collapsed"
BorderBrush="Transparent"
BorderThickness="1,1,0,1"
Margin="0,0,-2,0"
Background="{TemplateBinding Background}">
<TextBox x:Name="PART_EditableTextBox"
HorizontalAlignment="Stretch"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
BorderThickness="0"
Background="{x:Null}"
Margin="0,0,0,-2"
c:TextBoxHelper.Watermark="{TemplateBinding c:TextBoxHelper.Watermark}"
Foreground="{TemplateBinding Foreground}"
Focusable="True"
IsReadOnly="{TemplateBinding IsReadOnly}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}" />
</Border>
<Grid x:Name="ContentSite"
Margin="7 0 0 0">
<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Content="{TemplateBinding SelectionBoxItem}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
IsHitTestVisible="false"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Grid>
<Rectangle x:Name="FocusRectangle"
Stroke="{DynamicResource BorderBrushSelected}"
Opacity="0" />
<Rectangle x:Name="DisabledVisualElement"
Fill="{DynamicResource DisabledWhiteBrush}"
IsHitTestVisible="false"
Opacity="0" />
<!-- AllowsTransparency="true" fixes the redraw problem under windows vista/7 with a selected non aero theme -->
<Popup x:Name="PART_Popup"
AllowsTransparency="True"
Focusable="False"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
Placement="Bottom">
<Grid MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{Binding MaxDropDownHeight, RelativeSource={RelativeSource TemplatedParent}}">
<Border x:Name="PopupBorder"
Effect="{DynamicResource DropShadowBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
HorizontalAlignment="Stretch"
Height="Auto"
BorderBrush="{DynamicResource BorderBrushNormal}"
Background="{DynamicResource WhiteBrush}">
</Border>
<ScrollViewer x:Name="DropDownScrollViewer"
BorderThickness="0"
Margin="2"
Padding="1">
<ItemsPresenter x:Name="ItemsPresenter"
KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(c:TextBoxHelper.ClearTextButton)}"
Value="False">
<Setter TargetName="EditableTextBoxBorder"
Property="Grid.ColumnSpan"
Value="2" />
<Setter TargetName="ContentSite"
Property="Grid.ColumnSpan"
Value="2" />
</DataTrigger>
<Trigger Property="IsEditable"
Value="True">
<Setter Property="IsTabStop"
Value="false" />
<Setter TargetName="EditableTextBoxBorder"
Property="Visibility"
Value="Visible" />
<Setter TargetName="ContentSite"
Property="Visibility"
Value="Hidden" />
<Setter TargetName="PART_DropDownToggle"
Property="Focusable"
Value="False" />
<!-- #1037 : don't know why we set this to transparent ???
<Setter TargetName="PART_DropDownToggle"
Property="Background"
Value="Transparent" /> -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,67 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:ec="clr-namespace:Plane.Windows.Controls;assembly=Plane.Windows">
<Style TargetType="ec:ProgressButton"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Padding"
Value="20,0" />
<Setter Property="IsEnabled"
Value="{Binding IsProcessing, RelativeSource={RelativeSource Self}, Converter={StaticResource InverseBooleanConverter}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ec:ProgressButton">
<Border SnapsToDevicePixels="True"
BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="0,5"
ContentTemplate="{TemplateBinding ContentTemplate}"
RecognizesAccessKey="True" />
<c:ProgressRing Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="5,0,0,0"
IsActive="True"
Width="8"
Height="8"
Foreground="{StaticResource HighlightBrush}"
Visibility="{TemplateBinding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushHighlighted}" />
<Setter Property="Background"
Value="{StaticResource BackgroundHighlighted}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter Property="Background"
Value="{StaticResource BackgroundSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushSelected}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,80 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type TabControl}">
<Style.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{StaticResource Foreground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Height="30"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="1"
Margin="10,0,10,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="false">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Background"
Value="{DynamicResource BorderBrushSelected}" />
</Trigger>
<Trigger Property="IsSelected"
Value="true">
<Setter Property="Background"
Value="{DynamicResource BorderBrushSelected}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Background="Transparent"
BorderBrush="{DynamicResource BackgroundSelected}"
BorderThickness="0,0,0,3">
<TabPanel Name="HeaderPanel"
Margin="0,0,4,-1"
Panel.ZIndex="1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1" />
</Border>
<Border Grid.Row="1"
Background="Transparent" />
<ContentPresenter Name="PART_SelectedContentHost"
Grid.Row="1"
ContentSource="SelectedContent" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,42 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Padding"
Value="2" />
<Setter Property="BorderBrush"
Value="{StaticResource TextBoxBackgroundSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<Border x:Name="Highlight"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<ScrollViewer x:Name="PART_ContentHost"
Margin="1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Background"
Value="{StaticResource TextBoxBackgroundSelected}" />
</Trigger>
<Trigger Property="IsFocused"
Value="true">
<Setter Property="Background"
Value="{StaticResource TextBoxBackgroundSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource HighlightBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,16 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls">
<Style TargetType="c:MetroWindow"
BasedOn="{StaticResource VSWindowStyleKey}"
x:Key="VSWindowStyleKey">
<Setter Property="NonActiveWindowTitleBrush"
Value="Transparent" />
</Style>
<Style TargetType="c:MetroWindow"
BasedOn="{StaticResource VSWindowStyleKey}"
x:Key="AlertWindowStyle" />
</ResourceDictionary>

View File

@ -0,0 +1,86 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button"
BasedOn="{StaticResource StandardButton}">
<Setter Property="Visibility"
Value="Visible" />
<Setter Property="Foreground"
Value="{StaticResource Foreground}" />
<Setter Property="Background"
Value="{StaticResource BackgroundNormal}" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushNormal}" />
<Setter Property="Padding"
Value="20,5" />
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border SnapsToDevicePixels="True"
BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
ContentTemplate="{TemplateBinding ContentTemplate}"
RecognizesAccessKey="True" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushHighlighted}" />
<Setter Property="Background"
Value="{StaticResource BackgroundHighlighted}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter Property="Background"
Value="{StaticResource BackgroundSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushSelected}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
<Trigger Property="IsFocused"
Value="true">
<Setter Property="BorderBrush"
Value="{DynamicResource HighlightBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="LinkButton"
TargetType="Button">
<Setter Property="Cursor"
Value="Hand" />
<Setter Property="Foreground"
Value="{StaticResource LinkButtonForeground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock><ContentPresenter /></TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Foreground"
Value="{StaticResource LinkButtonForegroundHighlighted}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,194 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:options="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="options">
<!--Background-->
<SolidColorBrush x:Key="Background"
Color="#1C1C1C" />
<SolidColorBrush x:Key="LightBackground"
Color="#2D2D30" />
<Color x:Key="AccentColor">#2D2D30</Color>
<Color x:Key="BlackColor">#FFFFFFFF</Color>
<Color x:Key="WhiteColor">#2D2D30</Color>
<Color x:Key="HighlightColor">#007ACC</Color>
<!-- re-set brushes too -->
<SolidColorBrush x:Key="HighlightBrush"
Color="{StaticResource HighlightColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush"
Color="{StaticResource AccentColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush2"
Color="{StaticResource AccentColor2}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush3"
Color="{StaticResource AccentColor3}"
options:Freeze="True" />
<SolidColorBrush x:Key="AccentColorBrush4"
Color="{StaticResource AccentColor4}"
options:Freeze="True" />
<SolidColorBrush x:Key="WindowTitleColorBrush"
Color="{StaticResource AccentColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="NonActiveWindowTitleBrush"
Color="Transparent"
options:Freeze="True" />
<SolidColorBrush x:Key="BlackBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="LabelTextBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="BlackColorBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBoxMouseOverInnerBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBoxFocusBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ButtonMouseOverBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ButtonMouseOverInnerBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ComboBoxMouseOverBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ComboBoxMouseOverInnerBorderBrush"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="ControlBackgroundBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="WhiteBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="WhiteColorBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="DisabledWhiteBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="WindowBackgroundBrush"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="{x:Static SystemColors.WindowBrushKey}"
Color="{StaticResource WhiteColor}"
options:Freeze="True" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}"
Color="{StaticResource BlackColor}"
options:Freeze="True" />
<LinearGradientBrush x:Key="CheckBoxBackgroundBrush"
EndPoint="0.5,1"
StartPoint="0.5,0"
options:Freeze="True">
<GradientStop Color="{StaticResource Gray7}"
Offset="0" />
<GradientStop Color="{StaticResource WhiteColor}"
Offset="1" />
</LinearGradientBrush>
<!--Foreground-->
<SolidColorBrush x:Key="Foreground"
Color="#FFFFFF"
options:Freeze="True" />
<SolidColorBrush x:Key="WindowTitleForeground"
Color="#999988"
options:Freeze="True" />
<!--General-->
<SolidColorBrush x:Key="BackgroundHighlighted"
Color="#54545C"
options:Freeze="True" />
<SolidColorBrush x:Key="BorderBrushHighlighted"
Color="#6A6A75"
options:Freeze="True" />
<SolidColorBrush x:Key="BackgroundSelected"
Color="#007ACC"
options:Freeze="True" />
<SolidColorBrush x:Key="BorderBrushSelected"
Color="#1C97EA"
options:Freeze="True" />
<SolidColorBrush x:Key="BackgroundNormal"
Color="#3F3F46"
options:Freeze="True" />
<SolidColorBrush x:Key="BorderBrushNormal"
Color="#54545C"
options:Freeze="True" />
<!--<SolidColorBrush x:Key="WindowGlowBrush" Color="#017ACC" options:Freeze="True" />-->
<SolidColorBrush x:Key="WindowGlowBrush"
Color="#007ACC"
options:Freeze="True" />
<!--Text Box-->
<SolidColorBrush x:Key="TextBoxBackground"
Color="#333337"
options:Freeze="True" />
<SolidColorBrush x:Key="TextBoxBackgroundSelected"
Color="#3F3F46"
options:Freeze="True" />
<!--Search Text Box-->
<SolidColorBrush x:Key="SearchTextForeground"
Color="#999999"
options:Freeze="True" />
<!--Link Button-->
<SolidColorBrush x:Key="LinkButtonForeground"
Color="#1297FB"
options:Freeze="True" />
<SolidColorBrush x:Key="LinkButtonForegroundHighlighted"
Color="#55AAFF"
options:Freeze="True" />
<!--Close Button-->
<SolidColorBrush x:Key="CloseButtonBackgroundHighlighted"
Color="#39ADFB"
options:Freeze="True" />
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
Color="#084E7D"
options:Freeze="True" />
<SolidColorBrush x:Key="CloseButtonStroke"
Color="#AAFFFFFF"
options:Freeze="True" />
<SolidColorBrush x:Key="CloseButtonStrokeHighlighted"
Color="#FFFFFF"
options:Freeze="True" />
<!--Menu-->
<SolidColorBrush x:Key="MenuSeparatorBorderBrush"
Color="#333337"
options:Freeze="True" />
<SolidColorBrush x:Key="MenuItemHighlightedBackground"
Color="#3E3E40"
options:Freeze="True" />
<SolidColorBrush x:Key="SubmenuItemBackground"
Color="#1B1B1C"
options:Freeze="True" />
<SolidColorBrush x:Key="SubmenuItemBackgroundHighlighted"
Color="#333334"
options:Freeze="True" />
<SolidColorBrush x:Key="MenuDisabledForeground"
Color="#656565"
options:Freeze="True" />
<!--Scroll Bar-->
<SolidColorBrush x:Key="ScrollBarPageButtonBackgroundHighlighted"
Color="#05FFFFFF"
options:Freeze="True" />
</ResourceDictionary>

View File

@ -0,0 +1,328 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls">
<Style TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Background"
Value="#252525" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushNormal}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Grid.Resources>
<Style x:Key="comboToggleStyle"
TargetType="{x:Type ToggleButton}">
<Setter Property="Foreground"
Value="{DynamicResource TextBrush}" />
<Setter Property="Background"
Value="{DynamicResource TextBoxBackground}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrushNormal}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Padding"
Value="3" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="MouseOverBorder">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="PressedBorder">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="FocusRectangle">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Background"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<Rectangle x:Name="DisabledVisualElement"
Fill="{DynamicResource WhiteBrush}"
IsHitTestVisible="false"
Opacity="0" />
<Border x:Name="MouseOverBorder"
Background="{DynamicResource TextBoxBackgroundSelected}"
BorderThickness="{TemplateBinding BorderThickness}"
Opacity="0" />
<Border x:Name="PressedBorder"
Background="{DynamicResource TextBoxBackgroundSelected}"
BorderThickness="{TemplateBinding BorderThickness}"
Opacity="0" />
<Rectangle x:Name="FocusRectangle"
Stroke="{DynamicResource BorderBrushHighlighted}"
Opacity="0" />
<Rectangle x:Name="FocusVisualElement"
IsHitTestVisible="false"
Margin="1"
Stroke="{DynamicResource HighlightBrush}"
StrokeThickness="1"
Visibility="Collapsed" />
<Grid Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_ClearText"
Grid.Column="1"
FontSize="16"
Style="{DynamicResource ChromelessButtonStyle}"
Foreground="{TemplateBinding Foreground}"
FontFamily="Marlett"
Content="r"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}, Path=(c:TextBoxHelper.ClearTextButton), Converter={StaticResource BooleanToVisibilityConverter}}"
IsTabStop="False" />
<Rectangle x:Name="BtnArrowBackground"
Grid.Column="2"
Fill="Transparent"
StrokeThickness="0" />
<Path x:Name="BtnArrow"
Grid.Column="2"
IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "
HorizontalAlignment="Center"
Height="4"
Stretch="Uniform"
Width="8"
Fill="{DynamicResource GrayBrush1}" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="BtnArrowBackground"
Property="IsMouseOver"
Value="True">
<Setter TargetName="BtnArrowBackground"
Property="Fill"
Value="{DynamicResource GrayBrush2}" />
</Trigger>
<Trigger SourceName="FocusVisualElement"
Property="Visibility"
Value="Visible">
<Setter TargetName="BtnArrowBackground"
Property="Fill"
Value="{DynamicResource HighlightBrush}" />
</Trigger>
<Trigger SourceName="PART_ClearText"
Property="IsMouseOver"
Value="True">
<Setter TargetName="PART_ClearText"
Property="Background"
Value="{DynamicResource GrayBrush8}" />
<Setter TargetName="PART_ClearText"
Property="Foreground"
Value="{DynamicResource AccentColorBrush}" />
</Trigger>
<Trigger SourceName="PART_ClearText"
Property="IsPressed"
Value="True">
<Setter TargetName="PART_ClearText"
Property="Background"
Value="{DynamicResource BlackBrush}" />
<Setter TargetName="PART_ClearText"
Property="Foreground"
Value="{DynamicResource WhiteBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="DisabledVisualElement">
<SplineDoubleKeyFrame KeyTime="00:00:00"
Value="0.45" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="FocusRectangle">
<EasingDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="FocusedDropDown">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="00:00:00"
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="PopupBorder">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight, Mode=OneWay}" />
</Grid.ColumnDefinitions>
<ToggleButton x:Name="PART_DropDownToggle"
Grid.ColumnSpan="3"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Margin="0"
Style="{DynamicResource comboToggleStyle}"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Stretch"
KeyboardNavigation.IsTabStop="False" />
<Border x:Name="EditableTextBoxBorder"
Visibility="Collapsed"
BorderBrush="Transparent"
BorderThickness="1,1,0,1"
Margin="0,0,-2,0"
Background="{TemplateBinding Background}">
<TextBox x:Name="PART_EditableTextBox"
HorizontalAlignment="Stretch"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
BorderThickness="0"
Background="{x:Null}"
Margin="0,0,0,-2"
c:TextBoxHelper.Watermark="{TemplateBinding c:TextBoxHelper.Watermark}"
Foreground="{TemplateBinding Foreground}"
Focusable="True"
IsReadOnly="{TemplateBinding IsReadOnly}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}" />
</Border>
<Grid x:Name="ContentSite"
Margin="7 0 0 0">
<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Content="{TemplateBinding SelectionBoxItem}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
IsHitTestVisible="false"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Grid>
<Rectangle x:Name="FocusRectangle"
Stroke="{DynamicResource BorderBrushSelected}"
Opacity="0" />
<Rectangle x:Name="DisabledVisualElement"
Fill="{DynamicResource DisabledWhiteBrush}"
IsHitTestVisible="false"
Opacity="0" />
<!-- AllowsTransparency="true" fixes the redraw problem under windows vista/7 with a selected non aero theme -->
<Popup x:Name="PART_Popup"
AllowsTransparency="True"
Focusable="False"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
Placement="Bottom">
<Grid MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{Binding MaxDropDownHeight, RelativeSource={RelativeSource TemplatedParent}}">
<Border x:Name="PopupBorder"
Effect="{DynamicResource DropShadowBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
HorizontalAlignment="Stretch"
Height="Auto"
BorderBrush="{DynamicResource BorderBrushNormal}"
Background="{DynamicResource WhiteBrush}">
</Border>
<ScrollViewer x:Name="DropDownScrollViewer"
BorderThickness="0"
Margin="2"
Padding="1">
<ItemsPresenter x:Name="ItemsPresenter"
KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(c:TextBoxHelper.ClearTextButton)}"
Value="False">
<Setter TargetName="EditableTextBoxBorder"
Property="Grid.ColumnSpan"
Value="2" />
<Setter TargetName="ContentSite"
Property="Grid.ColumnSpan"
Value="2" />
</DataTrigger>
<Trigger Property="IsEditable"
Value="True">
<Setter Property="IsTabStop"
Value="false" />
<Setter TargetName="EditableTextBoxBorder"
Property="Visibility"
Value="Visible" />
<Setter TargetName="ContentSite"
Property="Visibility"
Value="Hidden" />
<Setter TargetName="PART_DropDownToggle"
Property="Focusable"
Value="False" />
<!-- #1037 : don't know why we set this to transparent ???
<Setter TargetName="PART_DropDownToggle"
Property="Background"
Value="Transparent" /> -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,67 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:ec="clr-namespace:Plane.Windows.Controls;assembly=Plane.Windows">
<Style TargetType="ec:ProgressButton"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Padding"
Value="20,0" />
<Setter Property="IsEnabled"
Value="{Binding IsProcessing, RelativeSource={RelativeSource Self}, Converter={StaticResource InverseBooleanConverter}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ec:ProgressButton">
<Border SnapsToDevicePixels="True"
BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="0,5"
ContentTemplate="{TemplateBinding ContentTemplate}"
RecognizesAccessKey="True" />
<c:ProgressRing Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="5,0,0,0"
IsActive="True"
Width="8"
Height="8"
Foreground="{StaticResource HighlightBrush}"
Visibility="{TemplateBinding IsProcessing, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushHighlighted}" />
<Setter Property="Background"
Value="{StaticResource BackgroundHighlighted}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter Property="Background"
Value="{StaticResource BackgroundSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource BorderBrushSelected}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,80 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type TabControl}">
<Style.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{StaticResource Foreground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Height="30"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="1"
Margin="10,0,10,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="false">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Background"
Value="{DynamicResource BorderBrushSelected}" />
</Trigger>
<Trigger Property="IsSelected"
Value="true">
<Setter Property="Background"
Value="{DynamicResource BackgroundSelected}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Background="Transparent"
BorderBrush="{DynamicResource BackgroundSelected}"
BorderThickness="0,0,0,3">
<TabPanel Name="HeaderPanel"
Margin="0,0,4,-1"
Panel.ZIndex="1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1" />
</Border>
<Border Grid.Row="1"
Background="{DynamicResource Background}" />
<ContentPresenter Name="PART_SelectedContentHost"
Grid.Row="1"
ContentSource="SelectedContent" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,42 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Padding"
Value="2" />
<Setter Property="BorderBrush"
Value="{StaticResource TextBoxBackgroundSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<Border x:Name="Highlight"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<ScrollViewer x:Name="PART_ContentHost"
Margin="1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Background"
Value="{StaticResource TextBoxBackgroundSelected}" />
</Trigger>
<Trigger Property="IsFocused"
Value="true">
<Setter Property="Background"
Value="{StaticResource TextBoxBackgroundSelected}" />
<Setter Property="BorderBrush"
Value="{StaticResource HighlightBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,16 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls">
<Style TargetType="c:MetroWindow"
BasedOn="{StaticResource VSWindowStyleKey}"
x:Key="VSWindowStyleKey">
<Setter Property="NonActiveWindowTitleBrush"
Value="Transparent" />
</Style>
<Style TargetType="c:MetroWindow"
BasedOn="{StaticResource VSWindowStyleKey}"
x:Key="AlertWindowStyle" />
</ResourceDictionary>

View File

@ -0,0 +1,52 @@
using Plane.Communication;
using Plane.Copters;
using Plane.FormationCreator.Formation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plane.FormationCreator
{
public class Test
{
public Test(CopterManager copterManager)
{
_copterManager = copterManager;
}
private CopterManager _copterManager;
private ICopter[] _copters = new FakeCopter[]
{
//new FakeCopter(23.14973333, 113.40974166) { Id = "C1", Name = "C1", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500, Voltage = 10.6f },
//new FakeCopter(23.14973333, 113.40984166) { Id = "C2", Name = "C2", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500, Voltage = 12.6f },
//new FakeCopter(23.14973333, 113.40994166) { Id = "C3", Name = "C3", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500 },
//new FakeCopter(23.14973333, 113.41004166) { Id = "C4", Name = "C4", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500 },
//new FakeCopter(23.14973333, 113.41014166) { Id = "C5", Name = "C5", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500 },
//new FakeCopter(23.14973333, 113.41024166) { Id = "C6", Name = "C6", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500 },
//new FakeCopter(23.14973333, 113.41034166) { Id = "C7", Name = "C7", Channel1 = 1500, Channel2 = 1500, Channel3 = 1500, Channel4 = 1500 }
};
public Test Prepare()
{
foreach (var copter in _copters)
{
_copterManager.Copters.Add(copter);
}
return this;
}
public void Run()
{
foreach (var copter in _copters)
{
copter.ConnectAsync();
copter.GetCopterDataAsync();
}
//await FormationController.Instance.FlyInCircleAsync(_copterManager.Copters, 280, 15, clockwise: false);
//await FormationController.Instance.FlyToAltitudeAsync(_copterManager.Copters, 50);
}
}
}

View File

@ -0,0 +1,176 @@
using Plane;
using Plane.Collections;
using Plane.FormationCreator.Formation;
using Plane.Logging;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Plane.Communication;
using Plane.Copters;
using Plane.Windows.Messages;
using Plane.FormationCreator.Properties;
using System.IO;
namespace Plane.FormationCreator.ViewModels
{
public class ConnectViewModel : ViewModelBase
{
public ConnectViewModel(CopterManager copterManager)
{
_copterManager = copterManager;
}
private CopterManager _copterManager;
private const int PORT = 5250;
// 不严谨,稍微验证一下。
private Regex _ipListRegex = new Regex(@"^(?:" + Environment.NewLine + @")*\d{1,3}(?:\.\d{1,3}){3}(?:" + Environment.NewLine + @"\d{1,3}(?:\.\d{1,3}){3})*(?:" + Environment.NewLine + @")*$", RegexOptions.Singleline | RegexOptions.Compiled);
// private string _IPs = @"192.168.1.11
//192.168.1.12
//192.168.1.13
//192.168.1.14
//192.168.1.15
//192.168.1.16
//192.168.1.17
//192.168.1.18
//192.168.1.19
//192.168.1.20
//";
private string _IPs = Settings.Default.IPs;
public string IPs
{
get { return _IPs; }
set { Set(nameof(IPs), ref _IPs, value); }
}
private bool _IsProcessing;
public bool IsProcessing
{
get { return _IsProcessing; }
set { Set(nameof(IsProcessing), ref _IsProcessing, value); }
}
private ICommand _ConnectCommand;
public ICommand ConnectCommand
{
get
{
return _ConnectCommand ?? (_ConnectCommand = new RelayCommand<string>(async connectionType =>
{
if (!_ipListRegex.IsMatch(IPs))
{
Alert.Show($"您输入的 IP 格式不对!形如{Environment.NewLine}192.168.1.10{Environment.NewLine}192.168.1.11");
return;
}
IsProcessing = true;
var connectionStatusDict =
IPs.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Where(ip => _copterManager.Copters.All(c => c.Id != ip))
.OrderBy(ip => ip)
.Distinct()
.ToDictionary(ip => ip, ip => (bool?)null);
var timeIsOut = false;
var uiThreadContext = SynchronizationContext.Current;
new Action(async () =>
{
foreach (var ip in connectionStatusDict.Keys.ToList())
{
var Connection =
connectionType == "SerialPort"
? new SerialPortConnection("COM10") as IConnection
: connectionType == "UDP"
? new UdpConnection(ip, PORT) as IConnection
: new TcpConnection(ip, PORT);
var c = new Copter(Connection, uiThreadContext)
//var c = new Copter(new SerialPortConnection("COM3"), uiThreadContext)
{
Id = ip,
Name = ip.Substring(ip.LastIndexOf('.') + 1)
};
try
{
await c.ConnectAsync().ConfigureAwait(false);
if (!timeIsOut)
{
connectionStatusDict[ip] = true;
App.Current.Dispatcher.Invoke(() =>
{
_copterManager.Copters.Add(c);
});
//await c.GetCopterDataAsync();
}
}
catch (SocketException)
{
connectionStatusDict[ip] = false;
}
catch (IOException)
{ // A device attached to the system is not functioning.
connectionStatusDict[ip] = false;
}
}
}).Invoke();
for (int waitedTime = 0, timeStep = 500; waitedTime < 2000; waitedTime += timeStep)
{
await Task.Delay(timeStep).ConfigureAwait(false);
if (connectionStatusDict.All(item => item.Value == true))
{
break;
}
}
timeIsOut = true;
IsProcessing = false;
var ipsCanNotConnect = string.Join(Environment.NewLine, connectionStatusDict.Where(item => !(item.Value ?? false)).Select(item => item.Key));
if (!string.IsNullOrEmpty(ipsCanNotConnect))
{
var message = $"无法连接以下 IP{Environment.NewLine}{ipsCanNotConnect}";
App.Current.Dispatcher.Invoke(() =>
{
Alert.Show(message);
});
}
else
{
CloseWindow();
}
}));
}
}
private Action _closeWindowCallback;
public void SetCloseWindowAction(Action closeWindowAction)
{
_closeWindowCallback = closeWindowAction;
}
private void CloseWindow()
{
if (_closeWindowCallback == null) return;
var dispatcher = App.Current.Dispatcher;
if (dispatcher.CheckAccess())
{
_closeWindowCallback();
}
else
{
dispatcher.InvokeAsync(() =>
{
_closeWindowCallback.Invoke();
});
}
}
}
}

View File

@ -0,0 +1,464 @@
using Plane.Copters;
using Plane.FormationCreator.Formation;
using Plane.Geography;
using Plane.Logging;
using Plane.Windows.Messages;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using Microsoft.Practices.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Plane.FormationCreator.ViewModels
{
public class ControlPanelViewModel : ViewModelBase
{
public ControlPanelViewModel(FormationController formationController, CopterManager copterManager)
{
_formationController = formationController;
_copterManager = copterManager;
}
private FormationController _formationController;
private CopterManager _copterManager;
private ILogger _logger = ServiceLocator.Current.GetInstance<ILogger>();
private float _AltP = 0.5f;
public float AltP
{
get { return _AltP; }
set { Set(nameof(AltP), ref _AltP, value); }
}
private ICommand _UnlockCommand;
public ICommand UnlockCommand
{
get
{
return _UnlockCommand ?? (_UnlockCommand = new RelayCommand(async () =>
{
// await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.UnlockAsync()));
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select( async c => {
await c.UnlockAsync();
for (int i = 0; !c.IsUnlocked; i++)
{
if (i % (1000 / 25) == 1000 / 25 - 1)
{
await c.UnlockAsync(); // 每 1000 毫秒重试一次。
}
await Task.Delay(25).ConfigureAwait(false);
}
}));
}));
}
}
private ICommand _LockCommand;
public ICommand LockCommand
{
get
{
return _LockCommand ?? (_LockCommand = new RelayCommand(async () =>
{
if (Alert.Show("您确定要上锁吗?飞行器将立即停止运转!", "警告", MessageBoxButton.OKCancel, MessageBoxImage.Warning)
== MessageBoxResult.OK)
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.LockAsync()));
}
}));
}
}
private ICommand _TakeOffCommand;
public ICommand TakeOffCommand
{
get
{
return _TakeOffCommand ?? (_TakeOffCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.TakeOffAsync()));
}));
}
}
private ICommand _HoverCommand;
public ICommand HoverCommand
{
get
{
return _HoverCommand ?? (_HoverCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.HoverAsync()));
}));
}
}
private ICommand _FloatCommand;
public ICommand FloatCommand
{
get
{
return _FloatCommand ?? (_FloatCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.FloatAsync()));
}));
}
}
private ICommand _ReturnToLaunchCommand;
public ICommand ReturnToLaunchCommand
{
get
{
return _ReturnToLaunchCommand ?? (_ReturnToLaunchCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.ReturnToLaunchAsync()));
}));
}
}
private ICommand _LandCommand;
public ICommand LandCommand
{
get
{
return _LandCommand ?? (_LandCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.LandAsync()));
}));
}
}
private ICommand _ConnectCommand;
public ICommand ConnectCommand
{
get
{
return _ConnectCommand ?? (_ConnectCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(async copter =>
{
await copter.ConnectAsync();
await copter.GetCopterDataAsync();
}));
}));
}
}
private ICommand _DisconnectCommand;
public ICommand DisconnectCommand
{
get
{
return _DisconnectCommand ?? (_DisconnectCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter => copter.DisconnectAsync()));
}));
}
}
private ICommand _FlyToAltitudeCommand;
public ICommand FlyToAltitudeCommand
{
get
{
return _FlyToAltitudeCommand ?? (_FlyToAltitudeCommand = new RelayCommand<float>(async alt =>
{
await _formationController.FlyToAltitudeAsync(_copterManager.AcceptingControlCopters, targetAlt: alt).ConfigureAwait(false);
}));
}
}
private ICommand _FlyInCircleCommand;
public ICommand FlyInCircleCommand
{
get
{
return _FlyInCircleCommand ?? (_FlyInCircleCommand = new RelayCommand(async () =>
{
await _formationController.FlyInCircleAsync(_copterManager.AcceptingControlCopters, centerDirection: 180, radius: 10, loop: true).ConfigureAwait(false);
}));
}
}
private ICommand _FlyAroundCenterOfCoptersCommand;
public ICommand FlyAroundCenterOfCoptersCommand
{
get
{
return _FlyAroundCenterOfCoptersCommand ?? (_FlyAroundCenterOfCoptersCommand = new RelayCommand(async () =>
{
await _formationController.FlyAroundCenterOfCoptersAsync(_copterManager.AcceptingControlCopters).ConfigureAwait(false);
}));
}
}
private ICommand _FlyInRectangleCommand;
public ICommand FlyInRectangleCommand
{
get
{
return _FlyInRectangleCommand ?? (_FlyInRectangleCommand = new RelayCommand(async () =>
{
await _formationController.FlyInRectangleAsync(_copterManager.AcceptingControlCopters, startDirection: 180, sideLength1: 15, sideLength2: 5, loop: true).ConfigureAwait(false);
}));
}
}
private ICommand _FlyToVerticalLineAndMakeCircleCommand;
public ICommand FlyToVerticalLineAndMakeCircleCommand
{
get
{
return _FlyToVerticalLineAndMakeCircleCommand ?? (_FlyToVerticalLineAndMakeCircleCommand = new RelayCommand(async () =>
{
await _formationController.FlyToVerticalLineAndMakeCircle(
_copterManager.AcceptingControlCopters,
centerDirection: 180,
radius: 10
).ConfigureAwait(false);
}));
}
}
private ICommand _FlagCommand;
public ICommand FlagCommand
{
get
{
return _FlagCommand ?? (_FlagCommand = new RelayCommand(async () =>
{
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(copter =>
{
var copters = _copterManager.Copters;
int index = copters.IndexOf(copter);
_copterManager.CopterStatus[index] = true;
return Task.Run(()=> { });
}));
}));
}
}
private ICommand _TestCommand;
public ICommand TestCommand
{
get
{
return _TestCommand ?? (_TestCommand = new RelayCommand(async () =>
{
var firstCopter = _copterManager.Copters.First();
// Test TurnAsync.
//var latLng = GeographyUtils.CalcLatLngSomeMetersAway(firstCopter.Latitude, firstCopter.Longitude, 135, 10);
//await _formationController.TurnAsync(_copterManager.AcceptingControlCopters, latLng.Item1, latLng.Item2);
// Test FlyToLatLngAsync.
//var copters = _copterManager.AcceptingControlCopters;
//var centerLat = copters.Average(c => c.Latitude);
//var centerLng = copters.Average(c => c.Longitude);
//var latDelta = 10 * GeographyUtils.MetersToLocalLat;
//var lngDelta = 10 * GeographyUtils.GetMetersToLocalLon(copters.First().Latitude);
//var targetLat = centerLat + latDelta;
//var targetLng = centerLng + lngDelta;
//await _formationController.FlyToLatLngAsync(copters, targetLat, targetLng);
// Test FlyInCircleAsync.
//await _formationController.FlyInCircleAsync(_copterManager.AcceptingControlCopters, centerDirection: 180, radius: 10, loop: false);
// Test FlyAroundCenterOfCoptersAsync.
//await _formationController.FlyAroundCenterOfCoptersAsync(_copterManager.AcceptingControlCopters);
// Test FlyToLatLngAsync.
//var targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway(firstCopter.Latitude, firstCopter.Longitude, 135, 20);
//await _formationController.FlyToLatLngAsync(firstCopter, targetLatLng.Item1, targetLatLng.Item2);
//firstCopter.LoiterTurns(1, 10);
//var radius = await firstCopter.GetParamAsync("CIRCLE_RADIUS");
////await firstCopter.SetParamAsync("CIRCLE_RADIUS", 1500);
////var param = await firstCopter.GetParamAsync("CIRCLE_RADIUS");
//// Alert.Show($"半径设置成功啦: {param}");
////await firstCopter.SetParamAsync("CIRCLE_RATE", 30);
//var rate = await firstCopter.GetParamAsync("CIRCLE_RATE");
//Alert.Show($"半径: {radius} 角速度: {rate}");
//await firstCopter.SetMobileControlAsync(yaw: 90);
//if (!_listeningMissionItemReceived)
//{
// firstCopter.MissionItemReceived += (sender, e) => Alert.Show($"{e.Index} {e.Total} {(Protocols.MAVLink.MAV_CMD)e.Details.id}");
// _listeningMissionItemReceived = true;
//}
//await firstCopter.RequestMissionListAsync();
var copter = firstCopter;
///* 写航线 */
//// 任务总数。
//await copter.SetMissionCountAsync(6).ConfigureAwait(false);
//// 起飞前准备。
//await copter.WritePreTakeOffMissionAsync().ConfigureAwait(false);
//// 起飞。
//await copter.WriteTakeOffMissionAsync().ConfigureAwait(false);
//// 不断自增的编号。
//ushort index = 2;
//// 航点。
//await copter.WriteWaypointMissionAsync(new EHLocation(copter).AddLatLngAlt(0.001, 0.002, 2), index++).ConfigureAwait(false);
//// 航点。
//await copter.WriteWaypointMissionAsync(new EHLocation(copter).AddLatLngAlt(0.003, 0.002, 2), index++).ConfigureAwait(false);
//// 返航。
//await copter.WriteReturnToLaunchMissionAsync(index++).ConfigureAwait(false);
//// 降落。
//await copter.WriteLandMissionAsync(index++).ConfigureAwait(false);
///* 读航线 */
// 监听“任务项已接收”事件。
//(copter as EHCopter).MissionItemReceived += (sender, e) => Alert.Show($"{e.Mission.Sequence} {e.Mission.Latitude} {e.Mission.Command}");
// 请求任务列表。
var missions = await copter.RequestMissionListAsync();
// Alert.Show(string.Join(Environment.NewLine, missions.Select(m => $"{m.Sequence}\t{m.Command}\t\t{m.Latitude}")));
//ICopter previousCopter = firstCopter;
//foreach (var item in _copterManager.Copters.Where(c => c != firstCopter))
//{
// item.Follow(previousCopter, keepYawDifference: false, keepFacingTarget: false);
// previousCopter = item;
//}
//var missions = new Mission[]
//{
// Mission.CreateWaypointMission(new EHLocation(firstCopter).AddLatLngAlt(0.0001, 0, 10)),
// Mission.CreateReturnToLaunchMission(),
// Mission.CreateLandMission()
//};
//var result = await firstCopter.WriteMissionsAsync(missions);
//var result2 = await firstCopter.WriteMissionsAsync(missions);
//Debug.WriteLine($"{result1} {result2}");
//var missions = new Mission[]
//{
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateWaypointMission(23.123, 113.123, 10),
// Mission.CreateReturnToLaunchMission(),
// Mission.CreateLandMission()
//};
//var result = await firstCopter.WriteMissionListAsync(missions);
//MessageBox.Show($"The result of WriteMissions: {result}");
// 跟随测试
/*
ICopter previousCopter = firstCopter;
foreach (var item in _copterManager.Copters.Where(c => c != firstCopter))
{
item.Follow(previousCopter, keepYawDifference: false, keepFacingTarget: false);
previousCopter = item;
}
*/
await Task.WhenAll(_copterManager.AcceptingControlCopters.Select(async c =>
{
float gpsLed = await c.GetParamAsync("NOTI_GPSLED");
if (gpsLed == 0.0f)
{
await LEDFlashAsync(c, true);
}
else
{
await LEDFlashAsync(c, false);
}
// for (int jj = 0; jj < 5; jj++)
{
// await LEDFlashAsync(c, true);
// await c.SetParamAsync("THR_ALT_P", AltP);
// await c.SetParamAsync("WPNAV_ACCEL_Z", AltP);
// await c.SetParamAsync("WPNAV_ACCEL", AltP);
// await c.SetParamAsync("WPNAV_SPEED", AltP);
// await c.SetParamAsync("WPNAV_SPEED_DN", AltP);
// await c.SetParamAsync("WPNAV_SPEED_UP", AltP);
// await LEDFlashAsync(c, false);
// await LEDFlashAsync(c, true);
// await Task.Delay(500).ConfigureAwait(false);
// await Task.Delay(1000).ConfigureAwait(false);
// await LEDFlashAsync(c, true);
// await Task.Delay(1000).ConfigureAwait(false);
}
}));
}));
}
}
private async Task LEDFlashAsync(ICopter copter, bool isOn)
{
// float gpsLed = await c.GetParamAsync("NOTI_GPSLED");
float ledControl = 0.0f;
if (isOn)
{
ledControl = 1.0f;
}
var tasks = new Task[2];
//tasks[0] = copter.SetParamAsync("NOTI_GPSLED", ledControl);
//tasks[1] = copter.SetParamAsync("NOTI_ARMLED", ledControl);
tasks[0] = Task.Run(async () =>
{
try
{
await copter.SetParamAsync("NOTI_GPSLED", ledControl, 5000);
}
catch (TimeoutException e)
{
_logger.Log($"NOTI_GPSLED 超时, CopterId: {copter.Id}。");
}
});
tasks[1] = Task.Run(async () =>
{
try
{
await copter.SetParamAsync("NOTI_ARMLED", ledControl, 5000);
}
catch (TimeoutException e)
{
_logger.Log($"NOTI_ARMLED 超时, CopterId: {copter.Id}。");
}
});
await Task.WhenAll(tasks).ConfigureAwait(false);
}
private bool _listeningMissionItemReceived;
private ICommand _StopTaskCommand;
public ICommand StopTaskCommand
{
get
{
return _StopTaskCommand ?? (_StopTaskCommand = new RelayCommand(async () =>
{
await _formationController.AllStop().ConfigureAwait(false);
}));
}
}
}
}

View File

@ -0,0 +1,100 @@
using Plane.Copters;
using Plane;
using Plane.FormationCreator.Formation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Threading;
using Plane.Communication;
namespace Plane.FormationCreator.ViewModels
{
public class CopterListViewModel : ViewModelBase
{
public CopterListViewModel(CopterManager copterManager, MapManager mapManager, FlightTaskManager flightTaskManager)
{
_copterManager = copterManager;
_mapManager = mapManager;
_flightTaskManager = flightTaskManager;
}
private CopterManager _copterManager;
private MapManager _mapManager;
private FlightTaskManager _flightTaskManager;
public CopterManager CopterManager { get { return _copterManager; } }
private ICopter _SelectedCopter;
public ICopter SelectedCopter
{
get { return _SelectedCopter; }
set { Set(nameof(SelectedCopter), ref _SelectedCopter, value); }
}
private int _virtualCopterId = 1;
private LatLng? _lastVirtualCopterLocation;
private ICommand _AddVirtualCopterCommand;
public ICommand AddVirtualCopterCommand
{
get
{
return _AddVirtualCopterCommand ?? (_AddVirtualCopterCommand = new RelayCommand<int>(async count =>
{
var center = _mapManager.Center;
string id;
for (int i = 0; i < count; ++i, ++_virtualCopterId)
{
id = _virtualCopterId.ToString();
_lastVirtualCopterLocation =
_lastVirtualCopterLocation == null ?
new LatLng(center.Lat, center.Lng) :
new LatLng(_lastVirtualCopterLocation.Value.Lat, _lastVirtualCopterLocation.Value.Lng + 0.0001);
var copter = new FakeCopter(SynchronizationContext.Current);
copter.SetProperties(
latitude: _lastVirtualCopterLocation.Value.Lat,
longitude: _lastVirtualCopterLocation.Value.Lng,
id: id,
name: id
);
await copter.ConnectAsync();
await copter.GetCopterDataAsync();
_copterManager.Copters.Add(copter);
_copterManager.CopterStatus.Add(false);
}
}));
}
}
private ICommand _ClearCoptersCommand;
public ICommand ClearCoptersCommand
{
get
{
return _ClearCoptersCommand ?? (_ClearCoptersCommand = new RelayCommand(async () =>
{
foreach (var copter in _copterManager.Copters)
{
await copter.DisconnectAsync();
}
await Task.Delay(100); // 如果不等待一段时间,很可能会再触发 DataStreamReceived 事件导致飞行器重新出现在地图上。
_copterManager.Copters.Clear();
_copterManager.CopterStatus.Clear();
_mapManager.ClearCopters();
_flightTaskManager.ClearTasks();
UdpServerConnectionManager.Instance.ClearConnections();
_virtualCopterId = 1;
_lastVirtualCopterLocation = null;
}));
}
}
}
}

View File

@ -0,0 +1,121 @@
using Plane.Communication;
using Plane.FormationCreator.Formation;
using Plane.Windows.Messages;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Plane.FormationCreator.ViewModels
{
public class MainViewModel : ViewModelBase
{
public MainViewModel(CopterListViewModel copterListViewModel)
{
_copterListViewModel = copterListViewModel;
Plane.Windows.Messages.Message.Configure(showAction: msg => this.Message = msg);
this.SwitchVelocityModeButtonContent = GetSwitchVelocityModeButtonContent();
AppEx.Current.PropertyChanged += AppEx_PropertyChanged;
}
private CopterListViewModel _copterListViewModel;
public CopterListViewModel CopterListViewModel { get { return _copterListViewModel; } }
public AppEx AppEx { get; } = AppEx.Current;
private void AppEx_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(AppEx.IsInFastMode):
this.SwitchVelocityModeButtonContent = GetSwitchVelocityModeButtonContent();
break;
default:
break;
}
}
private string _SwitchVelocityModeButtonContent;
public string SwitchVelocityModeButtonContent
{
get { return _SwitchVelocityModeButtonContent; }
set { Set(nameof(SwitchVelocityModeButtonContent), ref _SwitchVelocityModeButtonContent, value); }
}
private string _Message;
public string Message
{
get { return _Message; }
set { Set(nameof(Message), ref _Message, value); }
}
private ICommand _RestartListeningCommand;
public ICommand RestartListeningCommand
{
get
{
return _RestartListeningCommand ?? (_RestartListeningCommand = new RelayCommand(() =>
{
TcpServerConnectionManager.Instance.StopListening();
if (TcpServerConnectionManager.Instance.StartListening())
{
Alert.Show("已重启对连接请求的监听。");
}
else
{
Alert.Show("网络连接不正常,无法启动监听。");
}
UdpServerConnectionManager.Instance.StopReceiving();
UdpServerConnectionManager.Instance.StartReceiving();
}));
}
}
private ICommand _SwitchVelocityModeCommand;
public ICommand SwitchVelocityModeCommand
{
get
{
return _SwitchVelocityModeCommand ?? (_SwitchVelocityModeCommand = new RelayCommand(() =>
{
AppEx.Current.IsInFastMode = !AppEx.Current.IsInFastMode;
}));
}
}
private ICommand _SwitchAppModeCommand;
public ICommand SwitchAppModeCommand
{
get
{
return _SwitchAppModeCommand ?? (_SwitchAppModeCommand = new RelayCommand<AppMode>(mode =>
{
AppEx.Current.AppMode = mode;
}));
}
}
private ICommand _ShowOrHideModifyTaskViewCommand;
public ICommand ShowOrHideModifyTaskViewCommand
{
get
{
return _ShowOrHideModifyTaskViewCommand ?? (_ShowOrHideModifyTaskViewCommand = new RelayCommand(() =>
{
AppEx.Current.ShowModifyTaskView = !AppEx.Current.ShowModifyTaskView;
}));
}
}
private string GetSwitchVelocityModeButtonContent()
=> AppEx.Current.IsInFastMode ? "高速模式" : "低速模式";
}
}

View File

@ -0,0 +1,44 @@
using Plane.FormationCreator.Formation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Plane.FormationCreator.ViewModels
{
public class MapViewModel : ViewModelBase
{
public MapViewModel()
{
//var server = MapServer.Create(Reflection.Utils.GetResourcesUnder("views/map"));
//server.Start();
//HomePageUrl = server.HomePageUrl;
//App.Current.Exit += (s, e) =>
//{
// server.Stop();
// server.Close();
//};
HomePageUrl = "http://localhost:8080/Apps/HelloWorld.html";
//HomePageUrl = "http://localhost:8080/index-old.html";
//HomePageUrl = "http://www.google.com/maps";
}
public string HomePageUrl { get; private set; }
private ICommand _StartMeasuringDistanceCommand;
public ICommand StartMeasuringDistanceCommand
{
get
{
return _StartMeasuringDistanceCommand ?? (_StartMeasuringDistanceCommand = new RelayCommand(() =>
{
AppEx.Current.AppMode = AppMode.MeasuringDistance;
}));
}
}
}
}

View File

@ -0,0 +1,212 @@
using Plane.Collections;
using Plane.Copters;
using Plane.FormationCreator.Formation;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Plane.FormationCreator.ViewModels
{
public class ModifyTaskViewModel : ViewModelBase
{
public ModifyTaskViewModel(FlightTaskManager flightTaskManager)
{
_flightTaskManager = flightTaskManager;
//_flightTaskManager.SingleCopterInfoChanged += (sender, e) =>
//{
// var changedSingleInfo = e.ChangedSingleCopterInfo;
// if (changedSingleInfo.Copter == _copterManager.SelectedCopters.FirstOrDefault())
// {
// if (changedSingleInfo is FlyToFlightTaskSingleCopterInfo)
// {
// FlyToLat = changedSingleInfo.TargetLat;
// FlyToLng = changedSingleInfo.TargetLng;
// FlyToAlt = changedSingleInfo.TargetAlt;
// }
// }
//};
// TODO: 林俊清, 20150803, 没什么问题就把这段以及 FlyToLat 等属性删了。
//_flightTaskManager.PropertyChanged += (sender, e) =>
//{
// switch (e.PropertyName)
// {
// case nameof(FlightTaskManager.SelectedTask):
// var currentCopter = _copterManager.SelectedCopters.FirstOrDefault();
// if (currentCopter != null)
// {
// var selectedTask = _flightTaskManager.SelectedTask;
// if (selectedTask != null)
// {
// var singleCopterInfo = selectedTask.SingleCopterInfos.Find(i => i.Copter == currentCopter);
// if (singleCopterInfo is FlyToFlightTaskSingleCopterInfo)
// {
// var info = singleCopterInfo as FlyToFlightTaskSingleCopterInfo;
// this.FlyToLat = info.TargetLat;
// this.FlyToLng = info.TargetLng;
// this.FlyToAlt = info.TargetAlt;
// }
// }
// }
// break;
// default:
// break;
// }
//};
//_copterManager.SelectedCoptersChanged += (sender, e) =>
//{
// // TODO: 林俊清, 20150731, 处理多选飞行器时的情况。
// var selectedCopters = _copterManager.SelectedCopters;
// if (selectedCopters.Count() > 1)
// {
// CanModifySingleCopterInfo = false;
// return;
// }
// CanModifySingleCopterInfo = true;
// var currentCopter = e.AddedCopters.FirstOrDefault();
// if (currentCopter != null)
// {
// var selectedTask = _flightTaskManager.SelectedTask;
// if (selectedTask != null)
// {
// var singleCopterInfo = selectedTask.SingleCopterInfos.Find(i => i.Copter == currentCopter);
// if (singleCopterInfo is FlyToFlightTaskSingleCopterInfo)
// {
// var info = singleCopterInfo as FlyToFlightTaskSingleCopterInfo;
// this.FlyToLat = info.TargetLat;
// this.FlyToLng = info.TargetLng;
// this.FlyToAlt = info.TargetAlt;
// }
// }
// }
//};
//this.PropertyChanged += (sender, e) =>
//{
// switch (e.PropertyName)
// {
// case nameof(FlyToLat):
// InvokeForSelectedSingleCopterInfos(info => info.TargetLat = FlyToLat);
// break;
// case nameof(FlyToLng):
// InvokeForSelectedSingleCopterInfos(info => info.TargetLng = FlyToLng);
// break;
// case nameof(FlyToAlt):
// InvokeForSelectedSingleCopterInfos(info => info.TargetAlt = FlyToAlt);
// break;
// case nameof(StaggerRoutes):
// {
// var modifyingTask = _flightTaskManager.SelectedTask as FlyToFlightTask;
// if (modifyingTask != null)
// {
// modifyingTask.StaggerAlt = this.StaggerRoutes;
// }
// }
// break;
// default:
// break;
// }
//};
}
private FlightTaskManager _flightTaskManager;
public FlightTaskManager FlightTaskManager { get { return _flightTaskManager; } }
//private void InvokeForSelectedSingleCopterInfos(Action<FlightTaskSingleCopterInfo> action)
//{
// var modifyingTask = _flightTaskManager.SelectedTask as FlyToFlightTask;
// if (modifyingTask != null && _copterManager.SelectedCopters.Count() == 1) // 林俊清, 20150731, 目前不处理多选的情况。
// {
// modifyingTask.FlyToFlightTaskSingleCopterInfos
// .Where(i => _copterManager.SelectedCopters.FirstOrDefault() == i.Copter)
// .ForEach(action);
// // TODO: 林俊清, 20150731, 添加同时改多个 SingleCopterInfo 的程序。
// //.Where(i => _copterManager.SelectedCopters.Contains(i.Copter))
// }
//}
private bool _CanModifySingleCopterInfo = true;
public bool CanModifySingleCopterInfo
{
get { return _CanModifySingleCopterInfo; }
set { Set(nameof(CanModifySingleCopterInfo), ref _CanModifySingleCopterInfo, value); }
}
private double _FlyToLat;
public double FlyToLat
{
get { return _FlyToLat; }
set { Set(nameof(FlyToLat), ref _FlyToLat, value); }
}
private double _FlyToLng;
public double FlyToLng
{
get { return _FlyToLng; }
set { Set(nameof(FlyToLng), ref _FlyToLng, value); }
}
private float _FlyToAlt;
public float FlyToAlt
{
get { return _FlyToAlt; }
set { Set(nameof(FlyToAlt), ref _FlyToAlt, value); }
}
private bool _StaggerRoutes;
public bool StaggerRoutes
{
get { return _StaggerRoutes; }
set { Set(nameof(StaggerRoutes), ref _StaggerRoutes, value); }
}
private ICommand _ExportTasksCommand;
public ICommand ExportTasksCommand
{
get
{
return _ExportTasksCommand ?? (_ExportTasksCommand = new RelayCommand(() =>
{
var exportedText = _flightTaskManager.ExportTasks();
var dialog = new SaveFileDialog
{
DefaultExt = "fcg",
Filter = "编队飞行任务 (*.fcg)|*.fcg"
};
if (dialog.ShowDialog() == true)
{
File.WriteAllText(dialog.FileName, exportedText);
}
}));
}
}
private ICommand _ImportTasksCommand;
public ICommand ImportTasksCommand
{
get
{
return _ImportTasksCommand ?? (_ImportTasksCommand = new RelayCommand(() =>
{
var dialog = new OpenFileDialog
{
DefaultExt = "fcg",
Filter = "编队飞行任务 (*.fcg)|*.fcg"
};
if (dialog.ShowDialog() == true)
{
var tasksText = File.ReadAllText(dialog.FileName);
_flightTaskManager.ImportTasks(tasksText);
}
}));
}
}
}
}

View File

@ -0,0 +1,108 @@
using Plane.FormationCreator.Formation;
using Plane.Windows.Messages;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Plane.FormationCreator.ViewModels
{
public class TaskBarViewModel : ViewModelBase
{
public TaskBarViewModel(FlightTaskManager flightTaskManager)
{
_flightTaskManager = flightTaskManager;
}
private FlightTaskManager _flightTaskManager;
public FlightTaskManager FlightTaskManager { get { return _flightTaskManager; } }
public ObservableCollection<FlightTask> Tasks { get { return _flightTaskManager.Tasks; } }
private ICommand _AddTaskCommand;
public ICommand AddTaskCommand
{
get
{
return _AddTaskCommand ?? (_AddTaskCommand = new RelayCommand(() =>
{
_flightTaskManager.AddTask();
}));
}
}
private ICommand _ClearTasksCommand;
public ICommand ClearTasksCommand
{
get
{
return _ClearTasksCommand ?? (_ClearTasksCommand = new RelayCommand(() =>
{
_flightTaskManager.ClearTasks();
}));
}
}
private ICommand _ResetTasksCommand;
public ICommand ResetTasksCommand
{
get
{
return _ResetTasksCommand ?? (_ResetTasksCommand = new RelayCommand(() =>
{
_flightTaskManager.ResetTasks();
}));
}
}
//private ICommand _CancelModifyingTaskCommand;
//public ICommand CancelModifyingTaskCommand
//{
// get
// {
// return _CancelModifyingTaskCommand ?? (_CancelModifyingTaskCommand = new RelayCommand(() =>
// {
// AppEx.Instance.CurrentMode = AppMode.PreparedForRunningTasks;
// }));
// }
//}
private ICommand _RunTasksCommand;
public ICommand RunTasksCommand
{
get
{
return _RunTasksCommand ?? (_RunTasksCommand = new RelayCommand(async () =>
{
// string s = JsonConvert.SerializeObject(_flightTaskManager.Tasks, new JsonSerializerSettings
// {
// TypeNameHandling = TypeNameHandling.Objects
// });
// Alert.Show(s);
// return;
Message.Show("任务开始");
await _flightTaskManager.RunAsync();
Message.Show(_flightTaskManager.IsPaused == true ? "任务暂停" : "任务完成");
}));
}
}
private ICommand _PauseTasksCommand;
public ICommand PauseTasksCommand
{
get
{
return _PauseTasksCommand ?? (_PauseTasksCommand = new RelayCommand(() =>
{
_flightTaskManager.Pause();
}));
}
}
}
}

View File

@ -0,0 +1,123 @@
<c:MetroWindow x:Class="Plane.FormationCreator.Views.ConnectWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:c="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:ec="clr-namespace:Plane.Windows.Controls;assembly=Plane.Windows"
mc:Ignorable="d"
Title="连接"
Width="422"
Height="339"
Style="{StaticResource VSWindowStyleKey}"
WindowStartupLocation="CenterScreen"
FontFamily="Microsoft YaHei"
ResizeMode="NoResize">
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.Resources>
<Style TargetType="Label"
BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="HorizontalAlignment"
Value="Right" />
<Setter Property="VerticalAlignment"
Value="Center" />
</Style>
<Style TargetType="ComboBox"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="HorizontalAlignment"
Value="Left" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Width"
Value="150" />
</Style>
<Style TargetType="TextBox"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="HorizontalAlignment"
Value="Left" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Width"
Value="150" />
</Style>
<Style TargetType="PasswordBox"
BasedOn="{StaticResource {x:Type PasswordBox}}">
<Setter Property="HorizontalAlignment"
Value="Left" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="MinWidth"
Value="150" />
</Style>
<Style TargetType="Button"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="VerticalAlignment"
Value="Center" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Target="{Binding ElementName=txtIPs}"
Content="IP" />
<TextBox Name="txtIPs"
Grid.Column="2"
AcceptsReturn="True"
MinHeight="200"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{Binding IPs, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Grid.Row="1"
Grid.ColumnSpan="3">
<ec:ProgressButton HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="10"
Content="连接 _TCP"
IsDefault="True"
IsProcessing="{Binding IsProcessing}"
Command="{Binding Path=ConnectCommand}" />
<ec:ProgressButton HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="10"
Content="连接 _UDP"
IsDefault="True"
IsProcessing="{Binding IsProcessing}"
Command="{Binding Path=ConnectCommand}"
CommandParameter="UDP"/>
<ec:ProgressButton HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="10"
Content="连接串口"
IsProcessing="{Binding IsProcessing}"
Command="{Binding Path=ConnectCommand}"
CommandParameter="SerialPort" />
<!--<ec:ProgressButton Name="btnConnectUdp"
Grid.Row="1"
Grid.ColumnSpan="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="10"
Content="连接 _UDP"
IsDefault="True"
IsProcessing="{Binding IsProcessing}"
Command="{Binding Path=ConnectCommand}"
CommandParameter="UDP" />-->
</StackPanel>
</Grid>
</c:MetroWindow>

View File

@ -0,0 +1,44 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.ViewModels;
using MahApps.Metro.Controls;
using Microsoft.Practices.ServiceLocation;
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.Shapes;
namespace Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for LoginWindow.xaml
/// </summary>
public partial class ConnectWindow : MetroWindow
{
public ConnectWindow()
{
this.DataContextChanged += (sender, e) =>
{
var vm = e.NewValue as ConnectViewModel;
if (vm != null)
{
vm.SetCloseWindowAction(this.Close);
}
};
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<ConnectViewModel>();
txtIPs.Focus();
}
}
}

View File

@ -0,0 +1,93 @@
<UserControl x:Class="Plane.FormationCreator.Views.ControlPanelView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:fc="clr-namespace:Plane.FormationCreator"
xmlns:ec="clr-namespace:Plane.Copters;assembly=PlaneGcsSdk_Private_NET46"
xmlns:vm="clr-namespace:Plane.FormationCreator.ViewModels"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Margin"
Value="0,0,5,5" />
</Style>
</StackPanel.Resources>
<!--<TextBlock Text="连接" />
<WrapPanel>
<Button Content="连接"
Command="{Binding ConnectCommand}" />
<Button Content="断开"
Command="{Binding DisconnectCommand}" />
</WrapPanel>-->
<TextBlock Text="基础控制" />
<WrapPanel>
<Button Content="解锁"
Command="{Binding UnlockCommand}" />
<Button Content="起飞"
Command="{Binding TakeOffCommand}" />
<Button Content="悬停"
Command="{Binding HoverCommand}" />
<Button Content="手动"
Command="{Binding FloatCommand}" />
<Button Content="标记"
Command="{Binding FlagCommand}" />
</WrapPanel>
<WrapPanel>
<Button Content="返航"
Command="{Binding ReturnToLaunchCommand}" />
<Button Content="降落"
Command="{Binding LandCommand}" />
<Button Content="上锁"
Command="{Binding LockCommand}" />
<Button Content="Test"
Command="{Binding TestCommand}" />
<TextBox Width="50" Text="{Binding AltP, UpdateSourceTrigger=PropertyChanged}" />
</WrapPanel>
<!--// 林俊清, 20150920, 目前不再使用 FormationController删除相关按钮。
<StackPanel Visibility="{Binding Source={x:Static fc:AppEx.Current}, Path=AppMode, Converter={StaticResource AppModeToVisibilityConverter}, ConverterParameter=ControlPanelView_Formation}">
<TextBlock Text="任务" />
<WrapPanel>
<Button Content="飞到50米高"
Command="{Binding FlyToAltitudeCommand}"
CommandParameter="50" />
<Button Content="飞到15米高"
Command="{Binding FlyToAltitudeCommand}"
CommandParameter="15" />
<Button Content="Test"
Command="{Binding TestCommand}" />
</WrapPanel>
-->
<!--<WrapPanel>
<Button Content="画圈"
Command="{Binding FlyInCircleCommand}" />
<Button Content="绕队列中心画圈"
Command="{Binding FlyAroundCenterOfCoptersCommand}" />
<Button Content="Test"
Command="{Binding TestCommand}" />
<Button Content="画矩形"
Command="{Binding FlyInRectangleCommand}"
Visibility="Collapsed" />
</WrapPanel>
<WrapPanel>
<Button Content="飞到一条竖线画圈"
Command="{Binding FlyToVerticalLineAndMakeCircleCommand}" />
</WrapPanel>-->
<!--
<WrapPanel>
<Button Content="停止"
Command="{Binding StopTaskCommand}" />
</WrapPanel>
</StackPanel>-->
</StackPanel>
</UserControl>

View File

@ -0,0 +1,32 @@
using Plane.FormationCreator.ViewModels;
using Microsoft.Practices.ServiceLocation;
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 Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for ControlPanelView.xaml
/// </summary>
public partial class ControlPanelView : UserControl
{
public ControlPanelView()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<ControlPanelViewModel>();
}
}
}

View File

@ -0,0 +1,164 @@
<UserControl x:Class="Plane.FormationCreator.Views.CopterInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:ec="clr-namespace:Plane.Copters;assembly=PlaneGcsSdk_Private_NET46"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
d:DataContext="{d:DesignInstance Type=ec:FakeCopter, IsDesignTimeCreatable=True}">
<Grid>
<Grid.Resources>
<Style x:Key="CopterInfo_ComboBox"
TargetType="ComboBox"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="MinWidth"
Value="120" />
<Setter Property="VerticalAlignment"
Value="Center" />
</Style>
<Style x:Key="CopterInfo_StackPanel"
TargetType="StackPanel">
<Setter Property="Orientation"
Value="Horizontal" />
<Setter Property="Margin"
Value="0,2" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="ComboBox"
BasedOn="{StaticResource CopterInfo_ComboBox}" />
<Style TargetType="StackPanel"
BasedOn="{StaticResource CopterInfo_StackPanel}" />
</StackPanel.Resources>
<StackPanel>
<TextBlock Text="ID" />
<TextBlock Text="{Binding Path=Id}" />
</StackPanel>
<StackPanel>
<TextBlock Text="已连接:" />
<ContentPresenter Content="{Binding IsConnected, Converter={StaticResource CheckSignConverter}, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Text="已解锁:" />
<ContentPresenter Content="{Binding IsUnlocked, Converter={StaticResource CheckSignConverter}, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Text="心跳数:" />
<TextBlock Text="{Binding Path=HeartbeatCount}" />
</StackPanel>
<StackPanel>
<TextBlock Text="卫星数:" />
<TextBlock Text="{Binding Path=SatCount}" />
</StackPanel>
<StackPanel>
<TextBlock Text="Heading" />
<TextBlock Text="{Binding Path=Heading}" />
</StackPanel>
<!--<StackPanel>
<TextBlock Text="Yaw" />
<TextBlock Text="{Binding Path=Yaw, StringFormat=0.##}" />
</StackPanel>
<StackPanel>
<TextBlock Text="纬度:" />
<TextBlock Text="{Binding Path=Latitude}" />
</StackPanel>
<StackPanel>
<TextBlock Text="经度:" />
<TextBlock Text="{Binding Path=Longitude}" />
</StackPanel>-->
<StackPanel>
<TextBlock Text="高度:" />
<TextBlock Text="{Binding Altitude, StringFormat=0.##}" />
</StackPanel>
<StackPanel>
<TextBlock Text="距离:" />
<TextBlock Text="{Binding FlightDistance, StringFormat=0.##}" />
</StackPanel>
<StackPanel>
<TextBlock Text="Fix type" />
<TextBlock Text="{Binding GpsFixType}" />
</StackPanel>
<!--
<StackPanel>
<TextBlock Text="固件版本:" />
<TextBlock Text="{Binding FirmwareVersionText}" />
</StackPanel>-->
</StackPanel>
<StackPanel Grid.Row="1"
Grid.Column="1"
Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="ComboBox"
BasedOn="{StaticResource CopterInfo_ComboBox}" />
<Style TargetType="StackPanel"
BasedOn="{StaticResource CopterInfo_StackPanel}" />
</StackPanel.Resources>
<StackPanel>
<Slider x:Name="sldChannel3"
Width="100"
Minimum="1100"
Maximum="1900"
Value="{Binding Channel3, Mode=OneWay}"
ValueChanged="sldChannel3_ValueChanged">
<Slider.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Slider.Resources>
</Slider>
</StackPanel>
<StackPanel>
<TextBlock Text="电压:" />
<TextBlock Text="{Binding Path=Voltage}" />
</StackPanel>
<StackPanel>
<TextBlock Text="剩余电量:" />
<TextBlock Text="{Binding Path=BatteryPer}" />
<TextBlock Text="%" />
</StackPanel>
<StackPanel>
<TextBlock Text="状态:" />
<TextBlock Text="{Binding Path=State}" />
</StackPanel>
<StackPanel>
<TextBlock Text="通道1" />
<TextBlock Text="{Binding Channel1}" />
</StackPanel>
<StackPanel>
<TextBlock Text="通道2" />
<TextBlock Text="{Binding Channel2}" />
</StackPanel>
<StackPanel>
<TextBlock Text="通道3" />
<TextBlock Text="{Binding Channel3}" />
</StackPanel>
<StackPanel>
<TextBlock Text="通道4" />
<TextBlock Text="{Binding Channel4}" />
</StackPanel>
<StackPanel>
<TextBlock Text="HDOP" />
<TextBlock Text="{Binding GpsHdop, StringFormat=0.##}" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,50 @@
using Plane.Copters;
using Plane.FormationCreator.Formation;
using Microsoft.Practices.ServiceLocation;
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 Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for CopterInfoView.xaml
/// </summary>
public partial class CopterInfoView : UserControl
{
public CopterInfoView()
{
InitializeComponent();
}
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
private async void sldChannel3_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
try
{
var sldChannel3 = sender as Slider;
var copter = sldChannel3.DataContext as ICopter;
var selectedCopter = _copterManager.SelectedCopters.FirstOrDefault();
if (copter == null || copter != selectedCopter) return; // copter != selectedCopter 说明正在切换 copter。
var newCh3 = (ushort)e.NewValue;
if (Math.Abs(copter.Channel3 - newCh3) > 5)
{
await copter.SetChannelsAsync(ch3: newCh3);
}
}
catch { }
}
}
}

View File

@ -0,0 +1,71 @@
<UserControl x:Class="Plane.FormationCreator.Views.CopterListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
mc:Ignorable="d"
d:DesignHeight="600"
d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="lvwDrones"
ItemsSource="{Binding Path=CopterManager.Copters}"
SelectedItem="{Binding Path=SelectedCopter}"
Background="Transparent"
BorderThickness="0"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Width="20"
IsChecked="{Binding Path=IsConnected, Mode=OneWay}"
IsEnabled="False">
</CheckBox>
<Rectangle Width="15"
Height="15"
Margin="3,0">
<Rectangle.Fill>
<MultiBinding Converter="{StaticResource HeartbeatCountToBrushConverter}">
<Binding Path="HeartbeatCount" />
<Binding />
</MultiBinding>
</Rectangle.Fill>
</Rectangle>
<TextBlock MaxWidth="100"
Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1"
Orientation="Horizontal">
<TextBox x:Name="txtVirtualCopterCount"
Width="100"
Margin="0,0,5,0"
Text="3"
VerticalContentAlignment="Center" />
<Button Content="添加虚拟飞行器"
VerticalContentAlignment="Center"
Command="{Binding AddVirtualCopterCommand}"
CommandParameter="{Binding ElementName=txtVirtualCopterCount, Path=Text}" />
<Button Content="清除"
Command="{Binding ClearCoptersCommand}" />
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,45 @@
using Plane.Copters;
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.ViewModels;
using Microsoft.Practices.ServiceLocation;
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 Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for CopterListView.xaml
/// </summary>
public partial class CopterListView : UserControl
{
public CopterListView()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<CopterListViewModel>();
_copterManager.SetSelectionDelegates(
() => lvwDrones.SelectedItems,
copter => lvwDrones.SelectedItem = copter
);
lvwDrones.SelectionChanged += (sender, e) =>
{
_copterManager.RaiseSelectedCoptersChanged(e.AddedItems.Cast<ICopter>(), e.RemovedItems.Cast<ICopter>());
};
}
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
}
}

View File

@ -0,0 +1,42 @@
<UserControl x:Class="Plane.FormationCreator.Views.MapView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:m="clr-namespace:Plane.FormationCreator.Maps"
xmlns:bingMaps="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<Grid>
<bingMaps:Map Name="map"
CredentialsProvider="AmWbCvK6a-EUiuSvSewSUv0jWxqbIsEN5Ned6ocBkOwBk0tLra3Q7Y0dSY6MwDGT"
ZoomLevel="20">
<bingMaps:Map.Mode>
<bingMaps:AerialMode />
</bingMaps:Map.Mode>
<bingMaps:Map.Center>
<bingMaps:Location Latitude="23.14973333"
Longitude="113.40984166" />
</bingMaps:Map.Center>
<!--<m:OpenStreetMapTileLayer UriFormat="http://tile.openstreetmap.org/{z}/{x}/{y}.png" />-->
</bingMaps:Map>
<!--<Grid HorizontalAlignment="Right"
VerticalAlignment="Top">
<Button Content="测距" />
</Grid>-->
<ComboBox x:Name="MapSelectionComboBox"
HorizontalAlignment="Right"
VerticalAlignment="Top"
SelectionChanged="MapSelectionComboBox_SelectionChanged">
<ComboBoxItem Content="卫星地图"
Tag="卫星地图"
IsSelected="True" />
<ComboBoxItem Content="道路"
Tag="道路" />
<ComboBoxItem Content="OpenStreet"
Tag="OpenStreet" />
</ComboBox>
</Grid>
</UserControl>

View File

@ -0,0 +1,311 @@
using Plane.Copters;
using Plane;
using Plane.FormationCreator.Formation;
using Plane.Logging;
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
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;
using Plane.Collections;
using Microsoft.Maps.MapControl.WPF;
using Microsoft.Maps.MapControl.WPF.Overlays;
using System.Windows.Media.Effects;
using Microsoft.Practices.ServiceLocation;
using Plane.FormationCreator.ViewModels;
using Plane.Geography;
using Plane.FormationCreator.Maps;
namespace Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for MapView.xaml
/// </summary>
public partial class MapView : UserControl
{
public MapView()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<MapViewModel>();
_mapManager.MapView = this;
_mapManager.SetCenterGetter(() => new LatLng { Lat = map.Center.Latitude, Lng = map.Center.Longitude });
_flightTaskManager.TaskAdded += FlightTaskManager_TaskAdded;
Task.Factory.StartNew(async () =>
{
int delay = 500;
while (true)
{
if (Dispatcher.Invoke(RemoveLoadingErrorMessage))
{
delay = 2000;
}
await Task.Delay(delay);
}
});
_copterManager.SelectedCoptersChanged += (sender, e) =>
{
//if (AppEx.Instance.CurrentMode.IsForTasks())
//{
e.RemovedCopters?.ForEach(copter => _copterDrawings.GetValue(copter)?.SetEffect(false));
e.AddedCopters?.ForEach(copter => _copterDrawings.GetValue(copter)?.SetEffect(true));
//}
};
_flightTaskManager.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case nameof(FlightTaskManager.SelectedTaskIndex):
foreach (var item in _copterDrawings)
{
var copterDrawing = item.Value;
copterDrawing.SetTaskEffect(_flightTaskManager.SelectedTaskIndex);
}
break;
case nameof(FlightTaskManager.RightSelectedTaskIndex): // 单击右键触发
int taskIndexTmp = Math.Abs(_flightTaskManager.RightSelectedTaskIndex);
foreach (var item in _copterDrawings)
{
var copterDrawing = item.Value;
copterDrawing.SetTaskRightEffect(taskIndexTmp,
_flightTaskManager.Tasks[taskIndexTmp].IsRightSelected);
}
_flightTaskManager.Tasks[taskIndexTmp].IsRightSelected = !_flightTaskManager.Tasks[taskIndexTmp].IsRightSelected;
break;
default:
break;
}
};
map.MouseLeftButtonDown += (sender, e) =>
{
if (IsMouseOnCopterOrWaypoint(e.OriginalSource))
{
e.Handled = true;
}
};
_copterManager.Copters.ForEach(copter => copter.LocationChanged += Copter_LocationChanged);
_copterManager.Copters.CollectionChanged += Copters_CollectionChanged;
//_copterManager.Copters.ForEach(copter => AddOrMoveCopterForModifyingTask(copter));
//_copterManager.Copters.CollectionChanged += CoptersForModifyingTask_CollectionChanged;
map.MouseDoubleClick += async (sender, e) =>
{
e.Handled = true;
//// Test
//var pos = map.ViewportPointToLocation(e.GetPosition(map));
//AddOrMoveCopter(new FakeCopter(pos.Latitude, pos.Longitude));
// 林俊清, 20150930, AppMode 存在意义已不大;只有正在运行任务的时候才不允许指点。
//if (AppEx.Current.AppMode != AppMode.ControllingCopters) return;
if (_flightTaskManager.IsPaused == false) return;
var copters = _copterManager.AcceptingControlCopters;
if (!copters.Any()) return;
var pos = map.ViewportPointToLocation(e.GetPosition(map));
var centerLat = copters.Average(c => c.Latitude);
var centerLng = copters.Average(c => c.Longitude);
var latDelta = pos.Latitude - centerLat;
var lngDelta = pos.Longitude - centerLng;
await Task.WhenAll(copters.Select(copter => copter.FlyToAsync(copter.Latitude + latDelta, copter.Longitude + lngDelta, copter.Altitude)));
};
var center = _appConfig.Center;
map.Center = new Location(center.Lat, center.Lng);
map.ZoomLevel = _appConfig.ZoomLevel;
}
private CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
private MapManager _mapManager = ServiceLocator.Current.GetInstance<MapManager>();
private FlightTaskManager _flightTaskManager = ServiceLocator.Current.GetInstance<FlightTaskManager>();
private AppConfig _appConfig = ServiceLocator.Current.GetInstance<AppConfig>();
const string COPTER_TAG = "Copter";
const string WAYPOINT_TAG = "Waypoint";
public void Refresh()
{
foreach (var drawing in _copterDrawings.Values)
{
drawing.Track.Locations.Clear();
}
}
public bool RemoveLoadingErrorMessage()
{
var errorElement = map.Children.OfType<LoadingErrorMessage>().FirstOrDefault();
if (errorElement != null)
{
map.Children.Remove(errorElement);
return true;
}
else
{
return false;
}
}
public void GoHome()
{
map.ZoomLevel = 19;
map.Center = new Location(40.0559055, 116.322233);
}
private void Copters_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
e.OldItems?.ForEach<ICopter>(async copter =>
{
copter.LocationChanged -= Copter_LocationChanged;
await Task.Delay(100); // 如果不等待一段时间Copter_DataStreamReceived 很可能再被调用一次。
RemoveCopter(copter);
});
e.NewItems?.ForEach<ICopter>(copter => copter.LocationChanged += Copter_LocationChanged);
}
private void Copter_LocationChanged(object sender, EventArgs e)
{
var copter = sender as ICopter;
if (App.Current.CheckAccess())
{
AddOrMoveCopter(copter);
}
else
{
App.Current.Dispatcher.InvokeAsync(() => AddOrMoveCopter(copter));
}
}
private Dictionary<ICopter, CopterDrawing> _copterDrawings = new Dictionary<ICopter, CopterDrawing>();
private void AddOrMoveCopter(ICopter copter)
{
var drawing = _copterDrawings.ContainsKey(copter) ?
_copterDrawings[copter] :
(_copterDrawings[copter] = new CopterDrawing(copter, map));
drawing.AddOrMoveCopter();
}
private void RemoveCopter(ICopter copter)
{
if (_copterDrawings.ContainsKey(copter))
{
var drawing = _copterDrawings[copter];
drawing.RemoveCopter();
_copterDrawings.Remove(copter);
}
}
public void ClearCopters()
{
for (int i = map.Children.Count - 1; i >= 0; i--)
{
if (!(map.Children[i] is MapTileLayer))
{
map.Children.RemoveAt(i);
}
}
_copterDrawings.Clear();
}
private bool IsMouseOnCopterOrWaypoint(object originalSource)
{
var elem = originalSource as FrameworkElement;
string tag;
while (elem != map && elem != null)
{
tag = elem.Tag?.ToString();
if (tag == COPTER_TAG || tag == WAYPOINT_TAG)
{
return true;
}
elem = elem.Parent as FrameworkElement;
}
return false;
}
private void FlightTaskManager_TaskAdded(object sender, FlightTaskAddedEventArgs e)
{
foreach (var info in e.AddedTask.SingleCopterInfos)
{
var drawingInfo = _copterDrawings[info.Copter];
var location = new Location(info.TargetLat, info.TargetLng, info.TargetAlt);
drawingInfo.AddWaypoint(location, e.AddedTask.TaskType);
}
}
private void MapSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch ((e.AddedItems[0] as FrameworkElement).Tag.ToString())
{
case "卫星地图":
map.Mode = new AerialMode();
RemoveTileLayers();
break;
case "道路":
map.Mode = new RoadMode();
RemoveTileLayers();
break;
case "OpenStreet":
map.Mode = new MercatorMode();
if (!map.Children.OfType<OpenStreetMapTileLayer>().Any())
{
map.Children.Add(new OpenStreetMapTileLayer { UriFormat = "http://tile.openstreetmap.org/{z}/{x}/{y}.png" });
}
break;
}
}
private void RemoveTileLayers()
{
for (int i = map.Children.Count - 1; i >= 0; i--)
{
if (map.Children[i] is MapTileLayer)
{
map.Children.RemoveAt(i);
}
}
}
}
static class LocationExtensions
{
public static double CalcDistance(this Location loc1, Location loc2)
{
if (loc1 == null)
{
throw new ArgumentNullException(nameof(loc1));
}
if (loc2 == null)
{
throw new ArgumentNullException(nameof(loc2));
}
return GeographyUtils.CalcDistance(loc1.Latitude, loc1.Longitude, loc1.Altitude, loc2.Latitude, loc2.Longitude, loc2.Altitude);
}
}
}

View File

@ -0,0 +1,379 @@
using Plane.Collections;
using Plane.Copters;
using Plane.FormationCreator.Formation;
using Plane.Logging;
using Microsoft.Maps.MapControl.WPF;
using Microsoft.Practices.ServiceLocation;
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.Media;
using System.Windows.Media.Effects;
using System.Windows.Shapes;
namespace Plane.FormationCreator.Views
{
public partial class MapView
{
private class CopterDrawing
{
public CopterDrawing(ICopter copter, Map map)
{
this.Copter = copter;
_map = map;
if (copter is Copter)
{
_brush = ((Copter)copter).Brush;
_color = ((SolidColorBrush)_brush).Color;
}
else
{
_color = _colors[(_colorIndex++ % _colors.Length)];
_brush = new SolidColorBrush(_color);
}
_flightTaskManager.TasksCleared += (sender, e) =>
{
for (int i = this.Route.Locations.Count - 1; i >= 1; i--)
{
this.Route.Locations.RemoveAt(i);
}
foreach (var wp in this.Waypoints)
{
_map.Children.Remove(wp);
}
this.Waypoints.Clear();
};
}
static DropShadowEffect _selectedEffect = new DropShadowEffect();
static SolidColorBrush _selectedTaskStroke = new SolidColorBrush(Colors.White);
static Color[] _colors = new Color[]
{
Color.FromArgb(180, 255, 0, 0),
Color.FromArgb(180, 235, 97, 0),
Color.FromArgb(180, 255, 255, 0),
Color.FromArgb(180, 0, 255, 0),
Color.FromArgb(180, 0, 198, 255),
Color.FromArgb(180, 0, 122, 204),
Color.FromArgb(180, 174, 0, 255)
};
static int _colorIndex = 0;
const double COPTER_RADIUS = 12;
const double WAYPOINT_RADIUS = 6;
public ICopter Copter { get; set; }
public Grid DotContainer { get; set; }
public Polygon Dot { get; set; }
public MapPolyline Track { get; set; }
public Location LastLocation { get; set; }
public MapPolyline Route { get; set; }
public List<Ellipse> Waypoints { get; set; } = new List<Ellipse>();
Map _map;
Color _color;
Brush _brush;
ILogger _logger = ServiceLocator.Current.GetInstance<ILogger>();
CopterManager _copterManager = ServiceLocator.Current.GetInstance<CopterManager>();
FlightTaskManager _flightTaskManager = ServiceLocator.Current.GetInstance<FlightTaskManager>();
public void AddOrMoveCopter()
{
try
{
var location = new Location(Copter.Latitude, Copter.Longitude, Copter.Altitude);
Point center = _map.LocationToViewportPoint(location);
bool locationUpdated = true;
if (this.Dot == null)
{
Track = new MapPolyline();
Track.Stroke = _brush;
Track.StrokeThickness = 3.0;
Track.Locations = new LocationCollection();
_map.Children.Add(Track);
Dot = new Polygon();
Dot.Points.Add(new Point(0, -COPTER_RADIUS));
Dot.Points.Add(new Point(-COPTER_RADIUS * 2 / 3, COPTER_RADIUS));
Dot.Points.Add(new Point(COPTER_RADIUS * 2 / 3, COPTER_RADIUS));
Dot.Fill = _brush; // new SolidColorBrush(Color.FromArgb(200, 0, 122, 204));
Dot.Width = COPTER_RADIUS * 2;
Dot.Height = COPTER_RADIUS * 2;
ToolTip tt = new ToolTip();
tt.Content = $"Name: {Copter.Name}, Location: {location}";
Dot.ToolTip = tt;
DotContainer = new Grid { Tag = COPTER_TAG };
DotContainer.Children.Add(Dot);
DotContainer.Children.Add(new TextBlock
{
Text = Copter.Name,
Foreground = new SolidColorBrush(Colors.White),
Margin = new Thickness(-COPTER_RADIUS * 2, -COPTER_RADIUS * 1.5, 0, 0),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
});
_map.Children.Add(DotContainer);
MapLayer.SetZIndex(DotContainer, 100);
this.Route = new MapPolyline { Locations = new LocationCollection { location } };
this.Route.Stroke = _brush;
this.Route.StrokeThickness = 2.0;
_map.Children.Add(this.Route);
this.LastLocation = location;
RegisterEventHandlersForDraggingCopter(DotContainer);
}
else
{
if (LastLocation != null && location.CalcDistance(LastLocation) < 0.1)
{
locationUpdated = false;
}
if (locationUpdated)
{
//dot.RenderTransform = new RotateTransform(GeographyUtils.CalcDirection2D(lastLocation.Latitude, lastLocation.Longitude, location.Latitude, location.Longitude).RadiansToDegrees());
(Dot.ToolTip as ToolTip).Content = $"Name: {Copter.Name}, Location: {location}";
}
}
var trans = Dot.RenderTransform as RotateTransform;
if (trans == null)
{
Dot.RenderTransform = new RotateTransform(Copter.Heading);
}
else
{
if (trans.Angle != Copter.Heading)
{
trans.Angle = Copter.Heading;
}
}
if (locationUpdated)
{
Track.Locations.Add(location);
this.Route.Locations[0] = location;
MapLayer.SetPosition(Dot.Parent, location);
this.LastLocation = location;
}
}
catch (Exception ex)
{
_logger.Log(ex);
}
}
public void AddWaypoint(Location location, FlightTaskType type)
{
// Add waypoint.
var wp = new Ellipse
{
Tag = WAYPOINT_TAG,
Fill = _brush,
StrokeThickness = 2,
Width = WAYPOINT_RADIUS * 2,
Height = WAYPOINT_RADIUS * 2
}; ;
Waypoints.Add(wp);
_map.Children.Add(wp);
MapLayer.SetZIndex(wp, 100);
var wpPos = _map.LocationToViewportPoint(location);
wpPos.X -= WAYPOINT_RADIUS;
wpPos.Y -= WAYPOINT_RADIUS;
MapLayer.SetPosition(wp, _map.ViewportPointToLocation(wpPos));
SetEffect(_copterManager.SelectedCopters.Contains(Copter));
// Add route point.
Route.Locations.Add(location);
// Register event handlers.
RegisterEventHandlersForDraggingWaypoint(wp, taskIndex: Waypoints.Count);
// Register event handlers for task info.
RegisterEventHandlersForTaskInfo(wp, taskIndex: Waypoints.Count);
}
private void RegisterEventHandlersForDraggingCopter(Grid copterElement)
{
var dragMoving = false;
double offsetX = 0;
double offsetY = 0;
copterElement.MouseLeftButtonDown += (sender, e) =>
{
_copterManager.Select(this.Copter);
if (Copter is FakeCopter)
{
var posInObject = e.GetPosition(copterElement);
offsetX = posInObject.X;
offsetY = posInObject.Y;
dragMoving = true;
}
};
_map.MouseMove += (sender, e) =>
{
if (dragMoving)
{
var pos = e.GetPosition(_map);
pos.X -= offsetX;
pos.Y -= offsetY;
MapLayer.SetPosition(copterElement, _map.ViewportPointToLocation(pos));
var center = _map.ViewportPointToLocation(pos);
var routePoint = this.Route.Locations[0];
routePoint.Latitude = center.Latitude;
routePoint.Longitude = center.Longitude;
var fc = Copter as FakeCopter;
fc.SetProperties(
latitude: center.Latitude,
longitude: center.Longitude
);
}
};
_map.MouseLeftButtonUp += (sender, e) =>
{
if (dragMoving) dragMoving = false;
};
}
private Dictionary<object, bool> _dictDraggingWp = new Dictionary<object, bool>();
private void RegisterEventHandlersForDraggingWaypoint(Ellipse wp, int taskIndex)
{
_dictDraggingWp[wp] = false;
double offsetX = 0;
double offsetY = 0;
wp.MouseLeftButtonDown += (sender, e) =>
{
_copterManager.Select(this.Copter);
_flightTaskManager.Select(taskIndex, this.Copter);
var posInObject = e.GetPosition(wp);
offsetX = posInObject.X;
offsetY = posInObject.Y;
_dictDraggingWp[wp] = true;
};
wp.MouseRightButtonDown += (sender, e) =>
{
_flightTaskManager.RightSelect(taskIndex, this.Copter);
};
_map.MouseMove += (sender, e) =>
{
if (_dictDraggingWp[wp])
{
var eventPos = e.GetPosition(_map);
var leftTopPos = new Point(eventPos.X - offsetX, eventPos.Y - offsetY);
MapLayer.SetPosition(wp, _map.ViewportPointToLocation(leftTopPos));
var centerPos = new Point(leftTopPos.X + WAYPOINT_RADIUS, leftTopPos.Y + WAYPOINT_RADIUS);
var centerLoc = _map.ViewportPointToLocation(centerPos);
var routePoint = this.Route.Locations[taskIndex];
var modifyingSingleCopterInfo = _flightTaskManager.SelectedTask.SingleCopterInfos.Find(i => i.Copter == Copter);
modifyingSingleCopterInfo.TargetLat = routePoint.Latitude = centerLoc.Latitude;
modifyingSingleCopterInfo.TargetLng = routePoint.Longitude = centerLoc.Longitude;
}
};
_map.MouseLeftButtonUp += (sender, e) =>
{
if (_dictDraggingWp[wp]) _dictDraggingWp[wp] = false;
};
}
private void RegisterEventHandlersForTaskInfo(Ellipse wp, int taskIndex)
{
var info = _flightTaskManager.Tasks[taskIndex].SingleCopterInfos.FirstOrDefault(i => i.Copter == this.Copter);
info.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case nameof(FlightTaskSingleCopterInfo.TargetLat):
case nameof(FlightTaskSingleCopterInfo.TargetLng):
case nameof(FlightTaskSingleCopterInfo.TargetAlt):
if (!_dictDraggingWp.GetValue(wp, false))
{
var centerLocation = new Location(info.TargetLat, info.TargetLng, info.TargetAlt);
var centerPos = _map.LocationToViewportPoint(centerLocation);
var leftTopPos = new Point(centerPos.X - WAYPOINT_RADIUS, centerPos.Y - WAYPOINT_RADIUS);
MapLayer.SetPosition(wp, _map.ViewportPointToLocation(leftTopPos));
var routePoint = Route.Locations[taskIndex];
routePoint.Latitude = info.TargetLat;
routePoint.Longitude = info.TargetLng;
routePoint.Altitude = info.TargetAlt;
}
break;
}
};
}
public void RemoveCopter()
{
_map.Children.Remove(this.DotContainer);
_map.Children.Remove(this.Track);
_map.Children.Remove(this.Route);
foreach (var item in this.Waypoints)
{
_map.Children.Remove(item);
}
}
public void SetEffect(bool selected)
{
Dot.Effect = selected ? _selectedEffect : null;
Waypoints.ForEach(wp => wp.Effect = Dot.Effect);
}
public void SetTaskEffect(int taskIndex)
{
var wpIndex = taskIndex - 1; // Waypoints 中没有起飞点。
if (wpIndex >= 0 && wpIndex < Waypoints.Count)
{
Waypoints.ForEach(p => p.Stroke = null);
var wp = Waypoints[wpIndex];
wp.Stroke = _selectedTaskStroke;
}
}
// 右击任务时触发
public void SetTaskRightEffect(int taskIndex, bool flag)
{
var wpIndex = taskIndex - 1; // Waypoints 中没有起飞点。
if (wpIndex >= 0 && wpIndex < Waypoints.Count)
{
// Waypoints.ForEach(p => p.Stroke = null);
var wp = Waypoints[wpIndex];
// Route.Locations.RemoveAt(wpIndex);
// Route.Visibility = Visibility.Hidden;
// wp.Stroke = _selectedTaskStroke;
// var flightTaskTmp = _flightTaskManager.Tasks[taskIndex];
if (!flag)
{
wp.Visibility = Visibility.Hidden;
//Route.Visibility = Visibility.Hidden;
//Route.Children.RemoveAt(1);
}
else
{
wp.Visibility = Visibility.Visible;
//Route.Visibility = Visibility.Visible;
}
}
}
}
}
}

View File

@ -0,0 +1,433 @@
<UserControl x:Class="Plane.FormationCreator.Views.ModifyTaskView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:vm="clr-namespace:Plane.FormationCreator.ViewModels"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="StackPanel">
<Setter Property="Orientation"
Value="Horizontal" />
</Style>
</StackPanel.Resources>
<StackPanel>
<Button Content="导出任务"
Margin="0,5,0,0"
Command="{Binding ExportTasksCommand}" />
<Button Content="导入任务"
Margin="5,5,0,0"
Command="{Binding ImportTasksCommand}" />
<TextBox Grid.Column="1"
Width="30"
Margin="5, 5, 5, 0"
HorizontalContentAlignment="Right"
Name="firstCopter"
Text="0" />
<TextBox Grid.Column="1"
Width="30"
Margin="5, 5, 5, 0"
HorizontalContentAlignment="Right"
Name="secondCopter"
Text="1" />
<TextBox Grid.Column="1"
Width="40"
Margin="5, 5, 5, 0"
HorizontalContentAlignment="Right"
Name="distanceCopters"
Text="" />
<Button Content="计算"
Margin="5,5,0,0" Click="Button_Click"
/>
</StackPanel>
<TabControl Margin="0,5"
Grid.IsSharedSizeScope="True"
DataContext="{Binding FlightTaskManager.SelectedTask}"
SelectedIndex="{Binding TaskTypeIndex}">
<TabControl.Resources>
<Style TargetType="TextBlock">
<Setter Property="VerticalAlignment"
Value="Center" />
</Style>
<Style TargetType="TextBox"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Margin"
Value="2" />
<Setter Property="MinWidth"
Value="100" />
</Style>
<Style TargetType="CheckBox"
BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Margin"
Value="2" />
</Style>
</TabControl.Resources>
<TabItem Header="飞往">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text="错开路线: " />
<CheckBox Grid.Row="0" Grid.Column="1"
IsChecked="{Binding StaggerRoutes}" />
<TextBlock Grid.Row="1" Text="垂直升降: " />
<CheckBox Grid.Row="1" Grid.Column="1"
IsChecked="{Binding VerticalLift}" />
</Grid>
<Separator Grid.Row="1" />
<Grid Grid.Row="2"
DataContext="{Binding ModifyingSingleCopterInfo}"
IsEnabled="{Binding CanModifySingleCopterInfo}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="纬度: " />
<TextBox Grid.Column="1"
Text="{Binding TargetLat, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="1"
Text="经度: " />
<TextBox Grid.Row="1"
Grid.Column="1"
Text="{Binding TargetLng, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="2"
Text="高度: " />
<TextBox Grid.Row="2"
Grid.Column="1"
Text="{Binding TargetAlt, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</Grid>
</TabItem>
<TabItem Header="转向">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Separator Grid.Row="1" />
<Grid Grid.Row="2"
DataContext="{Binding ModifyingSingleCopterInfo}"
IsEnabled="{Binding CanModifySingleCopterInfo}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="目标偏航: " />
<TextBox Grid.Column="1"
Text="{Binding TargetHeading, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Column="2"
Text="°" />
</Grid>
</Grid>
</Grid>
</TabItem>
<TabItem Header="画圈">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Separator Grid.Row="1" />
<Grid Grid.Row="2"
DataContext="{Binding ModifyingSingleCopterInfo}"
IsEnabled="{Binding CanModifySingleCopterInfo}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="圆心方向: " />
<TextBox Grid.Column="1"
Text="{Binding CenterDirectionDeg, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Column="2"
Text="°" />
<TextBlock Grid.Row="1"
Text="半径: " />
<TextBox Grid.Row="1"
Grid.Column="1"
Text="{Binding Radius, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Text="cm" />
<TextBlock Grid.Row="2"
Text="角速度: " />
<TextBox Grid.Row="2"
Grid.Column="1"
Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="2"
Grid.Column="2"
Text="°/s" />
<TextBlock Grid.Row="3"
Text="圈数: " />
<TextBox Grid.Row="3"
Grid.Column="1"
Text="{Binding Turns, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="3"
Grid.Column="2"
Text="圈" />
<TextBlock Grid.Row="4"
Text="通道3: " />
<TextBox Grid.Row="4"
Grid.Column="1"
Text="{Binding Channel3, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</Grid>
</TabItem>
<TabItem Header="悬停">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="悬停时间: " />
<TextBox Grid.Column="1"
Text="{Binding LoiterTimeAttr, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="闪烁: " />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0"
IsChecked="{Binding flashAttr, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Column="1" Grid.Row="0"
Text="{Binding flashPeriodAttr, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<TextBlock Grid.Row="2" Grid.Column="0" Text="走马灯: " />
<Grid Grid.Row="2" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0"
IsChecked="{Binding oneByOneAttr, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Column="1" Grid.Row="0"
Text="{Binding oneByOnePeriodAttr, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<TextBox Grid.Row="3" Grid.ColumnSpan="2" IsReadOnly="True" Text="{Binding flashCopterNameArray, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</Grid>
</TabItem>
<!--
<TabItem Header="LED控制">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Separator Grid.Row="1" />
<Grid Grid.Row="2"
DataContext="{Binding ModifyingSingleCopterInfo}"
IsEnabled="{Binding CanModifySingleCopterInfo}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="全部闪烁: " />
<TextBox Grid.Column="1" Grid.Row="0"
Text="15" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="走马灯: " />
<TextBox Grid.Column="1" Grid.Row="1"
Text="15" />
</Grid>
</Grid>
</Grid>
</TabItem>
-->
<TabItem Header="返航">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="返航高度: " />
<TextBox Grid.Column="1"
Text="{Binding RTLAlt, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</Grid>
</TabItem>
<!--<TabItem Header="简单画圈">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="TabItem" />
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Separator Grid.Row="1" />
<Grid Grid.Row="2"
DataContext="{Binding ModifyingSingleCopterInfo}"
IsEnabled="{Binding CanModifySingleCopterInfo}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0"
Text="角速度: " />
<TextBox Grid.Row="0"
Grid.Column="1"
Text="{Binding Rate, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="0"
Grid.Column="2"
Text="°/s" />
<TextBlock Grid.Row="1"
Text="圈数: " />
<TextBox Grid.Row="1"
Grid.Column="1"
Text="{Binding Turns, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Text="圈" />
<TextBlock Grid.Row="2"
Text="通道3: " />
<TextBox Grid.Row="2"
Grid.Column="1"
Text="{Binding Channel3, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Grid>
</Grid>
</TabItem>-->
</TabControl>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,54 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.ViewModels;
using Microsoft.Practices.ServiceLocation;
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;
using Plane.Geography;
namespace Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for ModifyTaskView.xaml
/// </summary>
public partial class ModifyTaskView : UserControl
{
public ModifyTaskView()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<ModifyTaskViewModel>();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
FlightTaskManager _flightTaskManager = ServiceLocator.Current.GetInstance<FlightTaskManager>();
int firstIndex = int.Parse( firstCopter.Text );
int secondIndex = int.Parse( secondCopter.Text );
if (_flightTaskManager.SelectedTask != null)
{
var firstCopterInfo = _flightTaskManager.SelectedTask.SingleCopterInfos[firstIndex];
var secondCopterInfo = _flightTaskManager.SelectedTask.SingleCopterInfos[secondIndex];
double distance = GeographyUtils.CalcDistance(firstCopterInfo.TargetLat, firstCopterInfo.TargetLng, firstCopterInfo.TargetAlt,
secondCopterInfo.TargetLat, secondCopterInfo.TargetLng, secondCopterInfo.TargetAlt);
distanceCopters.Text = ((double)Math.Round(distance*100)/100).ToString();
}
}
}
}

View File

@ -0,0 +1,127 @@
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Plane.FormationCreator.Views"
xmlns:m="clr-namespace:Plane.FormationCreator.Formation"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Class="Plane.FormationCreator.Views.TaskBarView"
mc:Ignorable="d"
d:DesignHeight="40"
d:DesignWidth="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button HorizontalAlignment="Left"
Visibility="{Binding FlightTaskManager.IsPaused, Converter={StaticResource IsPausedToRunButtonVisibilityConverter}}"
Command="{Binding RunTasksCommand}">
<Button.Template>
<ControlTemplate>
<Grid Width="40"
Height="40"
Background="#31000000"
VerticalAlignment="Bottom">
<ed:RegularPolygon Fill="LightGray"
InnerRadius="1"
PointCount="3"
Width="40"
Height="40"
Stretch="Fill"
Stroke="Gray">
<ed:RegularPolygon.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="90" />
<TranslateTransform X="40" />
</TransformGroup>
</ed:RegularPolygon.RenderTransform>
</ed:RegularPolygon>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<Button HorizontalAlignment="Left"
Visibility="{Binding FlightTaskManager.IsPaused, Converter={StaticResource IsPausedToPauseButtonVisibilityConverter}}"
Command="{Binding PauseTasksCommand}">
<Button.Template>
<ControlTemplate>
<Grid Width="40"
Height="40"
VerticalAlignment="Bottom"
Background="#33000000">
<Rectangle Fill="LightGray"
HorizontalAlignment="Left"
Width="15"
Height="40"
Stroke="Gray" />
<Rectangle Fill="LightGray"
HorizontalAlignment="Right"
Width="15"
Height="40"
Stroke="Gray" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<ItemsControl Grid.Column="1"
VerticalAlignment="Bottom"
ItemsSource="{Binding Tasks}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Background="{Binding Status, Converter={StaticResource FlightTaskStatusToFillConverter}}"
Width="100"
Height="15"
BorderThickness="2"
BorderBrush="Gray"
Effect="{Binding IsSelected, Converter={StaticResource FlightTaskIsSelectedToEffectConverter}}"
MouseLeftButtonUp="SelectTask"
MouseRightButtonUp="HideTask">
<TextBlock Width="auto"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontSize="11"
Foreground="Black"
Text="{Binding TaskType}" />
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Grid.Column="2"
Orientation="Horizontal"
VerticalAlignment="Bottom">
<Button Content="添加任务"
Command="{Binding AddTaskCommand}" />
<Button Content="清除任务"
Command="{Binding ClearTasksCommand}" />
<Button Content="重置任务"
Command="{Binding ResetTasksCommand}" />
<!--<Button Content="保存" />
<Button Content="取消" />-->
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,67 @@
using Plane.FormationCreator.Formation;
using Plane.FormationCreator.ViewModels;
using Microsoft.Practices.ServiceLocation;
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 Plane.FormationCreator.Views
{
/// <summary>
/// Interaction logic for TaskBarView.xaml
/// </summary>
public partial class TaskBarView : UserControl
{
public TaskBarView()
{
InitializeComponent();
this.DataContext = ServiceLocator.Current.GetInstance<TaskBarViewModel>();
}
private FlightTaskManager _flightTaskManager = ServiceLocator.Current.GetInstance<FlightTaskManager>();
private void SelectTask(object sender, MouseButtonEventArgs e)
{
var elem = sender as FrameworkElement;
var task = elem.DataContext as FlightTask;
if (task.TaskType != FlightTaskType.TakeOff) // 不让选起飞任务。
{
_flightTaskManager.Select(task);
}
}
private void HideTask(object sender, MouseButtonEventArgs e)
{
var elem = sender as FrameworkElement;
var task = elem.DataContext as FlightTask;
// int a = _flightTaskManager.RightSelectedTaskIndex;
if (task.TaskType != FlightTaskType.TakeOff) // 不让选起飞任务。
{
var borderBrush = new SolidColorBrush(task.IsRightSelected ? Colors.Gray : Colors.OrangeRed);
if (elem is Rectangle)
{
(elem as Rectangle).Stroke = borderBrush;
}
else if (elem is Border)
{
(elem as Border).BorderBrush = borderBrush;
}
_flightTaskManager.RightSelect(task);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonServiceLocator" version="1.3" targetFramework="net45" />
<package id="MahApps.Metro" version="1.2.2.0" targetFramework="net46" />
<package id="MaterialDesignColors" version="1.1.2" targetFramework="net46" />
<package id="MaterialDesignThemes" version="1.4.0.473" targetFramework="net46" />
<package id="Microsoft.Maps.MapControl.WPF" version="1.0.0.3" targetFramework="net45" />
<package id="MvvmLightLibs" version="5.2.0.0" targetFramework="net46" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net46" />
</packages>