原始导入
This commit is contained in:
commit
3c52409203
74
Plane.FormationCreator.sln
Normal file
74
Plane.FormationCreator.sln
Normal 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
|
26
Plane.FormationCreator/App.config
Normal file
26
Plane.FormationCreator/App.config
Normal 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>
|
49
Plane.FormationCreator/App.xaml
Normal file
49
Plane.FormationCreator/App.xaml
Normal 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>
|
153
Plane.FormationCreator/App.xaml.cs
Normal file
153
Plane.FormationCreator/App.xaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
105
Plane.FormationCreator/AppConfig.cs
Normal file
105
Plane.FormationCreator/AppConfig.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
53
Plane.FormationCreator/AppEx.cs
Normal file
53
Plane.FormationCreator/AppEx.cs
Normal 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;
|
||||
}
|
||||
}
|
2
Plane.FormationCreator/ClassDiagram1.cd
Normal file
2
Plane.FormationCreator/ClassDiagram1.cd
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ClassDiagram />
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
30
Plane.FormationCreator/Formation/AppMode.cs
Normal file
30
Plane.FormationCreator/Formation/AppMode.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
44
Plane.FormationCreator/Formation/Copter.cs
Normal file
44
Plane.FormationCreator/Formation/Copter.cs
Normal 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); }
|
||||
}
|
||||
}
|
||||
}
|
85
Plane.FormationCreator/Formation/CopterManager.cs
Normal file
85
Plane.FormationCreator/Formation/CopterManager.cs
Normal 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; }
|
||||
}
|
||||
}
|
61
Plane.FormationCreator/Formation/CopterStatus.cs
Normal file
61
Plane.FormationCreator/Formation/CopterStatus.cs
Normal 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); }
|
||||
}
|
||||
}
|
||||
}
|
77
Plane.FormationCreator/Formation/Extensions.cs
Normal file
77
Plane.FormationCreator/Formation/Extensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
125
Plane.FormationCreator/Formation/FlightTask.cs
Normal file
125
Plane.FormationCreator/Formation/FlightTask.cs
Normal 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
|
||||
}
|
||||
}
|
830
Plane.FormationCreator/Formation/FlightTaskManager.cs
Normal file
830
Plane.FormationCreator/Formation/FlightTaskManager.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -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处于哪个阶段
|
||||
}
|
||||
}
|
@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
6
Plane.FormationCreator/Formation/FlightTaskStatus.cs
Normal file
6
Plane.FormationCreator/Formation/FlightTaskStatus.cs
Normal file
@ -0,0 +1,6 @@
|
||||
public enum FlightTaskStatus
|
||||
{
|
||||
Created,
|
||||
Saved,
|
||||
Running
|
||||
}
|
137
Plane.FormationCreator/Formation/FlightTask_Circle.cs
Normal file
137
Plane.FormationCreator/Formation/FlightTask_Circle.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
738
Plane.FormationCreator/Formation/FlightTask_FlyTo.cs
Normal file
738
Plane.FormationCreator/Formation/FlightTask_FlyTo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
474
Plane.FormationCreator/Formation/FlightTask_LoiterTime.cs
Normal file
474
Plane.FormationCreator/Formation/FlightTask_LoiterTime.cs
Normal 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为true,打开灯;isOn为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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
209
Plane.FormationCreator/Formation/FlightTask_ReturnToLand.cs
Normal file
209
Plane.FormationCreator/Formation/FlightTask_ReturnToLand.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
// });
|
||||
}
|
||||
}
|
||||
}
|
24
Plane.FormationCreator/Formation/FlightTask_SimpleCircle.cs
Normal file
24
Plane.FormationCreator/Formation/FlightTask_SimpleCircle.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
299
Plane.FormationCreator/Formation/FlightTask_TakeOff.cs
Normal file
299
Plane.FormationCreator/Formation/FlightTask_TakeOff.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
41
Plane.FormationCreator/Formation/FlightTask_Turn.cs
Normal file
41
Plane.FormationCreator/Formation/FlightTask_Turn.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
734
Plane.FormationCreator/Formation/FormationController.cs
Normal file
734
Plane.FormationCreator/Formation/FormationController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
24
Plane.FormationCreator/Formation/LatLng.cs
Normal file
24
Plane.FormationCreator/Formation/LatLng.cs
Normal 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; }
|
||||
}
|
||||
}
|
29
Plane.FormationCreator/Formation/MapManager.cs
Normal file
29
Plane.FormationCreator/Formation/MapManager.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
138
Plane.FormationCreator/Formation/MapServer.cs
Normal file
138
Plane.FormationCreator/Formation/MapServer.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
14
Plane.FormationCreator/Formation/PropertyChangedEventArgs.cs
Normal file
14
Plane.FormationCreator/Formation/PropertyChangedEventArgs.cs
Normal 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; }
|
||||
}
|
||||
}
|
132
Plane.FormationCreator/MainWindow.xaml
Normal file
132
Plane.FormationCreator/MainWindow.xaml
Normal 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>
|
308
Plane.FormationCreator/MainWindow.xaml.cs
Normal file
308
Plane.FormationCreator/MainWindow.xaml.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
Plane.FormationCreator/MainWindow_old.xaml
Normal file
121
Plane.FormationCreator/MainWindow_old.xaml
Normal 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>
|
23
Plane.FormationCreator/Maps/OpenStreetMapTileLayer.cs
Normal file
23
Plane.FormationCreator/Maps/OpenStreetMapTileLayer.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
20
Plane.FormationCreator/Maps/OpenStreetMapTileSource.cs
Normal file
20
Plane.FormationCreator/Maps/OpenStreetMapTileSource.cs
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
321
Plane.FormationCreator/Plane.FormationCreator.csproj
Normal file
321
Plane.FormationCreator/Plane.FormationCreator.csproj
Normal 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>
|
55
Plane.FormationCreator/Properties/AssemblyInfo.cs
Normal file
55
Plane.FormationCreator/Properties/AssemblyInfo.cs
Normal 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")]
|
63
Plane.FormationCreator/Properties/Resources.Designer.cs
generated
Normal file
63
Plane.FormationCreator/Properties/Resources.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
Plane.FormationCreator/Properties/Resources.resx
Normal file
117
Plane.FormationCreator/Properties/Resources.resx
Normal 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>
|
38
Plane.FormationCreator/Properties/Settings.Designer.cs
generated
Normal file
38
Plane.FormationCreator/Properties/Settings.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
Plane.FormationCreator/Properties/Settings.settings
Normal file
9
Plane.FormationCreator/Properties/Settings.settings
Normal 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>
|
47
Plane.FormationCreator/ServiceLocatorConfigurer.cs
Normal file
47
Plane.FormationCreator/ServiceLocatorConfigurer.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
BIN
Plane.FormationCreator/SplashScreen1.png
Normal file
BIN
Plane.FormationCreator/SplashScreen1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 125 KiB |
47
Plane.FormationCreator/Styles.xaml
Normal file
47
Plane.FormationCreator/Styles.xaml
Normal 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>
|
89
Plane.FormationCreator/Styles/Button.xaml
Normal file
89
Plane.FormationCreator/Styles/Button.xaml
Normal 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>
|
194
Plane.FormationCreator/Styles/Colors.xaml
Normal file
194
Plane.FormationCreator/Styles/Colors.xaml
Normal 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>
|
328
Plane.FormationCreator/Styles/ComboBox.xaml
Normal file
328
Plane.FormationCreator/Styles/ComboBox.xaml
Normal 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>
|
67
Plane.FormationCreator/Styles/ProgressButton.xaml
Normal file
67
Plane.FormationCreator/Styles/ProgressButton.xaml
Normal 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>
|
80
Plane.FormationCreator/Styles/TabControl.xaml
Normal file
80
Plane.FormationCreator/Styles/TabControl.xaml
Normal 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>
|
42
Plane.FormationCreator/Styles/TextBox.xaml
Normal file
42
Plane.FormationCreator/Styles/TextBox.xaml
Normal 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>
|
16
Plane.FormationCreator/Styles/Window.xaml
Normal file
16
Plane.FormationCreator/Styles/Window.xaml
Normal 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>
|
86
Plane.FormationCreator/StylesOld/Button.xaml
Normal file
86
Plane.FormationCreator/StylesOld/Button.xaml
Normal 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>
|
194
Plane.FormationCreator/StylesOld/Colors.xaml
Normal file
194
Plane.FormationCreator/StylesOld/Colors.xaml
Normal 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>
|
328
Plane.FormationCreator/StylesOld/ComboBox.xaml
Normal file
328
Plane.FormationCreator/StylesOld/ComboBox.xaml
Normal 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>
|
67
Plane.FormationCreator/StylesOld/ProgressButton.xaml
Normal file
67
Plane.FormationCreator/StylesOld/ProgressButton.xaml
Normal 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>
|
80
Plane.FormationCreator/StylesOld/TabControl.xaml
Normal file
80
Plane.FormationCreator/StylesOld/TabControl.xaml
Normal 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>
|
42
Plane.FormationCreator/StylesOld/TextBox.xaml
Normal file
42
Plane.FormationCreator/StylesOld/TextBox.xaml
Normal 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>
|
16
Plane.FormationCreator/StylesOld/Window.xaml
Normal file
16
Plane.FormationCreator/StylesOld/Window.xaml
Normal 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>
|
52
Plane.FormationCreator/Test.cs
Normal file
52
Plane.FormationCreator/Test.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
176
Plane.FormationCreator/ViewModels/ConnectViewModel.cs
Normal file
176
Plane.FormationCreator/ViewModels/ConnectViewModel.cs
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
464
Plane.FormationCreator/ViewModels/ControlPanelViewModel.cs
Normal file
464
Plane.FormationCreator/ViewModels/ControlPanelViewModel.cs
Normal 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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
Plane.FormationCreator/ViewModels/CopterListViewModel.cs
Normal file
100
Plane.FormationCreator/ViewModels/CopterListViewModel.cs
Normal 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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
Plane.FormationCreator/ViewModels/MainViewModel.cs
Normal file
121
Plane.FormationCreator/ViewModels/MainViewModel.cs
Normal 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 ? "高速模式" : "低速模式";
|
||||
}
|
||||
}
|
44
Plane.FormationCreator/ViewModels/MapViewModel.cs
Normal file
44
Plane.FormationCreator/ViewModels/MapViewModel.cs
Normal 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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
212
Plane.FormationCreator/ViewModels/ModifyTaskViewModel.cs
Normal file
212
Plane.FormationCreator/ViewModels/ModifyTaskViewModel.cs
Normal 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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
Plane.FormationCreator/ViewModels/TaskBarViewModel.cs
Normal file
108
Plane.FormationCreator/ViewModels/TaskBarViewModel.cs
Normal 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();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
Plane.FormationCreator/Views/ConnectWindow.xaml
Normal file
123
Plane.FormationCreator/Views/ConnectWindow.xaml
Normal 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>
|
44
Plane.FormationCreator/Views/ConnectWindow.xaml.cs
Normal file
44
Plane.FormationCreator/Views/ConnectWindow.xaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
93
Plane.FormationCreator/Views/ControlPanelView.xaml
Normal file
93
Plane.FormationCreator/Views/ControlPanelView.xaml
Normal 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>
|
32
Plane.FormationCreator/Views/ControlPanelView.xaml.cs
Normal file
32
Plane.FormationCreator/Views/ControlPanelView.xaml.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
164
Plane.FormationCreator/Views/CopterInfoView.xaml
Normal file
164
Plane.FormationCreator/Views/CopterInfoView.xaml
Normal 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>
|
50
Plane.FormationCreator/Views/CopterInfoView.xaml.cs
Normal file
50
Plane.FormationCreator/Views/CopterInfoView.xaml.cs
Normal 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 { }
|
||||
}
|
||||
}
|
||||
}
|
71
Plane.FormationCreator/Views/CopterListView.xaml
Normal file
71
Plane.FormationCreator/Views/CopterListView.xaml
Normal 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>
|
45
Plane.FormationCreator/Views/CopterListView.xaml.cs
Normal file
45
Plane.FormationCreator/Views/CopterListView.xaml.cs
Normal 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>();
|
||||
}
|
||||
}
|
42
Plane.FormationCreator/Views/MapView.xaml
Normal file
42
Plane.FormationCreator/Views/MapView.xaml
Normal 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>
|
311
Plane.FormationCreator/Views/MapView.xaml.cs
Normal file
311
Plane.FormationCreator/Views/MapView.xaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
379
Plane.FormationCreator/Views/MapView_CopterDrawing.cs
Normal file
379
Plane.FormationCreator/Views/MapView_CopterDrawing.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
433
Plane.FormationCreator/Views/ModifyTaskView.xaml
Normal file
433
Plane.FormationCreator/Views/ModifyTaskView.xaml
Normal 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>
|
54
Plane.FormationCreator/Views/ModifyTaskView.xaml.cs
Normal file
54
Plane.FormationCreator/Views/ModifyTaskView.xaml.cs
Normal 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
127
Plane.FormationCreator/Views/TaskBarView.xaml
Normal file
127
Plane.FormationCreator/Views/TaskBarView.xaml
Normal 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>
|
67
Plane.FormationCreator/Views/TaskBarView.xaml.cs
Normal file
67
Plane.FormationCreator/Views/TaskBarView.xaml.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
BIN
Plane.FormationCreator/bg.jpg
Normal file
BIN
Plane.FormationCreator/bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 226 KiB |
BIN
Plane.FormationCreator/gcs.ico
Normal file
BIN
Plane.FormationCreator/gcs.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
10
Plane.FormationCreator/packages.config
Normal file
10
Plane.FormationCreator/packages.config
Normal 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>
|
Loading…
Reference in New Issue
Block a user