diff --git a/PlaneGcsSdk_Shared/Communication/TcpConnection.cs b/PlaneGcsSdk_Shared/Communication/TcpConnection.cs index 8cf256a..b0e194b 100644 --- a/PlaneGcsSdk_Shared/Communication/TcpConnection.cs +++ b/PlaneGcsSdk_Shared/Communication/TcpConnection.cs @@ -1,118 +1,125 @@ -#if !NETFX_CORE - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace Plane.Communication -{ - /// - /// 提供作为 TCP 客户端通信的能力。 - /// - public class TcpConnection : TcpConnectionBase - { - private string _remoteHostname; - const int tcpReceiveBufferSize = 20 * 1024; //40 * 1024, - const int tcpReceiveTimeout = 1200; - - private int _remotePort; - - public TcpConnection(string remoteHostname, int remotePort = 5250) - { - _remoteHostname = remoteHostname; - _remotePort = remotePort; - _client = new TcpClient - { - ReceiveBufferSize = tcpReceiveBufferSize, - ReceiveTimeout = tcpReceiveTimeout - }; - } - - public void Open() - { - if (!IsOpen) - { - try - { - _client.Connect(_remoteHostname, _remotePort); - } - catch (SocketException) - { - CreateClientAndConnect(); - } - catch (ObjectDisposedException) - { - CreateClientAndConnect(); - } - _isBroken = false; - } - - _stream = _client.GetStream(); - } - - public async Task BindIP(string ip) - { - bool bind = false; - try - { - IPAddress IPLocal = IPAddress.Parse(ip); - _client.Client.Bind(new IPEndPoint(IPLocal, 0)); - bind = true; - } - catch - { - bind = false; - } - await Task.Delay(10).ConfigureAwait(false); - return bind; - } - - public override async Task OpenAsync() - { - string logstr; - if (!IsOpen) - { - try - { - await _client.ConnectAsync(_remoteHostname, _remotePort).ConfigureAwait(false); - } - //屏蔽掉异常处理 - //CreateClientAndConnectAsync会new一个TcpClient并且重新连接 - //之前设置的TcpClient中Socket Bind会无效,在多网卡工作时会导致断线重连的时间特别长 - catch (SocketException e) - { - logstr= e.Message; - //await CreateClientAndConnectAsync(); - } - catch (ObjectDisposedException) - { - //await CreateClientAndConnectAsync(); - } - _isBroken = false; - } - _stream = _client.GetStream(); - } - - private void CreateClientAndConnect() - { - _client = new TcpClient(_remoteHostname, _remotePort) - { - ReceiveBufferSize = tcpReceiveBufferSize, - ReceiveTimeout = tcpReceiveTimeout - }; - } - - private async Task CreateClientAndConnectAsync() - { - _client = new TcpClient - { - ReceiveBufferSize = tcpReceiveBufferSize,// 40 * 1024, - ReceiveTimeout = tcpReceiveTimeout// 1200 - }; - await _client.ConnectAsync(_remoteHostname, _remotePort).ConfigureAwait(false); - } - } -} - -#endif +#if !NETFX_CORE + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace Plane.Communication +{ + /// + /// 提供作为 TCP 客户端通信的能力。 + /// + public class TcpConnection : TcpConnectionBase + { + private string _remoteHostname; + const int tcpReceiveBufferSize = 20 * 1024; //40 * 1024, + const int tcpReceiveTimeout = 1200; + + private int _remotePort; + + public TcpConnection(string remoteHostname, int remotePort = 5250) + { + _remoteHostname = remoteHostname; + _remotePort = remotePort; + _client = new TcpClient + { + ReceiveBufferSize = tcpReceiveBufferSize, + ReceiveTimeout = tcpReceiveTimeout + }; + } + + public void Open() + { + if (!IsOpen) + { + try + { + _client.Connect(_remoteHostname, _remotePort); + } + catch (SocketException) + { + CreateClientAndConnect(); + } + catch (ObjectDisposedException) + { + CreateClientAndConnect(); + } + _isBroken = false; + } + + _stream = _client.GetStream(); + } + + public async Task BindIP(string ip) + { + bool bind = false; + try + { + IPAddress IPLocal = IPAddress.Parse(ip); + _client.Client.Bind(new IPEndPoint(IPLocal, 0)); + bind = true; + } + catch + { + bind = false; + } + await Task.Delay(10).ConfigureAwait(false); + return bind; + } + + public override async Task OpenAsync() + { + string logstr; + if (!IsOpen) + { + if (_client == null) + CreateClientAndConnect(); + try + { + var connectTask = _client.ConnectAsync(_remoteHostname, _remotePort); + if (await Task.WhenAny(connectTask, Task.Delay(5000)) == connectTask) + { + await connectTask.ConfigureAwait(false); + } + else + { + // Connection timed out + throw new TimeoutException("Connection timed out"); + } + } + catch (SocketException e) + { + logstr = e.Message; + } + catch (ObjectDisposedException) + { + } + _isBroken = false; + } + _stream = _client.GetStream(); + } + + + private void CreateClientAndConnect() + { + _client = new TcpClient(_remoteHostname, _remotePort) + { + ReceiveBufferSize = tcpReceiveBufferSize, + ReceiveTimeout = tcpReceiveTimeout + }; + } + + private async Task CreateClientAndConnectAsync() + { + _client = new TcpClient + { + ReceiveBufferSize = tcpReceiveBufferSize,// 40 * 1024, + ReceiveTimeout = tcpReceiveTimeout// 1200 + }; + await _client.ConnectAsync(_remoteHostname, _remotePort).ConfigureAwait(false); + } + } +} + +#endif diff --git a/PlaneGcsSdk_Shared/CommunicationManagement/CommModule.cs b/PlaneGcsSdk_Shared/CommunicationManagement/CommModule.cs index 1c29095..997cd1a 100644 --- a/PlaneGcsSdk_Shared/CommunicationManagement/CommModule.cs +++ b/PlaneGcsSdk_Shared/CommunicationManagement/CommModule.cs @@ -1,915 +1,918 @@ -using Plane.Communication; -using Plane.Protocols; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Media; - -using System.Runtime.InteropServices; -using Plane.Windows.Messages; - -namespace Plane.CommunicationManagement -{ - //通信模块 - public partial class CommModuleManager: ExceptionThrownEventSource, IDisposable - { - [DllImport("kernel32")] //返回取得字符串缓冲区的长度 - private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); - - private static CommModuleManager _Instance = new CommModuleManager(); - public static CommModuleManager Instance { get { return _Instance; } } - - public TcpConnection Connection = null; - public bool CommOK = false; - private const string MODULE_IP = "192.168.199.51"; //"192.168.199.51"; // "192.168.199.51"; //在用版本 ----//"192.168.1.65"; //新版本 - private string ServerIP = MODULE_IP; - // private const string LOCAL_IP = "192.168.199.50"; //"192.168.199.50"; - private const int PORT = 9551; - private bool _disposed; - public int CommModuleCopterCount = 0; - public int CommModuleCurMode = 0; - private long packetCountNum = 0; - - - /// - /// 用于判断写入任务操作超时的秒表。 - /// - private Stopwatch _writeMissionStopwatch; - - private void LoadIPSet() - { - StringBuilder temp = new StringBuilder(255); - - string path = Environment.CurrentDirectory + @"\Config.ini"; - - int i = GetPrivateProfileString("Default", "ServerIP", "", temp, 255, path); - ServerIP= temp.ToString(); - - if (ServerIP == "") ServerIP = MODULE_IP; - } - - - public async Task ConnectAsync() - { - LoadIPSet(); - Connection = new TcpConnection(ServerIP, PORT); - await ConnectOpenAsync(); - } - - public void Connect() - { - LoadIPSet(); - Task.Run(async () => - { - Connection = new TcpConnection(ServerIP, PORT); - await ConnectOpenAsync(); - } - ); - } - - public void CloseConnection() - { - CommOK = false; - Connection?.Close(); - } - - - - - - - - -/* - - - //获取内网IP - private IPAddress GetInternalIP() - { - NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); - - foreach (NetworkInterface adapter in nics) - { - foreach (var uni in adapter.GetIPProperties().UnicastAddresses) - { - if (uni.Address.AddressFamily == AddressFamily.InterNetwork) - { - return uni.Address; - } - } - } - return null; - } - - /// - /// 获取本地连接IP地址、无线连接IP地址 - /// - /// 0:本地连接,1:本地连接1,2:无线网络连接 - /// - public static IPAddress GetIP(int k) - { - NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); - int len = interfaces.Length; - IPAddress[] mip = { IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.0.0.0") }; - - for (int i = 0; i < len; i++) - { - NetworkInterface ni = interfaces[i]; - - if (ni.Name == "本地连接") - { - IPInterfaceProperties property = ni.GetIPProperties(); - foreach (UnicastIPAddressInformation ip in property.UnicastAddresses) - { - if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - mip[0] = ip.Address; - } - } - } - else if (ni.Name == "本地连接2") - { - IPInterfaceProperties property = ni.GetIPProperties(); - foreach (UnicastIPAddressInformation ip in property.UnicastAddresses) - { - if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - mip[1] = ip.Address; - } - } - } - else if (ni.Name == "无线网络连接") - { - IPInterfaceProperties property = ni.GetIPProperties(); - foreach (UnicastIPAddressInformation ip in property.UnicastAddresses) - { - if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - mip[2] = ip.Address; - } - } - } - } - return mip[k]; - } - - */ - private async Task ConnectOpenAsync() - { - CommOK = true; - //不绑定IP也可以通讯,有线网络需要设置192.168.199.X网段地址 - // string locip = GetIP(0).ToString(); - // bool bind = await Connection.BindIP(locip); - // if (bind) - { - try - { - await Connection.OpenAsync().ConfigureAwait(false); - } - catch - { } - } - - if (Connection.IsOnline) - { - Message.Connect(true); - SendQuery(); - await StartReadingPacketsAsync(); - } - else - { - Message.Connect(false); - Reconnect(); - } - } - - //循环发送查询,保持通信正常 - private void SendQuery() - { - Task.Run(async () => - { - long lastPacketCountNum = 0; - int faulttimes = 0; //等待没有数据次数 - const int revtimeout = 2000; //等待超时ms old=1200 - while (CommOK) - { - if (Connection != null) - { - //发送心跳包-等待回复消息 - await WriteCommPacketAsync(0, MavComm.COMM_QUERY_STATUS); - // await SendQueryStatusPacketsAsync(); - await Task.Delay(revtimeout).ConfigureAwait(false); - //等待是否接受到返回的数据包,如果没有收到认为连结断开,重新联接 - if (packetCountNum > lastPacketCountNum) - { - lastPacketCountNum = packetCountNum; - if (faulttimes>0) - Message.Show("收到新数据包,重置超时次数..."); - faulttimes = 0; - } - else - { - faulttimes++; - Message.Show("设备回复超时("+ revtimeout + "ms)" + faulttimes+"次"); - //2次没数据包返回就重连 - if (faulttimes > 1) - { - Message.Show("长时间未收到设备回复数据包"); - break; - } - } - } - } - Message.Show("主动断开连接,重连"); - Reconnect(); - }).ConfigureAwait(false); - } - - /* //直接调用不用封装函数了 - private async Task SendQueryStatusPacketsAsync() - { - await WriteCommPacketAsync(0, MavComm.COMM_QUERY_STATUS); - await Task.Delay(100);//100 - } - */ - - int times = 1; - private void Reconnect() - { - // Message.Show($"正在重新连接..."); - Task.Run(async () => - { - CloseConnection(); - await Task.Delay(250).ConfigureAwait(false); - //Message.Show($"正在重连:次数{times++}"); - await Task.Delay(250).ConfigureAwait(false); - await ConnectAsync(); - - }).ConfigureAwait(false); - } - - private async Task StartReadingPacketsAsync() - { - await Task.Run(async () => - { - while (CommOK) - { - if (Connection == null || !Connection.IsOnline) - { - if (Connection==null) - Message.Show("空连接异常"); - else if (!Connection.IsOnline) - { - Message.Show("检测到设备已断开连接"); - } - break; - } - - var packet = await ReadPacketAsync().ConfigureAwait(false); - if (packet != null) - { - packetCountNum++; - short serialNum = BitConverter.ToInt16(packet, 4); - short copterNum = BitConverter.ToInt16(packet, 6); - short commandType = BitConverter.ToInt16(packet, 8); - byte[] dealData; - switch (commandType) - { - case MavComm.COMM_DOWNLOAD_MODE: - dealData = packet.Skip(10).ToArray(); - AnalyzeMissionStartPacket(copterNum, dealData); - break; - - case MavComm.COMM_GET_COPTERS_COUNT: - dealData = packet.Skip(16).ToArray(); - AnalyzeCopterInfosPacket(dealData); - break; - - case MavComm.COMM_NOTIFICATION: - short messageType = BitConverter.ToInt16(packet, 10); - dealData = packet.Skip(12).ToArray(); - switch (messageType) - { - case (short)MavComm.MessageType.STR: - AnalyzeStringPacket(copterNum, dealData); - break; - - case (short)MavComm.MessageType.SCAN2: - AnalyzeComm2RetrunPacket(copterNum, dealData); - break; - - case (short)MavComm.MessageType.SCAN3: - AnalyzeComm3RetrunPacket(copterNum, dealData); - break; - case 4: - AnalyzeComm4RetrunPacket(copterNum, dealData); - break; - } - break; - case 0: - int packetLength = packet.Length; - short errorId = BitConverter.ToInt16(packet, packetLength - 6); - short copterCount = BitConverter.ToInt16(packet, packetLength - 4); - short curMode = BitConverter.ToInt16(packet, packetLength - 2); - string msg = string.Format("错误Id={0}, 检测飞机总数={1},工作模式={2} 流水号={3}", errorId, copterCount, curMode, serialNum); - CommModuleCopterCount = copterCount; - CommModuleCurMode = curMode; - //Message.Show(msg); - break; - } - } - } - }).ConfigureAwait(false); - Message.Show("退出读设备数据........"); - //Reconnect(); - } - - private async Task ReadPacketAsync() - { - int readnumber = 0; - if (Connection == null || !Connection.IsOnline) - { - return null; - } - try - { - byte[] bufferHaed = new byte[2]; - readnumber = await Connection.ReadAsync(bufferHaed, 0, 2); - if (bufferHaed[0] == 0x13 && bufferHaed[1] == 0x77) - { - byte[] bufferLength = new byte[2]; - readnumber = await Connection.ReadAsync(bufferLength, 0, 2); - short datalength = BitConverter.ToInt16(bufferLength, 0); - //Console.WriteLine("datalength = "+ datalength); - - byte[] bufferData = new byte[datalength]; - readnumber = await Connection.ReadAsync(bufferData, 0, datalength); - -// foreach (byte b in bufferData) -// { -// Console.Write(b.ToString("X2")); -// Console.Write(" "); -// } -// Console.WriteLine(""); - - byte[] needCRCData = new byte[datalength + 4]; - Array.Copy(bufferHaed, 0, needCRCData, 0, 2); - Array.Copy(bufferLength, 0, needCRCData, 2, 2); - Array.Copy(bufferData, 0, needCRCData, 4, datalength); - - byte[] crc = checkCRC16(needCRCData); - byte[] bufferCRC16 = new byte[2]; - readnumber = await Connection.ReadAsync(bufferCRC16, 0, 2); - - if (crc[0] == bufferCRC16[0] && crc[1] == bufferCRC16[1]) - { - return needCRCData; - } - } - } - catch - { - //Console.WriteLine("错误"); - return null; - } - return null; - } - - - - short serial_Number = 1; - - public async Task GenerateDataAsync(short copterId, short messageType, TMavCommPacket indata) - { - byte[] data; - data = MavlinkUtil.StructureToByteArray(indata); - await WriteCommPacketAsync(copterId, messageType, data); - } - - public async Task UpdateCommModule() - { - MavComm.comm_update_copter_module commUpdate = new MavComm.comm_update_copter_module(); - commUpdate.mav_count = (short)CommModuleCopterCount; - commUpdate.update_code = 0x7713; - await GenerateDataAsync(0, MavComm.COMM_UPDATE_COPTER_MODULE, commUpdate); - } - - - public async Task TestModule(short ModuleNo, short TestLong=100) - { - - - byte[] packet = new byte[2]; - packet[0] = (byte)(ModuleNo); - packet[1] = (byte)(TestLong); - byte[] senddata = packet; - - // Message.Show("长度 = " + senddata.Length); - await WriteCommPacketAsync(0, MavComm.COMM_TEST_MODULE, senddata); - // await Task.Delay(1000).ConfigureAwait(false); - - } - - - - public async Task SetModulePower(short ModuleNo, short ModulePower) - { - byte[] packet = { 0x01, 0x00, 0x03, 0x00, 0x01, 0x0A }; - packet[2] = (byte)(ModuleNo & 0xFF); - packet[3] = (byte)((ModuleNo & 0xFF00) >> 8); - packet[5] = (byte)(ModulePower); - await WriteCommPacketAsync(0, MavComm.COMM_TO_MODULE, packet); - } - - - - - - bool temp = false; - //测试用 灯光间隔1S闪烁 - public async Task TestLED(short id, string colorString) - { - - MAVLink.mavlink_msg_id_led_control led = new MAVLink.mavlink_msg_id_led_control(); - led.target_system = 1; - led.target_component = 1;//(byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_SYSTEM_CONTROL;; - led.instance = 0; - led.pattern = 0; - led.custom_len = 4; //命令类型——测试灯光 - led.custom_bytes = new byte[24]; - - if (colorString == "") colorString = "330033"; - - Color color = (Color)ColorConverter.ConvertFromString("#" + colorString); - - led.custom_bytes[0] = color.R; - led.custom_bytes[1] = color.G; - led.custom_bytes[2] = color.B; - led.custom_bytes[3] = 3; - byte[] data; - data = MavlinkUtil.StructureToByteArray(led); - - byte[] packet = new byte[data.Length + 6 + 2]; - packet[0] = MAVLink.MAVLINK_STX; - - packet[1] = (byte)(data.Length); - packet[2] = 1; - packet[3] = 255; // this is always 255 - MYGCS - - packet[4] = (byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_MISSIONPLANNER; - - packet[5] = MAVLink.MAVLINK_MSG_ID_LED_CONTROL; - int i = 6; - foreach (byte b in data) - { - packet[i] = b; - i++; - } - - ushort checksum = MavlinkCRC.crc_calculate(packet, packet[1] + 6); - checksum = MavlinkCRC.crc_accumulate(MAVLink.MAVLINK_MESSAGE_CRCS[MAVLink.MAVLINK_MSG_ID_LED_CONTROL], checksum); - - byte ck_a = (byte)(checksum & 0xFF); ///< High byte - byte ck_b = (byte)(checksum >> 8); ///< Low byte - - packet[i] = ck_a; - i += 1; - packet[i] = ck_b; - i += 1; - - - //await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - byte[] senddata = packet; - -// for (int times = 0; times < 14; times++) -// { -// senddata = senddata.Concat(packet).ToArray(); -// } - -// byte[] myByteArray = Enumerable.Repeat((byte)0x08, 15).ToArray(); -// -// senddata = senddata.Concat(myByteArray).ToArray(); - - temp = !temp; - while (temp) - { - Message.Show("测试灯光,长度 = " + senddata.Length); - await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, senddata); - await Task.Delay(1000).ConfigureAwait(false); - } - } - - //拉烟测试 - public async Task TestFire(short id, int channel) - { - - MAVLink.mavlink_msg_id_led_control led = new MAVLink.mavlink_msg_id_led_control(); - led.target_system = 1; - led.target_component = 1;//(byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_SYSTEM_CONTROL;; - led.instance = 0; - led.pattern = 0; - led.custom_len = 1;//命令类型——测试拉烟 - led.custom_bytes = new byte[24]; - - led.custom_bytes[0] = (byte)channel; - - byte[] data; - data = MavlinkUtil.StructureToByteArray(led); - - byte[] packet = new byte[data.Length + 6 + 2]; - packet[0] = MAVLink.MAVLINK_STX; - - packet[1] = (byte)(data.Length); - packet[2] = 1; - packet[3] = 255; // this is always 255 - MYGCS - - packet[4] = (byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_MISSIONPLANNER; - - packet[5] = MAVLink.MAVLINK_MSG_ID_LED_CONTROL; - int i = 6; - foreach (byte b in data) - { - packet[i] = b; - i++; - } - - ushort checksum = MavlinkCRC.crc_calculate(packet, packet[1] + 6); - checksum = MavlinkCRC.crc_accumulate(MAVLink.MAVLINK_MESSAGE_CRCS[MAVLink.MAVLINK_MSG_ID_LED_CONTROL], checksum); - - byte ck_a = (byte)(checksum & 0xFF); ///< High byte - byte ck_b = (byte)(checksum >> 8); ///< Low byte - - packet[i] = ck_a; - i += 1; - packet[i] = ck_b; - i += 1; - - - //await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - byte[] senddata = packet; - - for (int times = 0; times < 3; times++) - { - senddata = senddata.Concat(packet).ToArray(); - } - - - temp = !temp; - while (temp) - { - await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - await Task.Delay(1000).ConfigureAwait(false); - } - } - - - public async Task TestBattery(short id, float minivol) - { - - MAVLink.mavlink_msg_id_led_control led = new MAVLink.mavlink_msg_id_led_control(); - led.target_system = 1; - led.target_component = 1;//(byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_SYSTEM_CONTROL;; - led.instance = 0; - led.pattern = 0; - led.custom_len = 5;//命令类型——测试电池电压 - led.custom_bytes = new byte[24]; - - led.custom_bytes[0] = (byte)(int)(minivol); //整数部分 - led.custom_bytes[1] = (byte)(int)((minivol-(int)minivol)*100); //小数部分 --2位 - - - - - - byte[] data; - data = MavlinkUtil.StructureToByteArray(led); - - byte[] packet = new byte[data.Length + 6 + 2]; - packet[0] = MAVLink.MAVLINK_STX; - - packet[1] = (byte)(data.Length); - packet[2] = 1; - packet[3] = 255; // this is always 255 - MYGCS - - packet[4] = (byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_MISSIONPLANNER; - - packet[5] = MAVLink.MAVLINK_MSG_ID_LED_CONTROL; - int i = 6; - foreach (byte b in data) - { - packet[i] = b; - i++; - } - - ushort checksum = MavlinkCRC.crc_calculate(packet, packet[1] + 6); - checksum = MavlinkCRC.crc_accumulate(MAVLink.MAVLINK_MESSAGE_CRCS[MAVLink.MAVLINK_MSG_ID_LED_CONTROL], checksum); - - byte ck_a = (byte)(checksum & 0xFF); ///< High byte - byte ck_b = (byte)(checksum >> 8); ///< Low byte - - packet[i] = ck_a; - i += 1; - packet[i] = ck_b; - i += 1; - - - //await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - byte[] senddata = packet; - - for (int times = 0; times < 3; times++) - { - senddata = senddata.Concat(packet).ToArray(); - } - - - { //发3次 - await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - await Task.Delay(1000).ConfigureAwait(false); - await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - await Task.Delay(1000).ConfigureAwait(false); - await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); - - } - } - - - /// - /// 生成通信模块packet并且发送 - /// - /// 飞机ID - /// 命令类型 - /// 数据类型:空表示只切换模式 - /// 小批量数据包 - /// - public async Task WriteCommPacketAsync(short copterId, short messageType, byte[] data = null, byte[] batchPacket = null) - { - //新盒子的协议--暂时不用-需要问张伟 - /* - if (messageType == MavComm.COMM_DOWNLOAD_COMM && copterId == 0) - { - short byteNum; - short length; - if (batchPacket == null) - { - byteNum = 0; - length = (short)((0x5 << 12) ^ byteNum); - batchPacket = BitConverter.GetBytes(length); - } - else - { - byteNum = (short)(batchPacket.Length / 2); - length = (short)((0x5 << 12) ^ byteNum); - batchPacket = BitConverter.GetBytes(length).Concat(batchPacket).ToArray(); - } - } - */ - if (data != null && batchPacket != null) data = batchPacket.Concat(data).ToArray(); - - int packetlength = data == null ? 0 : data.Length; - - //数据长度+10 - byte[] packet = new byte[packetlength + 10]; - - byte[] buffer_header = new byte[2]; - buffer_header = BitConverter.GetBytes(MavComm.COMM_SEND_PACKET_HEADER); - Array.Copy(buffer_header, 0, packet, 0, 2); //发送数据头 2字节 - - byte[] buffer_length; - buffer_length = BitConverter.GetBytes((Int16)(packetlength + 6)); - Array.Copy(buffer_length, 0, packet, 2, 2); //数据长度 2字节 - - byte[] buffer_serial; - buffer_serial = BitConverter.GetBytes(serial_Number++); - Array.Copy(buffer_serial, 0, packet, 4, 2); //流水号 2字节 - - byte[] buffer_copterId = new byte[2]; - buffer_copterId = BitConverter.GetBytes((Int16)copterId); - Array.Copy(buffer_copterId, 0, packet, 6, 2); //飞机号 2字节 - - byte[] buffer_messageType = new byte[2]; - buffer_messageType = BitConverter.GetBytes((Int16)messageType); - Array.Copy(buffer_messageType, 0, packet, 8, 2); //命令类型 2字节 - - if (data != null) - Array.Copy(data, 0, packet, 10, data.Length); //数据内容 10字节开始 - - byte[] buffer_CRC = checkCRC16(packet); //计算所有数据的crc - - byte[] buffer_packet = new byte[packet.Length + 2]; //分配新的带crc的数据包 - Array.Copy(packet, buffer_packet, packet.Length); - Array.Copy(buffer_CRC, 0, buffer_packet, packet.Length, 2); //带crc的数据包 - - if (Connection != null && Connection.IsOnline) - { - //if (messageType==83) - // Console.WriteLine("RTCM:" + (ushort)rtcm.length + "[" + rtcm.packet[0] + "," + rtcm.packet[1] + "," + rtcm.packet[2] + "," + rtcm.packet[3] + "," + rtcm.packet[4] + "]"); - // Console.WriteLine("[{0}]Send rtcm:{1}", buffer_serial[0], packetlength); - try - { - await Connection.WriteAsync(buffer_packet, 0, buffer_packet.Length); - } - catch - { - - } - } - - } - - public async Task MissionPacket(short copterId, byte messageType, TMavCommPacket[] indata) - { - //数据长度 - int dataLength = 6 + 2 + indata.Length * 32; - byte[] data = new byte[dataLength]; - - byte[] uses = new byte[] { 0, 0, 1, 0, 0, 0 }; - //填充初始数据 - Array.Copy(uses, 0, data, 0, 6); - - Int16 countNum = (Int16)indata.Length; - Array.Copy(BitConverter.GetBytes(countNum), 0, data, 6, 2); - - - //新盒子的协议--暂时不用-需要问张伟 - /* - Int16 countNum = (Int16)indata.Length; - - byte[] uses = new byte[] { 0, 1, 0, 0, 0, 0 }; - - Array.Copy(uses, 0, data, 0, 6); - - Array.Copy(BitConverter.GetBytes(countNum), 0, data, 2, 2); - Array.Copy(BitConverter.GetBytes(countNum), 0, data, 6, 2); - */ - - - - - int offset = 8; - - foreach (TMavCommPacket MavPacket in indata) - { - byte[] MavPacketData = MavlinkUtil.StructureToByteArray(MavPacket); - byte[] tempData = new byte[32]; - - //张伟只需要32个有用数据 0-27, 30-31, 34-35 - Array.Copy(MavPacketData, 0, tempData, 0, 28); - Array.Copy(MavPacketData, 30, tempData, 28, 2); - Array.Copy(MavPacketData, 34, tempData, 30, 2); - - Array.Copy(tempData, 0, data, offset, 32); - offset += 32; - } - - await WriteCommPacketAsync(copterId, messageType, data); - - if (_writeMissionStopwatch == null) _writeMissionStopwatch = Stopwatch.StartNew(); - else _writeMissionStopwatch.Restart(); - - //先延时5秒判断通信模块是否返回错误ID,0为正确。 如果已经错误了就不需要等待下发了。 - MissionStartCopterId = 0; MissionErrorId = -1; - ErrorCode = 0; - - if (await AwaitCommMissionStartAsync(copterId, 5000)) - { - _writeMissionStopwatch.Restart(); - MissionSendCopterId = 0; - //等待通信模块地面站数据下发后,才能开始下个飞机的航点写入 - //下发不代表写入成功 - return await AwaitCommMissionRequestAsync(copterId, 10000).ConfigureAwait(false); - } - else - return false; - } - - public async Task AwaitCommMissionStartAsync(short copterId, int millisecondsTimeout) - { - while (MissionStartCopterId != copterId) - { - if (_writeMissionStopwatch.ElapsedMilliseconds >= millisecondsTimeout) - { - return false; - } - await Task.Delay(50).ConfigureAwait(false); - } - if (MissionErrorId == 0) - return true; - else - { - //Message.Show($"{copterId}:返回错误--{MissionErrorId}"); - return false; - } - } - - public async Task AwaitCommMissionRequestAsync(short copterId, int millisecondsTimeout) - { - while (MissionSendCopterId != copterId) - { - if (_writeMissionStopwatch.ElapsedMilliseconds >= millisecondsTimeout || ErrorCode != 0) - { - return false; - } - await Task.Delay(50).ConfigureAwait(false); - } - return true; - } - - private long GetCreatetime() - { - DateTime DateStart = new DateTime(2000, 1, 1, 0, 0, 0); - return Convert.ToInt64((DateTime.Now - DateStart).TotalMilliseconds); - } - - public static byte[] CRC16(byte[] data) - { - int len = data.Length; - if (len > 0) - { - ushort crc = 0xFFFF; - - for (int i = 0; i < len; i++) - { - crc = (ushort)(crc ^ (data[i])); - for (int j = 0; j < 8; j++) - { - crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1); - } - } - byte hi = (byte)((crc & 0xFF00) >> 8); //高位置 - byte lo = (byte)(crc & 0x00FF); //低位置 - return new byte[] { hi, lo }; - } - return new byte[] { 0, 0 }; - } - - public byte[] CRCCalc(byte[] crcbuf) - { - //计算并填写CRC校验码 - int crc = 0xffff; - int len = crcbuf.Length; - for (int n = 0; n < len; n++) - { - byte i; - crc = crc ^ crcbuf[n]; - for (i = 0; i < 8; i++) - { - int TT; - TT = crc & 1; - crc = crc >> 1; - crc = crc & 0x7fff; - if (TT == 1) - { - crc = crc ^ 0xa001; - } - crc = crc & 0xffff; - } - - } - byte[] redata = new byte[2]; - redata[1] = (byte)((crc >> 8) & 0xff); - redata[0] = (byte)((crc & 0xff)); - return redata; - } - - public byte[] checkCRC16(byte[] puchMsg) - { - - int usDataLen = puchMsg.Length; - - ushort iTemp = 0x0; - ushort flag = 0x0; - - for (int i = 0; i < usDataLen; i++) - { - iTemp ^= puchMsg[i]; - for (int j = 0; j < 8; j++) - { - flag = (ushort)(iTemp & 0x01); - iTemp >>= 1; - if (flag == 1) - { - iTemp ^= 0xa001; - } - } - } - byte[] ret = BitConverter.GetBytes(iTemp); - return ret; - } - - public void Dispose() - { - if (!CommOK) - { - return; - } - CommOK = false; - CloseConnection(); - } - } -} +using Plane.Communication; +using Plane.Protocols; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +using System.Runtime.InteropServices; +using Plane.Windows.Messages; + +namespace Plane.CommunicationManagement +{ + //通信模块 + public partial class CommModuleManager: ExceptionThrownEventSource, IDisposable + { + [DllImport("kernel32")] //返回取得字符串缓冲区的长度 + private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); + + private static CommModuleManager _Instance = new CommModuleManager(); + public static CommModuleManager Instance { get { return _Instance; } } + + public TcpConnection Connection = null; + public bool CommOK = false; + private const string MODULE_IP = "192.168.199.51"; //"192.168.199.51"; // "192.168.199.51"; //在用版本 ----//"192.168.1.65"; //新版本 + private string ServerIP = MODULE_IP; + // private const string LOCAL_IP = "192.168.199.50"; //"192.168.199.50"; + private const int PORT = 9551; + private bool _disposed; + public int CommModuleCopterCount = 0; + public int CommModuleCurMode = 0; + private long packetCountNum = 0; + + + /// + /// 用于判断写入任务操作超时的秒表。 + /// + private Stopwatch _writeMissionStopwatch; + + private void LoadIPSet() + { + StringBuilder temp = new StringBuilder(255); + + string path = Environment.CurrentDirectory + @"\Config.ini"; + + int i = GetPrivateProfileString("Default", "ServerIP", "", temp, 255, path); + ServerIP= temp.ToString(); + + if (ServerIP == "") ServerIP = MODULE_IP; + } + + + public async Task ConnectAsync() + { + LoadIPSet(); + Connection = new TcpConnection(ServerIP, PORT); + await ConnectOpenAsync(); + } + + public void Connect() + { + LoadIPSet(); + Task.Run(async () => + { + Connection = new TcpConnection(ServerIP, PORT); + await ConnectOpenAsync(); + } + ); + } + + public void CloseConnection() + { + CommOK = false; + Connection?.Close(); + } + + + + + + + + +/* + + + //获取内网IP + private IPAddress GetInternalIP() + { + NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + + foreach (NetworkInterface adapter in nics) + { + foreach (var uni in adapter.GetIPProperties().UnicastAddresses) + { + if (uni.Address.AddressFamily == AddressFamily.InterNetwork) + { + return uni.Address; + } + } + } + return null; + } + + /// + /// 获取本地连接IP地址、无线连接IP地址 + /// + /// 0:本地连接,1:本地连接1,2:无线网络连接 + /// + public static IPAddress GetIP(int k) + { + NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); + int len = interfaces.Length; + IPAddress[] mip = { IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.0.0.0") }; + + for (int i = 0; i < len; i++) + { + NetworkInterface ni = interfaces[i]; + + if (ni.Name == "本地连接") + { + IPInterfaceProperties property = ni.GetIPProperties(); + foreach (UnicastIPAddressInformation ip in property.UnicastAddresses) + { + if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + mip[0] = ip.Address; + } + } + } + else if (ni.Name == "本地连接2") + { + IPInterfaceProperties property = ni.GetIPProperties(); + foreach (UnicastIPAddressInformation ip in property.UnicastAddresses) + { + if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + mip[1] = ip.Address; + } + } + } + else if (ni.Name == "无线网络连接") + { + IPInterfaceProperties property = ni.GetIPProperties(); + foreach (UnicastIPAddressInformation ip in property.UnicastAddresses) + { + if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + mip[2] = ip.Address; + } + } + } + } + return mip[k]; + } + + */ + private async Task ConnectOpenAsync() + { + CommOK = true; + //不绑定IP也可以通讯,有线网络需要设置192.168.199.X网段地址 + // string locip = GetIP(0).ToString(); + // bool bind = await Connection.BindIP(locip); + // if (bind) + { + try + { + if (!Connection.IsOnline) + await Connection.OpenAsync().ConfigureAwait(false); + } + catch + { } + } + + if (Connection.IsOnline) + { + Message.Connect(true); + Message.Show($"通讯基站连接成功!"); + times = 0; + SendQuery(); + await StartReadingPacketsAsync(); + } + else + { + Message.Connect(false); + Reconnect(); + } + } + + //循环发送查询,保持通信正常 + private void SendQuery() + { + Task.Run(async () => + { + long lastPacketCountNum = 0; + int faulttimes = 0; //等待没有数据次数 + const int revtimeout = 2000; //等待超时ms old=1200 + while (CommOK) + { + if (Connection != null) + { + //发送心跳包-等待回复消息 + await WriteCommPacketAsync(0, MavComm.COMM_QUERY_STATUS); + // await SendQueryStatusPacketsAsync(); + await Task.Delay(revtimeout).ConfigureAwait(false); + //等待是否接受到返回的数据包,如果没有收到认为连结断开,重新联接 + if (packetCountNum > lastPacketCountNum) + { + lastPacketCountNum = packetCountNum; + if (faulttimes>0) + Message.Show("收到新数据包,重置超时次数..."); + faulttimes = 0; + } + else + { + faulttimes++; + Message.Show("设备回复超时("+ revtimeout + "ms)" + faulttimes+"次"); + //2次没数据包返回就重连 + if (faulttimes > 1) + { + Message.Show("长时间未收到设备回复数据包"); + break; + } + } + } + } + Message.Show("主动断开连接,重连"); + Reconnect(); + }).ConfigureAwait(false); + } + + /* //直接调用不用封装函数了 + private async Task SendQueryStatusPacketsAsync() + { + await WriteCommPacketAsync(0, MavComm.COMM_QUERY_STATUS); + await Task.Delay(100);//100 + } + */ + + int times = 0; + private void Reconnect() + { + // Message.Show($"正在重新连接..."); + Task.Run(async () => + { + CloseConnection(); + await Task.Delay(250).ConfigureAwait(false); + Message.Show($"正在重连:次数{++times}"); + await Task.Delay(250).ConfigureAwait(false); + await ConnectAsync(); + + }).ConfigureAwait(false); + } + + private async Task StartReadingPacketsAsync() + { + await Task.Run(async () => + { + while (CommOK) + { + if (Connection == null || !Connection.IsOnline) + { + if (Connection==null) + Message.Show("空连接异常"); + else if (!Connection.IsOnline) + { + Message.Show("检测到设备已断开连接"); + } + break; + } + + var packet = await ReadPacketAsync().ConfigureAwait(false); + if (packet != null) + { + packetCountNum++; + short serialNum = BitConverter.ToInt16(packet, 4); + short copterNum = BitConverter.ToInt16(packet, 6); + short commandType = BitConverter.ToInt16(packet, 8); + byte[] dealData; + switch (commandType) + { + case MavComm.COMM_DOWNLOAD_MODE: + dealData = packet.Skip(10).ToArray(); + AnalyzeMissionStartPacket(copterNum, dealData); + break; + + case MavComm.COMM_GET_COPTERS_COUNT: + dealData = packet.Skip(16).ToArray(); + AnalyzeCopterInfosPacket(dealData); + break; + + case MavComm.COMM_NOTIFICATION: + short messageType = BitConverter.ToInt16(packet, 10); + dealData = packet.Skip(12).ToArray(); + switch (messageType) + { + case (short)MavComm.MessageType.STR: + AnalyzeStringPacket(copterNum, dealData); + break; + + case (short)MavComm.MessageType.SCAN2: + AnalyzeComm2RetrunPacket(copterNum, dealData); + break; + + case (short)MavComm.MessageType.SCAN3: + AnalyzeComm3RetrunPacket(copterNum, dealData); + break; + case 4: + AnalyzeComm4RetrunPacket(copterNum, dealData); + break; + } + break; + case 0: + int packetLength = packet.Length; + short errorId = BitConverter.ToInt16(packet, packetLength - 6); + short copterCount = BitConverter.ToInt16(packet, packetLength - 4); + short curMode = BitConverter.ToInt16(packet, packetLength - 2); + string msg = string.Format("错误Id={0}, 检测飞机总数={1},工作模式={2} 流水号={3}", errorId, copterCount, curMode, serialNum); + CommModuleCopterCount = copterCount; + CommModuleCurMode = curMode; + //Message.Show(msg); + break; + } + } + } + }).ConfigureAwait(false); + Message.Show("退出读设备数据........"); + //Reconnect(); + } + + private async Task ReadPacketAsync() + { + int readnumber = 0; + if (Connection == null || !Connection.IsOnline) + { + return null; + } + try + { + byte[] bufferHaed = new byte[2]; + readnumber = await Connection.ReadAsync(bufferHaed, 0, 2); + if (bufferHaed[0] == 0x13 && bufferHaed[1] == 0x77) + { + byte[] bufferLength = new byte[2]; + readnumber = await Connection.ReadAsync(bufferLength, 0, 2); + short datalength = BitConverter.ToInt16(bufferLength, 0); + //Console.WriteLine("datalength = "+ datalength); + + byte[] bufferData = new byte[datalength]; + readnumber = await Connection.ReadAsync(bufferData, 0, datalength); + +// foreach (byte b in bufferData) +// { +// Console.Write(b.ToString("X2")); +// Console.Write(" "); +// } +// Console.WriteLine(""); + + byte[] needCRCData = new byte[datalength + 4]; + Array.Copy(bufferHaed, 0, needCRCData, 0, 2); + Array.Copy(bufferLength, 0, needCRCData, 2, 2); + Array.Copy(bufferData, 0, needCRCData, 4, datalength); + + byte[] crc = checkCRC16(needCRCData); + byte[] bufferCRC16 = new byte[2]; + readnumber = await Connection.ReadAsync(bufferCRC16, 0, 2); + + if (crc[0] == bufferCRC16[0] && crc[1] == bufferCRC16[1]) + { + return needCRCData; + } + } + } + catch + { + //Console.WriteLine("错误"); + return null; + } + return null; + } + + + + short serial_Number = 1; + + public async Task GenerateDataAsync(short copterId, short messageType, TMavCommPacket indata) + { + byte[] data; + data = MavlinkUtil.StructureToByteArray(indata); + await WriteCommPacketAsync(copterId, messageType, data); + } + + public async Task UpdateCommModule() + { + MavComm.comm_update_copter_module commUpdate = new MavComm.comm_update_copter_module(); + commUpdate.mav_count = (short)CommModuleCopterCount; + commUpdate.update_code = 0x7713; + await GenerateDataAsync(0, MavComm.COMM_UPDATE_COPTER_MODULE, commUpdate); + } + + + public async Task TestModule(short ModuleNo, short TestLong=100) + { + + + byte[] packet = new byte[2]; + packet[0] = (byte)(ModuleNo); + packet[1] = (byte)(TestLong); + byte[] senddata = packet; + + // Message.Show("长度 = " + senddata.Length); + await WriteCommPacketAsync(0, MavComm.COMM_TEST_MODULE, senddata); + // await Task.Delay(1000).ConfigureAwait(false); + + } + + + + public async Task SetModulePower(short ModuleNo, short ModulePower) + { + byte[] packet = { 0x01, 0x00, 0x03, 0x00, 0x01, 0x0A }; + packet[2] = (byte)(ModuleNo & 0xFF); + packet[3] = (byte)((ModuleNo & 0xFF00) >> 8); + packet[5] = (byte)(ModulePower); + await WriteCommPacketAsync(0, MavComm.COMM_TO_MODULE, packet); + } + + + + + + bool temp = false; + //测试用 灯光间隔1S闪烁 + public async Task TestLED(short id, string colorString) + { + + MAVLink.mavlink_msg_id_led_control led = new MAVLink.mavlink_msg_id_led_control(); + led.target_system = 1; + led.target_component = 1;//(byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_SYSTEM_CONTROL;; + led.instance = 0; + led.pattern = 0; + led.custom_len = 4; //命令类型——测试灯光 + led.custom_bytes = new byte[24]; + + if (colorString == "") colorString = "330033"; + + Color color = (Color)ColorConverter.ConvertFromString("#" + colorString); + + led.custom_bytes[0] = color.R; + led.custom_bytes[1] = color.G; + led.custom_bytes[2] = color.B; + led.custom_bytes[3] = 3; + byte[] data; + data = MavlinkUtil.StructureToByteArray(led); + + byte[] packet = new byte[data.Length + 6 + 2]; + packet[0] = MAVLink.MAVLINK_STX; + + packet[1] = (byte)(data.Length); + packet[2] = 1; + packet[3] = 255; // this is always 255 - MYGCS + + packet[4] = (byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_MISSIONPLANNER; + + packet[5] = MAVLink.MAVLINK_MSG_ID_LED_CONTROL; + int i = 6; + foreach (byte b in data) + { + packet[i] = b; + i++; + } + + ushort checksum = MavlinkCRC.crc_calculate(packet, packet[1] + 6); + checksum = MavlinkCRC.crc_accumulate(MAVLink.MAVLINK_MESSAGE_CRCS[MAVLink.MAVLINK_MSG_ID_LED_CONTROL], checksum); + + byte ck_a = (byte)(checksum & 0xFF); ///< High byte + byte ck_b = (byte)(checksum >> 8); ///< Low byte + + packet[i] = ck_a; + i += 1; + packet[i] = ck_b; + i += 1; + + + //await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + byte[] senddata = packet; + +// for (int times = 0; times < 14; times++) +// { +// senddata = senddata.Concat(packet).ToArray(); +// } + +// byte[] myByteArray = Enumerable.Repeat((byte)0x08, 15).ToArray(); +// +// senddata = senddata.Concat(myByteArray).ToArray(); + + temp = !temp; + while (temp) + { + Message.Show("测试灯光,长度 = " + senddata.Length); + await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, senddata); + await Task.Delay(1000).ConfigureAwait(false); + } + } + + //拉烟测试 + public async Task TestFire(short id, int channel) + { + + MAVLink.mavlink_msg_id_led_control led = new MAVLink.mavlink_msg_id_led_control(); + led.target_system = 1; + led.target_component = 1;//(byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_SYSTEM_CONTROL;; + led.instance = 0; + led.pattern = 0; + led.custom_len = 1;//命令类型——测试拉烟 + led.custom_bytes = new byte[24]; + + led.custom_bytes[0] = (byte)channel; + + byte[] data; + data = MavlinkUtil.StructureToByteArray(led); + + byte[] packet = new byte[data.Length + 6 + 2]; + packet[0] = MAVLink.MAVLINK_STX; + + packet[1] = (byte)(data.Length); + packet[2] = 1; + packet[3] = 255; // this is always 255 - MYGCS + + packet[4] = (byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_MISSIONPLANNER; + + packet[5] = MAVLink.MAVLINK_MSG_ID_LED_CONTROL; + int i = 6; + foreach (byte b in data) + { + packet[i] = b; + i++; + } + + ushort checksum = MavlinkCRC.crc_calculate(packet, packet[1] + 6); + checksum = MavlinkCRC.crc_accumulate(MAVLink.MAVLINK_MESSAGE_CRCS[MAVLink.MAVLINK_MSG_ID_LED_CONTROL], checksum); + + byte ck_a = (byte)(checksum & 0xFF); ///< High byte + byte ck_b = (byte)(checksum >> 8); ///< Low byte + + packet[i] = ck_a; + i += 1; + packet[i] = ck_b; + i += 1; + + + //await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + byte[] senddata = packet; + + for (int times = 0; times < 3; times++) + { + senddata = senddata.Concat(packet).ToArray(); + } + + + temp = !temp; + while (temp) + { + await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + await Task.Delay(1000).ConfigureAwait(false); + } + } + + + public async Task TestBattery(short id, float minivol) + { + + MAVLink.mavlink_msg_id_led_control led = new MAVLink.mavlink_msg_id_led_control(); + led.target_system = 1; + led.target_component = 1;//(byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_SYSTEM_CONTROL;; + led.instance = 0; + led.pattern = 0; + led.custom_len = 5;//命令类型——测试电池电压 + led.custom_bytes = new byte[24]; + + led.custom_bytes[0] = (byte)(int)(minivol); //整数部分 + led.custom_bytes[1] = (byte)(int)((minivol-(int)minivol)*100); //小数部分 --2位 + + + + + + byte[] data; + data = MavlinkUtil.StructureToByteArray(led); + + byte[] packet = new byte[data.Length + 6 + 2]; + packet[0] = MAVLink.MAVLINK_STX; + + packet[1] = (byte)(data.Length); + packet[2] = 1; + packet[3] = 255; // this is always 255 - MYGCS + + packet[4] = (byte)MAVLink.MAV_COMPONENT.MAV_COMP_ID_MISSIONPLANNER; + + packet[5] = MAVLink.MAVLINK_MSG_ID_LED_CONTROL; + int i = 6; + foreach (byte b in data) + { + packet[i] = b; + i++; + } + + ushort checksum = MavlinkCRC.crc_calculate(packet, packet[1] + 6); + checksum = MavlinkCRC.crc_accumulate(MAVLink.MAVLINK_MESSAGE_CRCS[MAVLink.MAVLINK_MSG_ID_LED_CONTROL], checksum); + + byte ck_a = (byte)(checksum & 0xFF); ///< High byte + byte ck_b = (byte)(checksum >> 8); ///< Low byte + + packet[i] = ck_a; + i += 1; + packet[i] = ck_b; + i += 1; + + + //await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + byte[] senddata = packet; + + for (int times = 0; times < 3; times++) + { + senddata = senddata.Concat(packet).ToArray(); + } + + + { //发3次 + await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + await Task.Delay(1000).ConfigureAwait(false); + await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + await Task.Delay(1000).ConfigureAwait(false); + await WriteCommPacketAsync(id, MavComm.COMM_DOWNLOAD_COMM, packet); + + } + } + + + /// + /// 生成通信模块packet并且发送 + /// + /// 飞机ID + /// 命令类型 + /// 数据类型:空表示只切换模式 + /// 小批量数据包 + /// + public async Task WriteCommPacketAsync(short copterId, short messageType, byte[] data = null, byte[] batchPacket = null) + { + //新盒子的协议--暂时不用-需要问张伟 + /* + if (messageType == MavComm.COMM_DOWNLOAD_COMM && copterId == 0) + { + short byteNum; + short length; + if (batchPacket == null) + { + byteNum = 0; + length = (short)((0x5 << 12) ^ byteNum); + batchPacket = BitConverter.GetBytes(length); + } + else + { + byteNum = (short)(batchPacket.Length / 2); + length = (short)((0x5 << 12) ^ byteNum); + batchPacket = BitConverter.GetBytes(length).Concat(batchPacket).ToArray(); + } + } + */ + if (data != null && batchPacket != null) data = batchPacket.Concat(data).ToArray(); + + int packetlength = data == null ? 0 : data.Length; + + //数据长度+10 + byte[] packet = new byte[packetlength + 10]; + + byte[] buffer_header = new byte[2]; + buffer_header = BitConverter.GetBytes(MavComm.COMM_SEND_PACKET_HEADER); + Array.Copy(buffer_header, 0, packet, 0, 2); //发送数据头 2字节 + + byte[] buffer_length; + buffer_length = BitConverter.GetBytes((Int16)(packetlength + 6)); + Array.Copy(buffer_length, 0, packet, 2, 2); //数据长度 2字节 + + byte[] buffer_serial; + buffer_serial = BitConverter.GetBytes(serial_Number++); + Array.Copy(buffer_serial, 0, packet, 4, 2); //流水号 2字节 + + byte[] buffer_copterId = new byte[2]; + buffer_copterId = BitConverter.GetBytes((Int16)copterId); + Array.Copy(buffer_copterId, 0, packet, 6, 2); //飞机号 2字节 + + byte[] buffer_messageType = new byte[2]; + buffer_messageType = BitConverter.GetBytes((Int16)messageType); + Array.Copy(buffer_messageType, 0, packet, 8, 2); //命令类型 2字节 + + if (data != null) + Array.Copy(data, 0, packet, 10, data.Length); //数据内容 10字节开始 + + byte[] buffer_CRC = checkCRC16(packet); //计算所有数据的crc + + byte[] buffer_packet = new byte[packet.Length + 2]; //分配新的带crc的数据包 + Array.Copy(packet, buffer_packet, packet.Length); + Array.Copy(buffer_CRC, 0, buffer_packet, packet.Length, 2); //带crc的数据包 + + if (Connection != null && Connection.IsOnline) + { + //if (messageType==83) + // Console.WriteLine("RTCM:" + (ushort)rtcm.length + "[" + rtcm.packet[0] + "," + rtcm.packet[1] + "," + rtcm.packet[2] + "," + rtcm.packet[3] + "," + rtcm.packet[4] + "]"); + // Console.WriteLine("[{0}]Send rtcm:{1}", buffer_serial[0], packetlength); + try + { + await Connection.WriteAsync(buffer_packet, 0, buffer_packet.Length); + } + catch + { + + } + } + + } + + public async Task MissionPacket(short copterId, byte messageType, TMavCommPacket[] indata) + { + //数据长度 + int dataLength = 6 + 2 + indata.Length * 32; + byte[] data = new byte[dataLength]; + + byte[] uses = new byte[] { 0, 0, 1, 0, 0, 0 }; + //填充初始数据 + Array.Copy(uses, 0, data, 0, 6); + + Int16 countNum = (Int16)indata.Length; + Array.Copy(BitConverter.GetBytes(countNum), 0, data, 6, 2); + + + //新盒子的协议--暂时不用-需要问张伟 + /* + Int16 countNum = (Int16)indata.Length; + + byte[] uses = new byte[] { 0, 1, 0, 0, 0, 0 }; + + Array.Copy(uses, 0, data, 0, 6); + + Array.Copy(BitConverter.GetBytes(countNum), 0, data, 2, 2); + Array.Copy(BitConverter.GetBytes(countNum), 0, data, 6, 2); + */ + + + + + int offset = 8; + + foreach (TMavCommPacket MavPacket in indata) + { + byte[] MavPacketData = MavlinkUtil.StructureToByteArray(MavPacket); + byte[] tempData = new byte[32]; + + //张伟只需要32个有用数据 0-27, 30-31, 34-35 + Array.Copy(MavPacketData, 0, tempData, 0, 28); + Array.Copy(MavPacketData, 30, tempData, 28, 2); + Array.Copy(MavPacketData, 34, tempData, 30, 2); + + Array.Copy(tempData, 0, data, offset, 32); + offset += 32; + } + + await WriteCommPacketAsync(copterId, messageType, data); + + if (_writeMissionStopwatch == null) _writeMissionStopwatch = Stopwatch.StartNew(); + else _writeMissionStopwatch.Restart(); + + //先延时5秒判断通信模块是否返回错误ID,0为正确。 如果已经错误了就不需要等待下发了。 + MissionStartCopterId = 0; MissionErrorId = -1; + ErrorCode = 0; + + if (await AwaitCommMissionStartAsync(copterId, 5000)) + { + _writeMissionStopwatch.Restart(); + MissionSendCopterId = 0; + //等待通信模块地面站数据下发后,才能开始下个飞机的航点写入 + //下发不代表写入成功 + return await AwaitCommMissionRequestAsync(copterId, 10000).ConfigureAwait(false); + } + else + return false; + } + + public async Task AwaitCommMissionStartAsync(short copterId, int millisecondsTimeout) + { + while (MissionStartCopterId != copterId) + { + if (_writeMissionStopwatch.ElapsedMilliseconds >= millisecondsTimeout) + { + return false; + } + await Task.Delay(50).ConfigureAwait(false); + } + if (MissionErrorId == 0) + return true; + else + { + //Message.Show($"{copterId}:返回错误--{MissionErrorId}"); + return false; + } + } + + public async Task AwaitCommMissionRequestAsync(short copterId, int millisecondsTimeout) + { + while (MissionSendCopterId != copterId) + { + if (_writeMissionStopwatch.ElapsedMilliseconds >= millisecondsTimeout || ErrorCode != 0) + { + return false; + } + await Task.Delay(50).ConfigureAwait(false); + } + return true; + } + + private long GetCreatetime() + { + DateTime DateStart = new DateTime(2000, 1, 1, 0, 0, 0); + return Convert.ToInt64((DateTime.Now - DateStart).TotalMilliseconds); + } + + public static byte[] CRC16(byte[] data) + { + int len = data.Length; + if (len > 0) + { + ushort crc = 0xFFFF; + + for (int i = 0; i < len; i++) + { + crc = (ushort)(crc ^ (data[i])); + for (int j = 0; j < 8; j++) + { + crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1); + } + } + byte hi = (byte)((crc & 0xFF00) >> 8); //高位置 + byte lo = (byte)(crc & 0x00FF); //低位置 + return new byte[] { hi, lo }; + } + return new byte[] { 0, 0 }; + } + + public byte[] CRCCalc(byte[] crcbuf) + { + //计算并填写CRC校验码 + int crc = 0xffff; + int len = crcbuf.Length; + for (int n = 0; n < len; n++) + { + byte i; + crc = crc ^ crcbuf[n]; + for (i = 0; i < 8; i++) + { + int TT; + TT = crc & 1; + crc = crc >> 1; + crc = crc & 0x7fff; + if (TT == 1) + { + crc = crc ^ 0xa001; + } + crc = crc & 0xffff; + } + + } + byte[] redata = new byte[2]; + redata[1] = (byte)((crc >> 8) & 0xff); + redata[0] = (byte)((crc & 0xff)); + return redata; + } + + public byte[] checkCRC16(byte[] puchMsg) + { + + int usDataLen = puchMsg.Length; + + ushort iTemp = 0x0; + ushort flag = 0x0; + + for (int i = 0; i < usDataLen; i++) + { + iTemp ^= puchMsg[i]; + for (int j = 0; j < 8; j++) + { + flag = (ushort)(iTemp & 0x01); + iTemp >>= 1; + if (flag == 1) + { + iTemp ^= 0xa001; + } + } + } + byte[] ret = BitConverter.GetBytes(iTemp); + return ret; + } + + public void Dispose() + { + if (!CommOK) + { + return; + } + CommOK = false; + CloseConnection(); + } + } +} diff --git a/PlaneGcsSdk_Shared/CopterControllers/GuidController.cs b/PlaneGcsSdk_Shared/CopterControllers/GuidController.cs index bc7de4f..a3e504d 100644 --- a/PlaneGcsSdk_Shared/CopterControllers/GuidController.cs +++ b/PlaneGcsSdk_Shared/CopterControllers/GuidController.cs @@ -1,347 +1,355 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Plane.CopterControllers -{ - class GuidController - { - - static float fabsf(float vvalue) - { - return Math.Abs(vvalue); - - } - static float sqrt(float vvalue) - { - return (float)Math.Sqrt(vvalue); - - } - - - /// - /// 计算小航点飞行 - /// - /// - /// - /// - /// - - //单算减速,不算加速的最小飞行时间 - static float getMinfligthtime_v2(float Distance, float fc_acc, float fc_maxspeed) - { - float fDis = fabsf(Distance); // 总距离 - float facc = fabsf(fc_acc); // 减速度 - - // 物体开始时即以最大速度运动,不考虑加速过程 - float vel = fc_maxspeed; - - // 计算减速所需的时间和距离 - // 减速时间 (从最大速度减速到0) - float dectime = vel / facc; - // 减速阶段覆盖的距离 - float decdis = (vel * dectime) - (0.5f * facc * dectime * dectime); - - // 判断是否有足够的距离进行减速 - if (decdis >= fDis) - { - // 如果减速所需距离已经超过或等于总距离,调整最大速度 - // 使用公式 v^2 = u^2 + 2as 解出 v - vel = (float)Math.Sqrt(2 * facc * fDis); - // 重新计算减速时间 - dectime = vel / facc; - } - - // 计算匀速阶段时间 - float unftime = 0.0f; - if (decdis < fDis) - { - // 如果有剩余距离进行匀速运动 - unftime = (fDis - decdis) / vel; - } - - // 总飞行时间 = 匀速阶段时间 + 减速阶段时间 - return unftime + dectime; - } - - //单算减速,不算加速的最大飞行速度 - public static float CalculateMaximumVelocity(float distance, float time, float acceleration) - { - // 二次方程系数 - float a_coeff = 1; - float b_coeff = -2 * time; - float c_coeff = (2 * distance) / acceleration; - - // 计算判别式 - float discriminant = b_coeff * b_coeff - 4 * a_coeff * c_coeff; - - if (discriminant < 0) - { - return -1; // 无实数解 - } - - // 计算二次方程的根 - float t1_root1 = (-b_coeff + (float)Math.Sqrt(discriminant)) / (2 * a_coeff); - float t1_root2 = (-b_coeff - (float)Math.Sqrt(discriminant)) / (2 * a_coeff); - - // 选择合理的根作为 t1 - float t1 = Math.Min(t1_root1, t1_root2); - if (t1 < 0 || t1 > time) - { - return -1; // 无合理解 - } - - // 计算最大速度 v - return acceleration * t1; - } - - //单算加速,不算减速的距离速度计算 - static float getspeeddist_V2(float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) - { - vdist = 0; - vspeed = 0; - float desV = CalculateMaximumVelocity(Distance, flight_time, fc_acc); - if ((desV == -1) || (desV > fc_maxspeed)) //飞不到 - { - vspeed = Distance / flight_time; - vdist = vspeed * curr_time; //匀速距离 - return 0; - - } - float dect = desV / fc_acc; // 总加速时间 - float unit = flight_time - dect; //匀速段时间 - float decD = (fc_acc * dect * dect) / 2; //总加速距离 - - if (dect > flight_time) dect = flight_time;//约束时间 - if (curr_time > dect) //大于加速段时间--匀速 - { - vspeed = (Distance - decD) / (flight_time - dect); - vdist = vspeed * (curr_time - dect); - vdist = vdist + decD; //总距离=匀速距离+减速距离 - if (vdist > Distance) vdist = Distance; //约束距离 - - } - else //加速段 - { - vspeed = fc_acc * curr_time; - vdist = (fc_acc * curr_time * curr_time) / 2; //加速距离 - } - return 0; - } - - //单算减速,不算加速的距离速度计算 - static float getspeeddist_V3(float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) - { - vdist = 0; - vspeed = 0; - float desV = CalculateMaximumVelocity(Distance, flight_time, fc_acc); - if ((desV == -1) || (desV > fc_maxspeed)) //飞不到 - { - vspeed = Distance / flight_time; - vdist = vspeed * curr_time; //匀速距离 - return 0; - - } - float dect = desV / fc_acc; // 总减速时间 - float unit = flight_time - dect; //匀速段时间 - float decD = desV * dect - (fc_acc * dect * dect) / 2; //总减速距离 - - if (dect > flight_time) dect = flight_time;//约束时间 - //匀速时间内 - if (curr_time < unit) - { - vspeed = (Distance - decD) / (flight_time - dect); - vdist = vspeed * curr_time; - } - else - { - if (((flight_time - dect) * unit) == 0) - vdist = 0; - else - vdist = (Distance - decD) / (flight_time - dect) * unit; //匀速距离 - float currdect = curr_time - unit; //减速时间 - if (currdect >= 0) - { - vspeed = desV - fc_acc * currdect; - decD = desV * currdect - (fc_acc * currdect * currdect) / 2; //减速距离 - } - vdist = vdist + decD; //总距离=匀速距离+减速距离 - if (vdist > Distance) vdist = Distance; //约束距离 - } - return 0; - } - - - - static float getMinfligthtime(float Distance, float fc_acc, float fc_maxspeed) - { - float fDis = fabsf(Distance); - float facc = fabsf(fc_acc); - - float realflytime = 0.0f; - //计算一半的距离 - float hafdis = (fDis / 2); - //计算最大速度 - float vel = (float)sqrt(2 * facc * hafdis); - //如果速度超过最大速度 - if (vel > fc_maxspeed) - //使用最大速度 - vel = fc_maxspeed; - //加速时间 - float acctim = vel / facc; - //加速距离 - float accdis = vel * vel / (2 * facc); - //匀速段时间 - float vtime = (hafdis - accdis) / vel; - //到一半的时间 - float haftime = acctim + vtime; - realflytime = haftime * 2; - return realflytime; - } - - ///计算飞行距离和速度 单位--米,秒---- - //返回 整个距离最大飞行速度 - ///flight_time 总飞行时间 Distance 飞行总距离 curr_time 当前飞行时间 fc_acc加速度, fc_maxspeed最大速度 vdist 计算的应该飞的距离 vspeed 计算的当前时间应该速度 - //========这是飞控正在使用的算法======== - static float getspeeddist_V1(float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) - { - //计划飞行时间 - float plan_flytime = flight_time; - //计算距离用绝对值 - float fDis = fabsf(Distance); - if (curr_time < 0) - { - vdist = 0; - vspeed = 0; - return 0.0f; - } - //导航加速度 米/秒*秒 ---不使用导航库 - float wpacc = fabsf(fc_acc);// g.fc_acc_xy;//1.5; - - //最大目标速度---米/秒 - float desired_velocity = 0; - float dist = 0; - // - float speed = 0; - - //计算最小飞行时间 - float minflytime = getMinfligthtime(Distance, fc_acc, fc_maxspeed); - if (flight_time < minflytime) - plan_flytime = minflytime;// + 0.1f; - - - //根据总飞行时间和总距离计算飞行速度----起始和结束速度都是0,中间按匀加速和匀减速计算,没考虑加加速度 - float delta = (0.5f * plan_flytime) * (0.5f * plan_flytime) - fDis / wpacc; - if (delta >= 0) - { - desired_velocity = (0.5f * plan_flytime - sqrt(delta)) / (1 / wpacc); - if (desired_velocity > fc_maxspeed) - { - plan_flytime = minflytime; - desired_velocity = fc_maxspeed; - } - } - else - { - desired_velocity = (0.5f * plan_flytime) / (1 / wpacc); - } - if (desired_velocity < 0.2f) desired_velocity = 0.2f; //限制最小速度0.2米/秒 - if (desired_velocity > fc_maxspeed) desired_velocity = fc_maxspeed;//限制最大速度10米/秒,应该参数化 - - //计算实时速度 - if (curr_time <= (plan_flytime / 2.0)) - speed = curr_time * wpacc; - else - //需要减速 - speed = (plan_flytime - curr_time) * wpacc; - //不能大于目标速度 - if (speed > desired_velocity) - speed = desired_velocity; - if (speed <= 0) - speed = 0; - vspeed = speed; - //计算实时距离 - float V_start = 0.0f; - float V_end = 0.0f; - //加速段 - float t1 = (desired_velocity - V_start) / wpacc; - //减速段 - float t3 = (desired_velocity - V_end) / wpacc; - //平飞段 - float t2 = plan_flytime - t1 - t3; - if (curr_time < t1) - { - dist = 0.5f * wpacc * curr_time * curr_time + curr_time * V_start; - } - else if (curr_time < t1 + t2) - { - dist = -0.5f * wpacc * t1 * t1 + (curr_time) * desired_velocity; - } - else - { - float t = curr_time - t1 - t2; - dist = -0.5f * wpacc * t1 * t1 + curr_time * desired_velocity - 0.5f * wpacc * t * t; - } - - if (fabsf(dist) > fabsf(Distance)) - vdist = Distance; - else - { - if (Distance < 0) - vdist = -dist; - else vdist = dist; - } - - return desired_velocity; - } - - public static float getspeeddist(int flytype, float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) - { - switch (flytype) - { - case 0: //匀速 - //case 1: //匀速 - vspeed = Distance / flight_time; - vdist = vspeed * curr_time; - return 0; - case 1: //同时计算加减速 - return getspeeddist_V1(flight_time, Distance, curr_time, fc_acc, fc_maxspeed, out vdist, out vspeed); - case 2: //单加速 - return getspeeddist_V2(flight_time, Distance, curr_time, fc_acc, fc_maxspeed, out vdist, out vspeed); - case 3: //单减速 - return getspeeddist_V3(flight_time, Distance, curr_time, fc_acc, fc_maxspeed, out vdist, out vspeed); - default: - vspeed = 0; - vdist = 0; - return 0; - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - } - } +using System; +using System.Collections.Generic; +using System.Text; + +namespace Plane.CopterControllers +{ + class GuidController + { + + float fabsf(float vvalue) + { + return Math.Abs(vvalue); + + } + float sqrt(float vvalue) + { + return (float)Math.Sqrt(vvalue); + + } + + + /// + /// 计算小航点飞行 + /// + /// + /// + /// + /// + + //单算减速,不算加速的最小飞行时间 + float getMinfligthtime_v2(float Distance, float fc_acc, float fc_maxspeed) + { + float fDis = fabsf(Distance); // 总距离 + float facc = fabsf(fc_acc); // 减速度 + + // 物体开始时即以最大速度运动,不考虑加速过程 + float vel = fc_maxspeed; + + // 计算减速所需的时间和距离 + // 减速时间 (从最大速度减速到0) + float dectime = vel / facc; + // 减速阶段覆盖的距离 + float decdis = (vel * dectime) - (0.5f * facc * dectime * dectime); + + // 判断是否有足够的距离进行减速 + if (decdis >= fDis) + { + // 如果减速所需距离已经超过或等于总距离,调整最大速度 + // 使用公式 v^2 = u^2 + 2as 解出 v + vel = (float)Math.Sqrt(2 * facc * fDis); + // 重新计算减速时间 + dectime = vel / facc; + } + + // 计算匀速阶段时间 + float unftime = 0.0f; + if (decdis < fDis) + { + // 如果有剩余距离进行匀速运动 + unftime = (fDis - decdis) / vel; + } + + // 总飞行时间 = 匀速阶段时间 + 减速阶段时间 + return unftime + dectime; + } + + //单算减速,不算加速的最大飞行速度 + public float CalculateMaximumVelocity(float distance, float time, float acceleration) + { + // 二次方程系数 + float a_coeff = 1; + float b_coeff = -2 * time; + float c_coeff = (2 * distance) / acceleration; + + // 计算判别式 + float discriminant = b_coeff * b_coeff - 4 * a_coeff * c_coeff; + + if (discriminant < 0) + { + return -1; // 无实数解 + } + + // 计算二次方程的根 + float t1_root1 = (-b_coeff + (float)Math.Sqrt(discriminant)) / (2 * a_coeff); + float t1_root2 = (-b_coeff - (float)Math.Sqrt(discriminant)) / (2 * a_coeff); + + // 选择合理的根作为 t1 + float t1 = Math.Min(t1_root1, t1_root2); + if (t1 < 0 || t1 > time) + { + return -1; // 无合理解 + } + + // 计算最大速度 v + return acceleration * t1; + } + + //单算加速,不算减速的距离速度计算 + float getspeeddist_V2(float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) + { + vdist = 0; + vspeed = 0; + float desV = CalculateMaximumVelocity(Distance, flight_time, fc_acc); + if ((desV == -1) || (desV > fc_maxspeed)) //飞不到 + { + vspeed = Distance / flight_time; + vdist = vspeed * curr_time; //匀速距离 + return 0; + + } + float dect = desV / fc_acc; // 总加速时间 + float unit = flight_time - dect; //匀速段时间 + float decD = (fc_acc * dect * dect) / 2; //总加速距离 + + if (dect > flight_time) dect = flight_time;//约束时间 + if (curr_time > dect) //大于加速段时间--匀速 + { + vspeed = (Distance - decD) / (flight_time - dect); + vdist = vspeed * (curr_time - dect); + vdist = vdist + decD; //总距离=匀速距离+减速距离 + if (vdist > Distance) vdist = Distance; //约束距离 + + } + else //加速段 + { + vspeed = fc_acc * curr_time; + vdist = (fc_acc * curr_time * curr_time) / 2; //加速距离 + } + return 0; + } + + //单算减速,不算加速的距离速度计算 + float getspeeddist_V3(float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) + { + vdist = 0; + vspeed = 0; + float desV = CalculateMaximumVelocity(Distance, flight_time, fc_acc); + if ((desV == -1) || (desV > fc_maxspeed)) //飞不到 + { + vspeed = Distance / flight_time; + vdist = vspeed * curr_time; //匀速距离 + return 0; + + } + float dect = desV / fc_acc; // 总减速时间 + float unit = flight_time - dect; //匀速段时间 + float decD = desV * dect - (fc_acc * dect * dect) / 2; //总减速距离 + + if (dect > flight_time) dect = flight_time;//约束时间 + //匀速时间内 + if (curr_time < unit) + { + vspeed = (Distance - decD) / (flight_time - dect); + vdist = vspeed * curr_time; + } + else + { + if (((flight_time - dect) * unit) == 0) + vdist = 0; + else + vdist = (Distance - decD) / (flight_time - dect) * unit; //匀速距离 + float currdect = curr_time - unit; //减速时间 + if (currdect >= 0) + { + vspeed = desV - fc_acc * currdect; + decD = desV * currdect - (fc_acc * currdect * currdect) / 2; //减速距离 + } + vdist = vdist + decD; //总距离=匀速距离+减速距离 + if (vdist > Distance) vdist = Distance; //约束距离 + } + return 0; + } + + + + float getMinfligthtime(float Distance, float fc_acc, float fc_maxspeed) + { + float fDis = fabsf(Distance); + float facc = fabsf(fc_acc); + + float realflytime = 0.0f; + //计算一半的距离 + float hafdis = (fDis / 2); + //计算最大速度 + float vel = (float)sqrt(2 * facc * hafdis); + //如果速度超过最大速度 + if (vel > fc_maxspeed) + //使用最大速度 + vel = fc_maxspeed; + //加速时间 + float acctim = vel / facc; + //加速距离 + float accdis = vel * vel / (2 * facc); + //匀速段时间 + float vtime = (hafdis - accdis) / vel; + //到一半的时间 + float haftime = acctim + vtime; + realflytime = haftime * 2; + return realflytime; + } + + ///计算飞行距离和速度 单位--米,秒---- + //返回 整个距离最大飞行速度 + ///flight_time 总飞行时间 Distance 飞行总距离 curr_time 当前飞行时间 fc_acc加速度, fc_maxspeed最大速度 vdist 计算的应该飞的距离 vspeed 计算的当前时间应该速度 + //========这是飞控正在使用的算法======== + float getspeeddist_V1(float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) + { + float plan_flytime=_plan_flytime; + float desired_velocity=_desired_velocity; + float dist = 0; + //导航加速度 米/秒*秒 ---不使用导航库 + float wpacc = fabsf(fc_acc);// g.fc_acc_xy;//1.5; + // + float speed = 0; + //计算实时速度 + if (curr_time <= (plan_flytime / 2.0)) + speed = curr_time * wpacc; + else + //需要减速 + speed = (plan_flytime - curr_time) * wpacc; + //不能大于目标速度 + if (speed > desired_velocity) + speed = desired_velocity; + if (speed <= 0) + speed = 0; + vspeed = speed; + //计算实时距离 + float V_start = 0.0f; + float V_end = 0.0f; + //加速段 + float t1 = (desired_velocity - V_start) / wpacc; + //减速段 + float t3 = (desired_velocity - V_end) / wpacc; + //平飞段 + float t2 = plan_flytime - t1 - t3; + if (curr_time < t1) + { + dist = 0.5f * wpacc * curr_time * curr_time + curr_time * V_start; + } + else if (curr_time < t1 + t2) + { + dist = -0.5f * wpacc * t1 * t1 + (curr_time) * desired_velocity; + } + else + { + float t = curr_time - t1 - t2; + dist = -0.5f * wpacc * t1 * t1 + curr_time * desired_velocity - 0.5f * wpacc * t * t; + } + + if (fabsf(dist) > fabsf(Distance)) + vdist = Distance; + else + { + if (Distance < 0) + vdist = -dist; + else vdist = dist; + } + + return desired_velocity; + } + + float _vspeed=0; + + float _desired_velocity = 0; + float _plan_flytime = 0; + + float initgetspeeddist_V1(float flight_time, float Distance, float fc_acc, float fc_maxspeed) + { + //计划飞行时间 + float plan_flytime = flight_time; + //计算距离用绝对值 + float fDis = fabsf(Distance); + + //导航加速度 米/秒*秒 ---不使用导航库 + float wpacc = fabsf(fc_acc);// g.fc_acc_xy;//1.5; + + //最大目标速度---米/秒 + float desired_velocity = 0; + + + //计算最小飞行时间 + float minflytime = getMinfligthtime(Distance, fc_acc, fc_maxspeed); + if (flight_time < minflytime) + plan_flytime = minflytime;// + 0.1f; + + + //根据总飞行时间和总距离计算飞行速度----起始和结束速度都是0,中间按匀加速和匀减速计算,没考虑加加速度 + float delta = (0.5f * plan_flytime) * (0.5f * plan_flytime) - fDis / wpacc; + if (delta >= 0) + { + desired_velocity = (0.5f * plan_flytime - sqrt(delta)) / (1 / wpacc); + if (desired_velocity > fc_maxspeed) + { + plan_flytime = minflytime; + desired_velocity = fc_maxspeed; + } + } + else + { + desired_velocity = (0.5f * plan_flytime) / (1 / wpacc); + } + if (desired_velocity < 0.2f) desired_velocity = 0.2f; //限制最小速度0.2米/秒 + if (desired_velocity > fc_maxspeed) desired_velocity = fc_maxspeed;//限制最大速度10米/秒,应该参数化 + + _desired_velocity = desired_velocity; + _plan_flytime = plan_flytime; + return desired_velocity; + } + + + public float initgetspeeddist(int flytype, float flight_time, float Distance, float fc_acc, float fc_maxspeed) + { + flytype = 0; + switch (flytype) + { + case 0: //匀速 + _vspeed = Distance / flight_time; + return 0; + case 1: //同时计算加减速 + return initgetspeeddist_V1(flight_time, Distance, fc_acc, fc_maxspeed); + case 2: //单加速 + return 0; + case 3: //单减速 + return 0; + default: + + return 0; + } + + } + public float getspeeddist(int flytype, float flight_time, float Distance, float curr_time, float fc_acc, float fc_maxspeed, out float vdist, out float vspeed) + { + flytype = 0; + switch (flytype) + { + case 0: //匀速 + //case 1: //匀速 + vspeed = _vspeed; + vdist = vspeed * curr_time; + return 0; + case 1: //同时计算加减速 + return getspeeddist_V1(flight_time, Distance, curr_time, fc_acc, fc_maxspeed, out vdist, out vspeed); + case 2: //单加速 + return getspeeddist_V2(flight_time, Distance, curr_time, fc_acc, fc_maxspeed, out vdist, out vspeed); + case 3: //单减速 + return getspeeddist_V3(flight_time, Distance, curr_time, fc_acc, fc_maxspeed, out vdist, out vspeed); + default: + vspeed = 0; + vdist = 0; + return 0; + } + } + + } + } diff --git a/PlaneGcsSdk_Shared/Copters/FakeCopter.cs b/PlaneGcsSdk_Shared/Copters/FakeCopter.cs index 240748c..45a5958 100644 --- a/PlaneGcsSdk_Shared/Copters/FakeCopter.cs +++ b/PlaneGcsSdk_Shared/Copters/FakeCopter.cs @@ -1,922 +1,935 @@ -using Plane.Communication; -using Plane.Geography; -using System; -using System.Diagnostics; -using System.Drawing; -using System.Threading; -using System.Threading.Tasks; -using static Plane.Copters.Constants; -using System.Windows.Media; -using Plane.CopterControllers; - -namespace Plane.Copters -{ - /// - /// 虚拟的 实现。 - /// - [DebuggerDisplay("Name={Name}")] - public partial class FakeCopter : CopterImplSharedPart, IFakeCopter - { - /// - /// 心跳间隔,单位为毫秒。 - /// - private const int HEARTBEAT_INTERVAL = 200; - /// - /// 更新虚拟飞行器状态的时间间隔,单位为毫秒。 - /// - static private int UPDATE_INTERVAL =100; //默认100, i7电脑 150可以跑1000架 100可以跑500架 50可以200-300架 - - static private int UPDATE_INTERVAL_TEMP = 50; - /// - /// 在一个更新间隔中的最大移动距离。 - /// - private float MAX_MOVE_IN_INTERVAL = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000; - - /// - /// 高速模式下,在一个更新间隔中的最大移动距离。 - /// - private float MAX_MOVE_IN_INTERVAL_FAST = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000 * 4; //MAX_MOVE_IN_INTERVAL*4 - - - - // private int _UPDATE_INTERVAL = 50; - - /// - /// 对飞行器的模拟是否正在运行。 - /// - private bool _isRunning = false; - - /// - /// 上次计算速度时的位置。 - /// - private ILocation _lastCalcSpeedPoint; - - /// - /// 上次计算速度的时间。 - /// - private DateTime _lastCalcSpeedTime; - - /// - /// 高速模式下,按比例缩放过的在一个更新间隔中的最大移动距离。 - /// - private float _scaledFastMaxMoveInInterval = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000 * 4; //MAX_MOVE_IN_INTERVAL_FAST - - /// - /// 按比例缩放过的在一个更新间隔中的最大移动距离。 - /// - private float _scaledMaxMoveInInterval = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000; //MAX_MOVE_IN_INTERVAL - - /// - /// 速度缩放比例。 - /// - private float _speedScale = 1; - - /// - /// 自动起飞的目标高度。 - /// - private int _takeOffTargetAltitude; - - /// - /// FlyTo 的目标高度。 - /// - private float _targetAlt; - //航点开始高度 - private float _startAlt; - - private float _Lng_delta; - private float _Lat_delta; - - private float _flytime; - private DateTime _startTicks; - - private float _distance_xy; - private float _distance_z; - private float _xy_speed; - private float _z_speed; - private int _flytype; - private float currflytime; - // 根据飞行时间计算飞行距离。 - private float currdis_xy; - private float currdis_z; - - - // 目标点相对于开始位置的方向。 - private double _direction; - - /// - /// FlyTo 的目标纬度。 - /// - private double _targetLat; - - /// - /// FlyTo 的目标经度。 - /// - private double _targetLng; - /// - /// FlyTo 的目标纬度。 - /// - private double _startLat; - - /// - /// FlyTo 的目标经度。 - /// - private double _startLng; - - - /// - /// FlyTo 的目标经度。 - /// - private bool _ShowLED; - - private System.Drawing.ColorConverter rgbconverter; - - - /// - /// 使用 创建 实例。 - /// - public FakeCopter() : this(SynchronizationContext.Current) - { - - - } - - - new public int sim_update_int - { - get { return UPDATE_INTERVAL; } - set - { - //最大移动距离 - MAX_MOVE_IN_INTERVAL = MAX_VEL * value / 1000; - //快速模式最大移动距离 - MAX_MOVE_IN_INTERVAL_FAST = MAX_MOVE_IN_INTERVAL * 4; - //速度缩放后快速速度距离 - _scaledFastMaxMoveInInterval = MAX_MOVE_IN_INTERVAL_FAST* _speedScale; - //速度缩放后速度距离 - _scaledMaxMoveInInterval = MAX_MOVE_IN_INTERVAL* _speedScale; - Set(nameof(sim_update_int), ref UPDATE_INTERVAL, value); - } - } - - - public Task DoCommandAckAsync(ushort command, byte result) - { - return TaskUtils.CompletedTask; - } - - /// - /// 创建 实例。 - /// - /// UI 线程的同步上下文。 - /// 自动起飞时的目标高度。 - /// 速度缩放比例。 - public FakeCopter(SynchronizationContext uiSyncContext, int takeOffTargetAltitude = 10, float speedScale = 1, IConnection connection = null) - : base(uiSyncContext) - { - Latitude = 23.155382266268134; - Longitude = 113.45038586296141; - - Elevation = 20; - this.PropertyChanged += (sender, e) => - { - switch (e.PropertyName) - { - case nameof(Altitude): - Elevation = Altitude + 20; - break; - - case nameof(IsUnlocked): - if (IsUnlocked) - { - // 王海, 20151029, 在解锁时上次计算速度点、上次计算速度时刻。 - _lastCalcSpeedPoint = new PLLocation { Latitude = Latitude, Longitude = Longitude, Altitude = Altitude }; - _lastCalcSpeedTime = DateTime.Now; - } - break; - } - }; - - _takeOffTargetAltitude = takeOffTargetAltitude; - - _speedScale = speedScale; - _scaledMaxMoveInInterval = MAX_MOVE_IN_INTERVAL * _speedScale; - _scaledFastMaxMoveInInterval = MAX_MOVE_IN_INTERVAL_FAST * _speedScale; - - this.Connection = connection ?? EmptyConnection.Instance; - _isRunning = true; - // 持续计算并更新虚拟飞行器的状态。 - // Task.Run(async () => - - Task.Factory.StartNew(async () => - - { - while (_isRunning) - { - Update(); - await TaskUtils.Delay(UPDATE_INTERVAL).ConfigureAwait(false); - } - } - , TaskCreationOptions.LongRunning - ); - - - ++HeartbeatCount; - GpsFixType = GpsFixType.Fix3D; - GpsHdop = 1; - IsGpsAccurate = true; - HasSwitchedToGpsMode = true; - - /* - // 持续假装收到飞行器发来的心跳。 - Task.Run(async () => - { - while (true) - { - if (_isRunning) - { - ++HeartbeatCount; - - IsCheckingConnection = false; - - if (HeartbeatCount >= 10) - { - // 收到若干个心跳之后,设置一下 GPS 相关属性。 - GpsFixType = GpsFixType.Fix3D; - GpsHdop = 1; - IsGpsAccurate = true; - HasSwitchedToGpsMode = true; - } - - _uiSyncContext.Post(() => RaiseHeartbeatReceived(new HeartbeatReceivedEventArgs(HeartbeatCount))); - } - await TaskUtils.Delay(HEARTBEAT_INTERVAL).ConfigureAwait(false); - } - }); - */ - } - - public IConnection Connection { get; set; } - - public string Id { get; set; } - - private int ReturnToLaunchAltitude { get { return _takeOffTargetAltitude; } } - - public Task ConnectAsync() - { - IsConnected = true; - _isRunning = true; - IsCheckingConnection = true; - return TaskUtils.CompletedTask; - } - - public Task DisconnectAsync() - { - IsConnected = false; - _isRunning = false; - IsCheckingConnection = false; - return TaskUtils.CompletedTask; - } - - protected override Task FlyToCoreAsync(double lat, double lng, float alt, float flytime = 0, int flytype = 0) - { - if (!IsEmergencyHoverActive) - { - _targetLat = lat; - _targetLng = lng; - _targetAlt = alt; - _startLat = Latitude; - _startLng = Longitude ; - _startAlt = Altitude; - _direction = this.CalcDirection2D(lat, lng); - - _flytime = flytime*1000; //ms - _startTicks = DateTime.Now; //ms - - - _Lng_delta = (float)( Math.Sin(_direction) * GeographyUtils.CalcMetersToLngSpan(Latitude)); - _Lat_delta =(float)( Math.Cos(_direction) * GeographyUtils.METERS_TO_LAT_SPAN); - - //计算xy和x方向距离 - _distance_xy = (float)this.CalcDistance(lat, lng, Altitude); - _distance_z = alt - Altitude; - Console.WriteLine($"{this.Id}d:{_distance_xy},lat{this.Latitude},lng{this.Longitude },tlat:{lat}tlng:{lng}"); - - _flytype = flytype; - // _xy_speed = _distance_xy / _flytime; - // _z_speed = _distance_z / _flytime; - Mode = FlightMode.GUIDED; - - - } - return TaskUtils.CompletedTask; - } - - public Task GetCopterDataAsync() - { - return TaskUtils.CompletedTask; - } - - public Task GetParamAsync(string paramName, int millisecondsTimeout = Timeout.Infinite) - { - // TODO: 王海, 20150806, 实现仿真的 GetParamAsync。 - return Task.FromResult(0f); - } - - public Task LockAsync() - { - if (IsUnlocked) - { - IsUnlocked = false; - } - return TaskUtils.CompletedTask; - } - - public Task LEDAsync() - { - return TaskUtils.CompletedTask; - } - - public Task MotorTestAsync(int motor) - { - return TaskUtils.CompletedTask; - } - - public override Task SetChannelsAsync() - { - Channel1 = DesiredChannel1 ?? Channel1; - Channel2 = DesiredChannel2 ?? Channel2; - Channel3 = DesiredChannel3 ?? Channel3; - Channel4 = DesiredChannel4 ?? Channel4; - Channel5 = DesiredChannel5 ?? Channel5; - Channel6 = DesiredChannel6 ?? Channel6; - Channel7 = DesiredChannel7 ?? Channel7; - Channel8 = DesiredChannel8 ?? Channel8; - return TaskUtils.CompletedTask; - } - - public override Task SetMobileControlAsync() - { - Channel1 = DesiredChannel1 ?? Channel1; - Channel2 = DesiredChannel2 ?? Channel2; - Channel3 = DesiredChannel3 ?? Channel3; - Channel4 = DesiredChannel4 ?? Channel4; - Channel5 = DesiredChannel5 ?? Channel5; - Channel6 = DesiredChannel6 ?? Channel6; - Channel7 = DesiredChannel7 ?? Channel7; - Channel8 = DesiredChannel8 ?? Channel8; - Yaw = DesiredYaw ?? Yaw; - Heading = (short)Yaw; - return TaskUtils.CompletedTask; - } - - //得到收到的总数据量 - public void GetCommunicationNumber(out int recnumber, out int sendnumber) - { - recnumber = 0; - sendnumber = 0; - } - - - //重设数据量 - public void ResetCommunicationNumber() - { - - } - - public async Task DoCommandAsync(int actionid, float p1, float p2, float p3, float p4, float p5, float p6, float p7) - { - - await Task.Delay(50).ConfigureAwait(false); - - - } - public Task SetParamAsync(string paramName, float value, int millisecondsTimeout = Timeout.Infinite) - { - // TODO: 王海, 20150807, 实现仿真的 SetParamAsync。 - return TaskUtils.CompletedTask; - } - public bool GetShowLEDAsync() - { - return _ShowLED; - } - public Task SetShowLEDAsync(bool Ledon) - { - _ShowLED = Ledon; - RaiseLocationChanged(); - return TaskUtils.CompletedTask; - } - - - public Task SetShowRGBLEDAsync(int RedValue, int GreenValue, int BlueValue) - { - return TaskUtils.CompletedTask; - } - - - public Task SetShowLEDFlashAsync(int Flashtype, int millisecondsTime) - { - bool Ledison; - Ledison = GetShowLEDAsync(); - if (!Ledison) - { - SetShowLEDAsync(true); - Task.Delay(millisecondsTime).ConfigureAwait(false); - SetShowLEDAsync(false); - } - else - { - SetShowLEDAsync(false); - Task.Delay(millisecondsTime).ConfigureAwait(false); - SetShowLEDAsync(true); - } - - return TaskUtils.CompletedTask; - } - - public void SetProperties( - string id = null, //"Junqing's Drone", - double? latitude = null, //23.14973333, - double? longitude = null, //113.40974166, - float? altitude = null, //0, - string name = null, //"王海的飞行器", - byte? batteryPer = null, //10, - short? heading = null, //33, - bool? isConnected = null, //true, - float? pitch = null, //-70, - float? roll = null, //28, - byte? satCount = null, //6, - float? airSpeed = null, //3.333, - double? flightDistance = null, //100.388, - double? flightDistance2D = null, // 100.88, - TimeSpan? flightTimeSpan = null) - { - if (id != null) Id = id; - if (name != null) Name = name; - if (latitude != null) Latitude = latitude.Value; - if (longitude != null) Longitude = longitude.Value; - if (altitude != null) Altitude = altitude.Value; - if (batteryPer != null) BatteryPer = batteryPer.Value; - if (heading != null) Heading = heading.Value; - if (isConnected != null) IsConnected = isConnected.Value; - if (pitch != null) Pitch = pitch.Value; - if (roll != null) Roll = roll.Value; - if (satCount != null) SatCount = satCount.Value; - if (airSpeed != null) AirSpeed = airSpeed.Value; - if (flightDistance != null) FlightDistance = flightDistance.Value; - if (flightDistance2D != null) FlightDistance2D = flightDistance2D.Value; - if (flightTimeSpan != null) FlightTimeSpan = flightTimeSpan.Value; - } - - public Task StartPairingAsync() - { - RaisePairingCompleted(new PairingCompletedEventArgs(true, 0, 0)); - return TaskUtils.CompletedTask; - } - - public Task StopPairingAsync() - { - return TaskUtils.CompletedTask; - } - - public async Task TakeOffAsync(float alt) - { - await UnlockAsync().ConfigureAwait(false); - _takeOffTargetAltitude = (int)alt; - await TakeOffAsync().ConfigureAwait(false); - } - public Task MissionStartAsync(int hour_utc, int minute_utc, int second_utc, double Missionlng, double Missionlat) - { - return TaskUtils.CompletedTask; - } - - - public async Task UnlockAsync() - { - if (!IsUnlocked) - { - await SetChannelsAsync( - ch1: 1500, - ch2: 1500, - ch3: 1100, - ch4: 1500 - ).ConfigureAwait(false); - if (await SetModeAsync(FlightMode.ALT_HOLD).ConfigureAwait(false)) - { - IsUnlocked = true; - } - } - } - - /// - /// 无论在不在紧急悬停状态下都可用的切换模式方法。 - /// - /// 模式。 - /// 超时。 - /// 成功与否。 - internal override Task SetModeCoreAsync(FlightMode mode, int millisecondsTimeout = 5000) - { - Mode = mode; - return Task.FromResult(true); - } - - protected override void UpdateFlightDataIfNeeded() - { - //计算飞机距离,没必要 - /* - if (!TakeOffPoint.IsNullOrEmpty()) - { - FlightDistance = TakeOffPoint.CalcDistance(this); - FlightDistance2D = TakeOffPoint.CalcDistance2D(this); - } - - - if (FlightTimeSpan.TotalSeconds > 0) - { - if (_lastCalcSpeedPoint.IsNullOrEmpty()) - { - if (!this.IsNullOrEmpty()) - { - _lastCalcSpeedPoint = new PLLocation(this); - _lastCalcSpeedTime = DateTime.Now; - } - } - else - { - var movedDistance = _lastCalcSpeedPoint.CalcDistance(this); - var movedTime = DateTime.Now - _lastCalcSpeedTime; - if (movedDistance >= 3 || movedTime.TotalSeconds >= 3) - { - var airSpeed = movedDistance / movedTime.TotalSeconds; - if (airSpeed < 100) // 王海, 20151023, 速度过大时不正常,经纬度可能有错误。 - { - AirSpeed = (float)airSpeed; - } - //Windows.Messages.Message.Show(AirSpeed.ToString()); - GroundSpeed = AirSpeed; - _lastCalcSpeedPoint = new PLLocation(this); - _lastCalcSpeedTime = DateTime.Now; - } - } - } - */ - } - - private void MoveToPointImmediately(double lat, double lng, float alt) - { - Latitude = lat; - Longitude = lng; - Altitude = alt; - Roll = 0; - Pitch = 0; - } - public Task InjectGpsDataAsync(byte[] data, ushort length) - { - return TaskUtils.CompletedTask; - - } - - public static System.Drawing.Color GetRandomColor() - { - Random rand = new Random(); - return System.Drawing.Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256)); - } - - private DateTime led_laston; - private System.Drawing.Color Led_color; - - //更新显示颜色,根据设置的led参数 - private void UpdateShowColor() - { - // 使用实例化的对象调用ConvertFromString - //LEDShowColor = (System.Drawing.Color)converter.ConvertFromString("#" + LEDColor); - // 创建ColorConverter实例用于颜色转换 - if ( rgbconverter==null) rgbconverter=new System.Drawing.ColorConverter(); - //简化版的颜色模拟 - switch (LEDMode) - { - case 0: - if (LEDColor != null) - LEDShowColor = (System.Drawing.Color)rgbconverter.ConvertFromString("#" + LEDColor); - else LEDShowColor = (System.Drawing.Color)rgbconverter.ConvertFromString("#FFFFFF"); - - break; - //闪烁 - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - - if (DateTime.Now.AddMilliseconds(-(LEDInterval*1000)) >= led_laston) - { - led_laston = DateTime.Now; - if (LEDShowColor != System.Drawing.Color.Black) - { - LEDShowColor = System.Drawing.Color.Black; - - } - else - { - if ((LEDMode == 1) || (LEDMode == 4) || (LEDMode == 5) || (LEDMode == 6) || (LEDMode == 7) || (LEDMode == 9) || (LEDMode == 11) || (LEDMode == 13)) - LEDShowColor = (System.Drawing.Color)rgbconverter.ConvertFromString("#" + LEDColor); - if ((LEDMode == 2)||(LEDMode == 3) || (LEDMode == 8) || (LEDMode == 10) || (LEDMode == 12) || (LEDMode == 14)) - LEDShowColor = GetRandomColor(); - } - } - - break; - - } - - - - - - } - - private bool ReachedDestination(double destLat, double destLng, float destAlt) => this.CalcDistance(destLat, destLng, destAlt) < 0.1; - - /// - /// 计算并更新虚拟飞行器状态。 - /// - private void Update() - { - if (IsUnlocked) - { - // 飞行器接触地面(只判断 Altitude <= 0)后,如果处于 LAND 模式或者保持 Channel3 < MIN_HOVER_CHANNEL,在一段时间后自动锁定。 - if ((Mode == FlightMode.LAND || Channel3 < MIN_HOVER_CHANNEL) && Altitude <= 0) - { - Task.Run(async () => - { - for (int i = 0; i < 4000 / 20; i++) - { - await TaskUtils.Delay(20).ConfigureAwait(false); - if ((Mode != FlightMode.LAND && Channel3 >= MIN_HOVER_CHANNEL) || !IsUnlocked) - { - return; - } - } - IsUnlocked = false; - }); - } - else - { - switch (Mode) - { - case FlightMode.ACRO: - break; - - case FlightMode.AUTO: - /* - // 暂时只管起飞。以最大速度上升,直到达到目标高度。 - if (Altitude < _takeOffTargetAltitude) - { - Altitude += _scaledMaxMoveInInterval; - } - else - { - Mode = FlightMode.LOITER; - } - */ - break; - - case FlightMode.GUIDED: - // 王海, 20160317, 指点时也能体感控制若干通道。 - //考虑不更新这个,好像没必要-xu - //UpdateWithChannels(); - //UpdateWithDestination(_targetLat, _targetLng, _targetAlt); - if (FlightControlMode==1) - UpdateWithDestination_v2(_targetLat, _targetLng, _targetAlt); - else - UpdateWithDestination(_targetLat, _targetLng, _targetAlt); - break; - - case FlightMode.STABILIZE: - case FlightMode.ALT_HOLD: - case FlightMode.LOITER: - UpdateWithChannels(); - break; - - case FlightMode.RTL: - if (Altitude < ReturnToLaunchAltitude && !ReachedDestination(Latitude, Longitude, ReturnToLaunchAltitude)) - { - UpdateWithDestination(Latitude, Longitude, ReturnToLaunchAltitude); - } - else if (Altitude > ReturnToLaunchAltitude && !ReachedDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, Altitude)) - { - UpdateWithDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, Altitude); - } - else - { - UpdateWithDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, ReturnToLaunchAltitude); - } - break; - - case FlightMode.CIRCLE: - break; - - case FlightMode.POSITION: - break; - - case FlightMode.LAND: - // 王海, 20160317, 降落时也能体感控制若干通道。 - UpdateWithChannels(); - // 以最大速度降落,直到高度为 0。 - if (Altitude > 0) - { - if (Altitude > _scaledMaxMoveInInterval) - { - Altitude -= _scaledMaxMoveInInterval; - } - else - { - Altitude = 0; - } - Roll = 0; - Pitch = 0; - } - break; - - case FlightMode.OF_LOITER: - break; - - case FlightMode.TOY: - break; - - default: - break; - } - //UpdateFlightDataIfNeeded(); - } - } - else - { - // TODO: 王海, 20151228, 模拟空中锁定。 - // 锁定时直接把速度设为 0。 - AirSpeed = 0; - } - - // UpdateShowColor(); - - - - _uiSyncContext.Post(() => - { - //位置变化需要在UI刷新 - RaiseLocationChangedIfNeeded(); - //高度变化需要在UI刷新 - // RaiseAltitudeChangedIfNeeded(); - //GPS数据用于显示 - // RaiseDataStreamReceived(PDataStreamType.GPS_RAW_INT); - //不考虑姿态 - // RaiseAttitudeChanged(); - }); - } - - /// - /// 根据各个通道值来更新位置、姿态等状态。 - /// - private void UpdateWithChannels() - { - var maxMove = Mode == FlightMode.LOITER ? _scaledMaxMoveInInterval : _scaledFastMaxMoveInInterval; - var ch3Delta = Channel3 > MAX_HOVER_CHANNEL ? Channel3 - MAX_HOVER_CHANNEL : Channel3 < MIN_HOVER_CHANNEL ? Channel3 - MIN_HOVER_CHANNEL : 0; - if (ch3Delta != 0) - { - // 更新相对高度。 - Altitude = Math.Max(0, Altitude + maxMove * ch3Delta / MAX_CHANNEL_DELTA); - } - - var yawRad = Yaw.DegToRad(); - var metersToLocalLng = GeographyUtils.CalcMetersToLngSpan(Latitude); - - var ch1Delta = Channel1 > MAX_HOVER_CHANNEL ? Channel1 - MAX_HOVER_CHANNEL : Channel1 < MIN_HOVER_CHANNEL ? Channel1 - MIN_HOVER_CHANNEL : 0; - var xDistance = maxMove * ch1Delta / MAX_CHANNEL_DELTA; - if (ch1Delta != 0) - { - // 更新横滚方向的纬度和经度。 - Latitude -= xDistance * Math.Sin(yawRad) * GeographyUtils.METERS_TO_LAT_SPAN; - Longitude += xDistance * Math.Cos(yawRad) * metersToLocalLng; - // 更新横滚姿态。 - Roll = MAX_TILT_IN_FLIGHT * ch1Delta / MAX_CHANNEL_DELTA; - } - else - { - Roll = 0; - } - - var ch2Delta = Channel2 > MAX_HOVER_CHANNEL ? Channel2 - MAX_HOVER_CHANNEL : Channel2 < MIN_HOVER_CHANNEL ? Channel2 - MIN_HOVER_CHANNEL : 0; - var yDistance = maxMove * ch2Delta / MAX_CHANNEL_DELTA; - if (ch2Delta != 0) - { - // 更新俯仰方向的纬度和经度。 - Latitude -= yDistance * Math.Cos(yawRad) * GeographyUtils.METERS_TO_LAT_SPAN; - Longitude -= yDistance * Math.Sin(yawRad) * metersToLocalLng; - // 更新俯仰姿态。 - Pitch = MAX_TILT_IN_FLIGHT * ch2Delta / MAX_CHANNEL_DELTA; - } - else - { - Pitch = 0; - } - } - - /// - /// 根据目的地来更新位置、姿态等状态。 - /// - private void UpdateWithDestination(ILocation loc) - { - UpdateWithDestination(loc.Latitude, loc.Longitude, loc.Altitude); - } - - /// - /// 根据目的地来更新位置、姿态等状态。 - /// - private void UpdateWithDestination(double lat, double lng, float alt) - { - // 与目标点之间的距离。 - var distance = this.CalcDistance(lat, lng, alt); - // 距离已经很近,直接移动到目标点。 - if (distance < _scaledMaxMoveInInterval) - { - MoveToPointImmediately(lat, lng, alt); - return; - } - // 在空间中的移动距离。 - var move = _scaledMaxMoveInInterval; - // 竖直方向的移动距离。 - var altDelta = (float)(move * (alt - Altitude) / distance); - // 更新高度。 - Altitude += altDelta; - // 目标点相对于当前位置的方向。 - // var direction = this.CalcDirection2D(lat, lng); - // 水平面上的移动距离。 - var moveInHorizontalPlane = Math.Sqrt(move * move - altDelta * altDelta); - if (double.IsNaN(moveInHorizontalPlane)) - { - MoveToPointImmediately(lat, lng, alt); - return; - } - - // 更新纬度。 - //Longitude += moveInHorizontalPlane * Math.Sin(direction) * GeographyUtils.CalcMetersToLngSpan(Latitude); - Longitude += moveInHorizontalPlane * _Lng_delta; - Latitude += moveInHorizontalPlane * _Lat_delta; - // 更新经度。 - //Latitude += moveInHorizontalPlane * Math.Cos(direction) * GeographyUtils.METERS_TO_LAT_SPAN; - } - //新版本小航点计算移动位置 - private void UpdateWithDestination_v2(double lat, double lng, float alt) - { - //_flytime 总飞行时间 秒 - //_startTicks 开始飞行时间ms - // _distance_xy 米 - // _distance_z 米 - //当前飞行时间 - if ((lat == Latitude) && (lng == Longitude) && (alt == Altitude)) return; - currflytime =(float)( DateTime.Now -_startTicks).TotalMilliseconds;//实际飞行时间 ms - //超时直接移动到目标点 - if (currflytime >= _flytime) - { - MoveToPointImmediately(lat, lng, alt); - return; - } - //if (currflytime > 13000) - // return; - // 根据飞行时间计算飞行距离 - - float vspeed = 0; - - GuidController.getspeeddist(_flytype, _flytime / 1000, _distance_xy, currflytime / 1000, acc_xy,maxspeed_xy , out currdis_xy, out vspeed); - - if (alt> Altitude) - GuidController.getspeeddist(_flytype, _flytime / 1000, _distance_z, currflytime / 1000, acc_z,maxspeed_up, out currdis_z, out vspeed); - else - GuidController.getspeeddist(_flytype, _flytime / 1000, _distance_z, currflytime / 1000, acc_z,maxspeed_down, out currdis_z, out vspeed); - - Console.WriteLine($"{this.Id}:time:{currflytime / 1000} to {currdis_z}"); - - // 距离已经很近,直接移动到目标点。 - if ((Math.Abs(_distance_xy-currdis_xy) < 0.1)&& (Math.Abs(_distance_z - currdis_z) < 0.1)) - { - Console.WriteLine($"{this.Id} to tlat:{lat}tlng:{lng}"); - - MoveToPointImmediately(lat, lng, alt); - return; - } - // 更新位置 - Altitude = _startAlt+ currdis_z; - Longitude = _startLng + currdis_xy*_Lng_delta; - Latitude = _startLat + currdis_xy *_Lat_delta; - } - - } -} +using Plane.Communication; +using Plane.Geography; +using System; +using System.Diagnostics; +using System.Drawing; +using System.Threading; +using System.Threading.Tasks; +using static Plane.Copters.Constants; +using System.Windows.Media; +using Plane.CopterControllers; + +namespace Plane.Copters +{ + /// + /// 虚拟的 实现。 + /// + [DebuggerDisplay("Name={Name}")] + public partial class FakeCopter : CopterImplSharedPart, IFakeCopter + { + /// + /// 心跳间隔,单位为毫秒。 + /// + private const int HEARTBEAT_INTERVAL = 200; + /// + /// 更新虚拟飞行器状态的时间间隔,单位为毫秒。 + /// + static private int UPDATE_INTERVAL =100; //默认100, i7电脑 150可以跑1000架 100可以跑500架 50可以200-300架 + + static private int UPDATE_INTERVAL_TEMP = 50; + /// + /// 在一个更新间隔中的最大移动距离。 + /// + private float MAX_MOVE_IN_INTERVAL = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000; + + /// + /// 高速模式下,在一个更新间隔中的最大移动距离。 + /// + private float MAX_MOVE_IN_INTERVAL_FAST = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000 * 4; //MAX_MOVE_IN_INTERVAL*4 + + + + // private int _UPDATE_INTERVAL = 50; + + /// + /// 对飞行器的模拟是否正在运行。 + /// + private bool _isRunning = false; + + /// + /// 上次计算速度时的位置。 + /// + private ILocation _lastCalcSpeedPoint; + + /// + /// 上次计算速度的时间。 + /// + private DateTime _lastCalcSpeedTime; + + /// + /// 高速模式下,按比例缩放过的在一个更新间隔中的最大移动距离。 + /// + private float _scaledFastMaxMoveInInterval = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000 * 4; //MAX_MOVE_IN_INTERVAL_FAST + + /// + /// 按比例缩放过的在一个更新间隔中的最大移动距离。 + /// + private float _scaledMaxMoveInInterval = MAX_VEL * UPDATE_INTERVAL_TEMP / 1000; //MAX_MOVE_IN_INTERVAL + + /// + /// 速度缩放比例。 + /// + private float _speedScale = 1; + + /// + /// 自动起飞的目标高度。 + /// + private int _takeOffTargetAltitude; + + /// + /// FlyTo 的目标高度。 + /// + private float _targetAlt; + //航点开始高度 + private float _startAlt; + + private float _Lng_delta; + private float _Lat_delta; + + private float _flytime; + private DateTime _startTicks; + + private float _distance_xy; + private float _distance_z; + private float _xy_speed; + private float _z_speed; + private int _flytype; + private float currflytime; + // 根据飞行时间计算飞行距离。 + private float currdis_xy; + private float currdis_z; + + + // 目标点相对于开始位置的方向。 + private double _direction; + + /// + /// FlyTo 的目标纬度。 + /// + private double _targetLat; + + /// + /// FlyTo 的目标经度。 + /// + private double _targetLng; + /// + /// FlyTo 的目标纬度。 + /// + private double _startLat; + + /// + /// FlyTo 的目标经度。 + /// + private double _startLng; + + + /// + /// FlyTo 的目标经度。 + /// + private bool _ShowLED; + + private System.Drawing.ColorConverter rgbconverter; + + + /// + /// 使用 创建 实例。 + /// + public FakeCopter() : this(SynchronizationContext.Current) + { + + + } + + + new public int sim_update_int + { + get { return UPDATE_INTERVAL; } + set + { + //最大移动距离 + MAX_MOVE_IN_INTERVAL = MAX_VEL * value / 1000; + //快速模式最大移动距离 + MAX_MOVE_IN_INTERVAL_FAST = MAX_MOVE_IN_INTERVAL * 4; + //速度缩放后快速速度距离 + _scaledFastMaxMoveInInterval = MAX_MOVE_IN_INTERVAL_FAST* _speedScale; + //速度缩放后速度距离 + _scaledMaxMoveInInterval = MAX_MOVE_IN_INTERVAL* _speedScale; + Set(nameof(sim_update_int), ref UPDATE_INTERVAL, value); + } + } + + + public Task DoCommandAckAsync(ushort command, byte result) + { + return TaskUtils.CompletedTask; + } + + /// + /// 创建 实例。 + /// + /// UI 线程的同步上下文。 + /// 自动起飞时的目标高度。 + /// 速度缩放比例。 + public FakeCopter(SynchronizationContext uiSyncContext, int takeOffTargetAltitude = 10, float speedScale = 1, IConnection connection = null) + : base(uiSyncContext) + { + Latitude = 23.155382266268134; + Longitude = 113.45038586296141; + + Elevation = 20; + this.PropertyChanged += (sender, e) => + { + switch (e.PropertyName) + { + case nameof(Altitude): + Elevation = Altitude + 20; + break; + + case nameof(IsUnlocked): + if (IsUnlocked) + { + // 王海, 20151029, 在解锁时上次计算速度点、上次计算速度时刻。 + _lastCalcSpeedPoint = new PLLocation { Latitude = Latitude, Longitude = Longitude, Altitude = Altitude }; + _lastCalcSpeedTime = DateTime.Now; + } + break; + } + }; + + _takeOffTargetAltitude = takeOffTargetAltitude; + + _speedScale = speedScale; + _scaledMaxMoveInInterval = MAX_MOVE_IN_INTERVAL * _speedScale; + _scaledFastMaxMoveInInterval = MAX_MOVE_IN_INTERVAL_FAST * _speedScale; + + this.Connection = connection ?? EmptyConnection.Instance; + _isRunning = true; + // 持续计算并更新虚拟飞行器的状态。 + // Task.Run(async () => + + Task.Factory.StartNew(async () => + + { + while (_isRunning) + { + Update(); + await TaskUtils.Delay(UPDATE_INTERVAL).ConfigureAwait(false); + } + } + , TaskCreationOptions.LongRunning + ); + + + ++HeartbeatCount; + GpsFixType = GpsFixType.Fix3D; + GpsHdop = 1; + IsGpsAccurate = true; + HasSwitchedToGpsMode = true; + GC_xy = new GuidController(); + GC_z = new GuidController(); + + /* + // 持续假装收到飞行器发来的心跳。 + Task.Run(async () => + { + while (true) + { + if (_isRunning) + { + ++HeartbeatCount; + + IsCheckingConnection = false; + + if (HeartbeatCount >= 10) + { + // 收到若干个心跳之后,设置一下 GPS 相关属性。 + GpsFixType = GpsFixType.Fix3D; + GpsHdop = 1; + IsGpsAccurate = true; + HasSwitchedToGpsMode = true; + } + + _uiSyncContext.Post(() => RaiseHeartbeatReceived(new HeartbeatReceivedEventArgs(HeartbeatCount))); + } + await TaskUtils.Delay(HEARTBEAT_INTERVAL).ConfigureAwait(false); + } + }); + */ + } + + public IConnection Connection { get; set; } + + public string Id { get; set; } + + private int ReturnToLaunchAltitude { get { return _takeOffTargetAltitude; } } + private GuidController GC_xy; + private GuidController GC_z; + + + public Task ConnectAsync() + { + IsConnected = true; + _isRunning = true; + IsCheckingConnection = true; + return TaskUtils.CompletedTask; + } + + public Task DisconnectAsync() + { + IsConnected = false; + _isRunning = false; + IsCheckingConnection = false; + return TaskUtils.CompletedTask; + } + + protected override Task FlyToCoreAsync(double lat, double lng, float alt, float flytime = 0, int flytype = 0) + { + if (!IsEmergencyHoverActive) + { + _targetLat = lat; + _targetLng = lng; + _targetAlt = alt; + _startLat = Latitude; + _startLng = Longitude ; + _startAlt = Altitude; + _direction = this.CalcDirection2D(lat, lng); + + _flytime = flytime*1000; //ms + _startTicks = DateTime.Now; //ms + + + _Lng_delta = (float)( Math.Sin(_direction) * GeographyUtils.CalcMetersToLngSpan(Latitude)); + _Lat_delta =(float)( Math.Cos(_direction) * GeographyUtils.METERS_TO_LAT_SPAN); + + //计算xy和x方向距离 + _distance_xy = (float)this.CalcDistance(lat, lng, Altitude); + _distance_z = alt - Altitude; + Console.WriteLine($"{this.Id}d:{_distance_xy},lat{this.Latitude},lng{this.Longitude },tlat:{lat}tlng:{lng}"); + + _flytype = flytype; + // _xy_speed = _distance_xy / _flytime; + // _z_speed = _distance_z / _flytime; + GC_xy.initgetspeeddist(_flytype, _flytime / 1000, _distance_xy, acc_xy, maxspeed_xy); + if (_targetAlt>Altitude ) + GC_z.initgetspeeddist(_flytype, _flytime / 1000, _distance_z, acc_z, maxspeed_up); + else + GC_z.initgetspeeddist(_flytype, _flytime / 1000, _distance_z, acc_z, maxspeed_down); + + + + Mode = FlightMode.GUIDED; + + + } + return TaskUtils.CompletedTask; + } + + public Task GetCopterDataAsync() + { + return TaskUtils.CompletedTask; + } + + public Task GetParamAsync(string paramName, int millisecondsTimeout = Timeout.Infinite) + { + // TODO: 王海, 20150806, 实现仿真的 GetParamAsync。 + return Task.FromResult(0f); + } + + public Task LockAsync() + { + if (IsUnlocked) + { + IsUnlocked = false; + } + return TaskUtils.CompletedTask; + } + + public Task LEDAsync() + { + return TaskUtils.CompletedTask; + } + + public Task MotorTestAsync(int motor) + { + return TaskUtils.CompletedTask; + } + + public override Task SetChannelsAsync() + { + Channel1 = DesiredChannel1 ?? Channel1; + Channel2 = DesiredChannel2 ?? Channel2; + Channel3 = DesiredChannel3 ?? Channel3; + Channel4 = DesiredChannel4 ?? Channel4; + Channel5 = DesiredChannel5 ?? Channel5; + Channel6 = DesiredChannel6 ?? Channel6; + Channel7 = DesiredChannel7 ?? Channel7; + Channel8 = DesiredChannel8 ?? Channel8; + return TaskUtils.CompletedTask; + } + + public override Task SetMobileControlAsync() + { + Channel1 = DesiredChannel1 ?? Channel1; + Channel2 = DesiredChannel2 ?? Channel2; + Channel3 = DesiredChannel3 ?? Channel3; + Channel4 = DesiredChannel4 ?? Channel4; + Channel5 = DesiredChannel5 ?? Channel5; + Channel6 = DesiredChannel6 ?? Channel6; + Channel7 = DesiredChannel7 ?? Channel7; + Channel8 = DesiredChannel8 ?? Channel8; + Yaw = DesiredYaw ?? Yaw; + Heading = (short)Yaw; + return TaskUtils.CompletedTask; + } + + //得到收到的总数据量 + public void GetCommunicationNumber(out int recnumber, out int sendnumber) + { + recnumber = 0; + sendnumber = 0; + } + + + //重设数据量 + public void ResetCommunicationNumber() + { + + } + + public async Task DoCommandAsync(int actionid, float p1, float p2, float p3, float p4, float p5, float p6, float p7) + { + + await Task.Delay(50).ConfigureAwait(false); + + + } + public Task SetParamAsync(string paramName, float value, int millisecondsTimeout = Timeout.Infinite) + { + // TODO: 王海, 20150807, 实现仿真的 SetParamAsync。 + return TaskUtils.CompletedTask; + } + public bool GetShowLEDAsync() + { + return _ShowLED; + } + public Task SetShowLEDAsync(bool Ledon) + { + _ShowLED = Ledon; + RaiseLocationChanged(); + return TaskUtils.CompletedTask; + } + + + public Task SetShowRGBLEDAsync(int RedValue, int GreenValue, int BlueValue) + { + return TaskUtils.CompletedTask; + } + + + public Task SetShowLEDFlashAsync(int Flashtype, int millisecondsTime) + { + bool Ledison; + Ledison = GetShowLEDAsync(); + if (!Ledison) + { + SetShowLEDAsync(true); + Task.Delay(millisecondsTime).ConfigureAwait(false); + SetShowLEDAsync(false); + } + else + { + SetShowLEDAsync(false); + Task.Delay(millisecondsTime).ConfigureAwait(false); + SetShowLEDAsync(true); + } + + return TaskUtils.CompletedTask; + } + + public void SetProperties( + string id = null, //"Junqing's Drone", + double? latitude = null, //23.14973333, + double? longitude = null, //113.40974166, + float? altitude = null, //0, + string name = null, //"王海的飞行器", + byte? batteryPer = null, //10, + short? heading = null, //33, + bool? isConnected = null, //true, + float? pitch = null, //-70, + float? roll = null, //28, + byte? satCount = null, //6, + float? airSpeed = null, //3.333, + double? flightDistance = null, //100.388, + double? flightDistance2D = null, // 100.88, + TimeSpan? flightTimeSpan = null) + { + if (id != null) Id = id; + if (name != null) Name = name; + if (latitude != null) Latitude = latitude.Value; + if (longitude != null) Longitude = longitude.Value; + if (altitude != null) Altitude = altitude.Value; + if (batteryPer != null) BatteryPer = batteryPer.Value; + if (heading != null) Heading = heading.Value; + if (isConnected != null) IsConnected = isConnected.Value; + if (pitch != null) Pitch = pitch.Value; + if (roll != null) Roll = roll.Value; + if (satCount != null) SatCount = satCount.Value; + if (airSpeed != null) AirSpeed = airSpeed.Value; + if (flightDistance != null) FlightDistance = flightDistance.Value; + if (flightDistance2D != null) FlightDistance2D = flightDistance2D.Value; + if (flightTimeSpan != null) FlightTimeSpan = flightTimeSpan.Value; + } + + public Task StartPairingAsync() + { + RaisePairingCompleted(new PairingCompletedEventArgs(true, 0, 0)); + return TaskUtils.CompletedTask; + } + + public Task StopPairingAsync() + { + return TaskUtils.CompletedTask; + } + + public async Task TakeOffAsync(float alt) + { + await UnlockAsync().ConfigureAwait(false); + _takeOffTargetAltitude = (int)alt; + await TakeOffAsync().ConfigureAwait(false); + } + public Task MissionStartAsync(int hour_utc, int minute_utc, int second_utc, double Missionlng, double Missionlat) + { + return TaskUtils.CompletedTask; + } + + + public async Task UnlockAsync() + { + if (!IsUnlocked) + { + await SetChannelsAsync( + ch1: 1500, + ch2: 1500, + ch3: 1100, + ch4: 1500 + ).ConfigureAwait(false); + if (await SetModeAsync(FlightMode.ALT_HOLD).ConfigureAwait(false)) + { + IsUnlocked = true; + } + } + } + + /// + /// 无论在不在紧急悬停状态下都可用的切换模式方法。 + /// + /// 模式。 + /// 超时。 + /// 成功与否。 + internal override Task SetModeCoreAsync(FlightMode mode, int millisecondsTimeout = 5000) + { + Mode = mode; + return Task.FromResult(true); + } + + protected override void UpdateFlightDataIfNeeded() + { + //计算飞机距离,没必要 + /* + if (!TakeOffPoint.IsNullOrEmpty()) + { + FlightDistance = TakeOffPoint.CalcDistance(this); + FlightDistance2D = TakeOffPoint.CalcDistance2D(this); + } + + + if (FlightTimeSpan.TotalSeconds > 0) + { + if (_lastCalcSpeedPoint.IsNullOrEmpty()) + { + if (!this.IsNullOrEmpty()) + { + _lastCalcSpeedPoint = new PLLocation(this); + _lastCalcSpeedTime = DateTime.Now; + } + } + else + { + var movedDistance = _lastCalcSpeedPoint.CalcDistance(this); + var movedTime = DateTime.Now - _lastCalcSpeedTime; + if (movedDistance >= 3 || movedTime.TotalSeconds >= 3) + { + var airSpeed = movedDistance / movedTime.TotalSeconds; + if (airSpeed < 100) // 王海, 20151023, 速度过大时不正常,经纬度可能有错误。 + { + AirSpeed = (float)airSpeed; + } + //Windows.Messages.Message.Show(AirSpeed.ToString()); + GroundSpeed = AirSpeed; + _lastCalcSpeedPoint = new PLLocation(this); + _lastCalcSpeedTime = DateTime.Now; + } + } + } + */ + } + + private void MoveToPointImmediately(double lat, double lng, float alt) + { + Latitude = lat; + Longitude = lng; + Altitude = alt; + Roll = 0; + Pitch = 0; + } + public Task InjectGpsDataAsync(byte[] data, ushort length) + { + return TaskUtils.CompletedTask; + + } + + public static System.Drawing.Color GetRandomColor() + { + Random rand = new Random(); + return System.Drawing.Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256)); + } + + private DateTime led_laston; + private System.Drawing.Color Led_color; + + //更新显示颜色,根据设置的led参数 + private void UpdateShowColor() + { + // 使用实例化的对象调用ConvertFromString + //LEDShowColor = (System.Drawing.Color)converter.ConvertFromString("#" + LEDColor); + // 创建ColorConverter实例用于颜色转换 + if ( rgbconverter==null) rgbconverter=new System.Drawing.ColorConverter(); + //简化版的颜色模拟 + switch (LEDMode) + { + case 0: + if (LEDColor != null) + LEDShowColor = (System.Drawing.Color)rgbconverter.ConvertFromString("#" + LEDColor); + else LEDShowColor = (System.Drawing.Color)rgbconverter.ConvertFromString("#FFFFFF"); + + break; + //闪烁 + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + + if (DateTime.Now.AddMilliseconds(-(LEDInterval*1000)) >= led_laston) + { + led_laston = DateTime.Now; + if (LEDShowColor != System.Drawing.Color.Black) + { + LEDShowColor = System.Drawing.Color.Black; + + } + else + { + if ((LEDMode == 1) || (LEDMode == 4) || (LEDMode == 5) || (LEDMode == 6) || (LEDMode == 7) || (LEDMode == 9) || (LEDMode == 11) || (LEDMode == 13)) + LEDShowColor = (System.Drawing.Color)rgbconverter.ConvertFromString("#" + LEDColor); + if ((LEDMode == 2)||(LEDMode == 3) || (LEDMode == 8) || (LEDMode == 10) || (LEDMode == 12) || (LEDMode == 14)) + LEDShowColor = GetRandomColor(); + } + } + + break; + + } + + + + + + } + + private bool ReachedDestination(double destLat, double destLng, float destAlt) => this.CalcDistance(destLat, destLng, destAlt) < 0.1; + + /// + /// 计算并更新虚拟飞行器状态。 + /// + private void Update() + { + if (IsUnlocked) + { + // 飞行器接触地面(只判断 Altitude <= 0)后,如果处于 LAND 模式或者保持 Channel3 < MIN_HOVER_CHANNEL,在一段时间后自动锁定。 + if ((Mode == FlightMode.LAND || Channel3 < MIN_HOVER_CHANNEL) && Altitude <= 0) + { + Task.Run(async () => + { + for (int i = 0; i < 4000 / 20; i++) + { + await TaskUtils.Delay(20).ConfigureAwait(false); + if ((Mode != FlightMode.LAND && Channel3 >= MIN_HOVER_CHANNEL) || !IsUnlocked) + { + return; + } + } + IsUnlocked = false; + }); + } + else + { + switch (Mode) + { + case FlightMode.ACRO: + break; + + case FlightMode.AUTO: + /* + // 暂时只管起飞。以最大速度上升,直到达到目标高度。 + if (Altitude < _takeOffTargetAltitude) + { + Altitude += _scaledMaxMoveInInterval; + } + else + { + Mode = FlightMode.LOITER; + } + */ + break; + + case FlightMode.GUIDED: + // 王海, 20160317, 指点时也能体感控制若干通道。 + //考虑不更新这个,好像没必要-xu + //UpdateWithChannels(); + //UpdateWithDestination(_targetLat, _targetLng, _targetAlt); + if (FlightControlMode==1) + UpdateWithDestination_v2(_targetLat, _targetLng, _targetAlt); + else + UpdateWithDestination(_targetLat, _targetLng, _targetAlt); + break; + + case FlightMode.STABILIZE: + case FlightMode.ALT_HOLD: + case FlightMode.LOITER: + UpdateWithChannels(); + break; + + case FlightMode.RTL: + if (Altitude < ReturnToLaunchAltitude && !ReachedDestination(Latitude, Longitude, ReturnToLaunchAltitude)) + { + UpdateWithDestination(Latitude, Longitude, ReturnToLaunchAltitude); + } + else if (Altitude > ReturnToLaunchAltitude && !ReachedDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, Altitude)) + { + UpdateWithDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, Altitude); + } + else + { + UpdateWithDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, ReturnToLaunchAltitude); + } + break; + + case FlightMode.CIRCLE: + break; + + case FlightMode.POSITION: + break; + + case FlightMode.LAND: + // 王海, 20160317, 降落时也能体感控制若干通道。 + UpdateWithChannels(); + // 以最大速度降落,直到高度为 0。 + if (Altitude > 0) + { + if (Altitude > _scaledMaxMoveInInterval) + { + Altitude -= _scaledMaxMoveInInterval; + } + else + { + Altitude = 0; + } + Roll = 0; + Pitch = 0; + } + break; + + case FlightMode.OF_LOITER: + break; + + case FlightMode.TOY: + break; + + default: + break; + } + //UpdateFlightDataIfNeeded(); + } + } + else + { + // TODO: 王海, 20151228, 模拟空中锁定。 + // 锁定时直接把速度设为 0。 + AirSpeed = 0; + } + + // UpdateShowColor(); + + + + _uiSyncContext.Post(() => + { + //位置变化需要在UI刷新 + RaiseLocationChangedIfNeeded(); + //高度变化需要在UI刷新 + // RaiseAltitudeChangedIfNeeded(); + //GPS数据用于显示 + // RaiseDataStreamReceived(PDataStreamType.GPS_RAW_INT); + //不考虑姿态 + // RaiseAttitudeChanged(); + }); + } + + /// + /// 根据各个通道值来更新位置、姿态等状态。 + /// + private void UpdateWithChannels() + { + var maxMove = Mode == FlightMode.LOITER ? _scaledMaxMoveInInterval : _scaledFastMaxMoveInInterval; + var ch3Delta = Channel3 > MAX_HOVER_CHANNEL ? Channel3 - MAX_HOVER_CHANNEL : Channel3 < MIN_HOVER_CHANNEL ? Channel3 - MIN_HOVER_CHANNEL : 0; + if (ch3Delta != 0) + { + // 更新相对高度。 + Altitude = Math.Max(0, Altitude + maxMove * ch3Delta / MAX_CHANNEL_DELTA); + } + + var yawRad = Yaw.DegToRad(); + var metersToLocalLng = GeographyUtils.CalcMetersToLngSpan(Latitude); + + var ch1Delta = Channel1 > MAX_HOVER_CHANNEL ? Channel1 - MAX_HOVER_CHANNEL : Channel1 < MIN_HOVER_CHANNEL ? Channel1 - MIN_HOVER_CHANNEL : 0; + var xDistance = maxMove * ch1Delta / MAX_CHANNEL_DELTA; + if (ch1Delta != 0) + { + // 更新横滚方向的纬度和经度。 + Latitude -= xDistance * Math.Sin(yawRad) * GeographyUtils.METERS_TO_LAT_SPAN; + Longitude += xDistance * Math.Cos(yawRad) * metersToLocalLng; + // 更新横滚姿态。 + Roll = MAX_TILT_IN_FLIGHT * ch1Delta / MAX_CHANNEL_DELTA; + } + else + { + Roll = 0; + } + + var ch2Delta = Channel2 > MAX_HOVER_CHANNEL ? Channel2 - MAX_HOVER_CHANNEL : Channel2 < MIN_HOVER_CHANNEL ? Channel2 - MIN_HOVER_CHANNEL : 0; + var yDistance = maxMove * ch2Delta / MAX_CHANNEL_DELTA; + if (ch2Delta != 0) + { + // 更新俯仰方向的纬度和经度。 + Latitude -= yDistance * Math.Cos(yawRad) * GeographyUtils.METERS_TO_LAT_SPAN; + Longitude -= yDistance * Math.Sin(yawRad) * metersToLocalLng; + // 更新俯仰姿态。 + Pitch = MAX_TILT_IN_FLIGHT * ch2Delta / MAX_CHANNEL_DELTA; + } + else + { + Pitch = 0; + } + } + + /// + /// 根据目的地来更新位置、姿态等状态。 + /// + private void UpdateWithDestination(ILocation loc) + { + UpdateWithDestination(loc.Latitude, loc.Longitude, loc.Altitude); + } + + /// + /// 根据目的地来更新位置、姿态等状态。 + /// + private void UpdateWithDestination(double lat, double lng, float alt) + { + // 与目标点之间的距离。 + var distance = this.CalcDistance(lat, lng, alt); + // 距离已经很近,直接移动到目标点。 + if (distance < _scaledMaxMoveInInterval) + { + MoveToPointImmediately(lat, lng, alt); + return; + } + // 在空间中的移动距离。 + var move = _scaledMaxMoveInInterval; + // 竖直方向的移动距离。 + var altDelta = (float)(move * (alt - Altitude) / distance); + // 更新高度。 + Altitude += altDelta; + // 目标点相对于当前位置的方向。 + // var direction = this.CalcDirection2D(lat, lng); + // 水平面上的移动距离。 + var moveInHorizontalPlane = Math.Sqrt(move * move - altDelta * altDelta); + if (double.IsNaN(moveInHorizontalPlane)) + { + MoveToPointImmediately(lat, lng, alt); + return; + } + + // 更新纬度。 + //Longitude += moveInHorizontalPlane * Math.Sin(direction) * GeographyUtils.CalcMetersToLngSpan(Latitude); + Longitude += moveInHorizontalPlane * _Lng_delta; + Latitude += moveInHorizontalPlane * _Lat_delta; + // 更新经度。 + //Latitude += moveInHorizontalPlane * Math.Cos(direction) * GeographyUtils.METERS_TO_LAT_SPAN; + } + //新版本小航点计算移动位置 + private void UpdateWithDestination_v2(double lat, double lng, float alt) + { + //_flytime 总飞行时间 秒 + //_startTicks 开始飞行时间ms + // _distance_xy 米 + // _distance_z 米 + //当前飞行时间 + if ((lat == Latitude) && (lng == Longitude) && (alt == Altitude)) return; + currflytime =(float)( DateTime.Now -_startTicks).TotalMilliseconds;//实际飞行时间 ms + //超时直接移动到目标点 + if (currflytime >= _flytime) + { + MoveToPointImmediately(lat, lng, alt); + return; + } + //if (currflytime > 13000) + // return; + // 根据飞行时间计算飞行距离 + + float vspeed = 0; + + GC_xy.getspeeddist(_flytype, _flytime / 1000, _distance_xy, currflytime / 1000, acc_xy,maxspeed_xy , out currdis_xy, out vspeed); + + if (alt> Altitude) + GC_z.getspeeddist(_flytype, _flytime / 1000, _distance_z, currflytime / 1000, acc_z,maxspeed_up, out currdis_z, out vspeed); + else + GC_z.getspeeddist(_flytype, _flytime / 1000, _distance_z, currflytime / 1000, acc_z,maxspeed_down, out currdis_z, out vspeed); + + Console.WriteLine($"{this.Id}:time:{currflytime / 1000} to {currdis_z}"); + + // 距离已经很近,直接移动到目标点。 + if ((Math.Abs(_distance_xy-currdis_xy) < 0.1)&& (Math.Abs(_distance_z - currdis_z) < 0.1)) + { + Console.WriteLine($"{this.Id} to tlat:{lat}tlng:{lng}"); + + MoveToPointImmediately(lat, lng, alt); + return; + } + // 更新位置 + Altitude = _startAlt+ currdis_z; + Longitude = _startLng + currdis_xy*_Lng_delta; + Latitude = _startLat + currdis_xy *_Lat_delta; + } + + } +}