Mark van Renswoude
d6fbff68af
Added detection of sim running and paused states Added rudimentary live KML option (needs more styling)
165 lines
5.0 KiB
C#
165 lines
5.0 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Ionic.BZip2;
|
|
using Nito.AsyncEx;
|
|
using SharpKml.Base;
|
|
using SharpKml.Dom;
|
|
|
|
namespace FlightLoggerLib.Concrete
|
|
{
|
|
public class KMLLiveFlightLogger : IFlightLogger
|
|
{
|
|
private readonly CancellationTokenSource listenerCancellationTokenSource = new CancellationTokenSource();
|
|
private readonly HttpListener listener;
|
|
private readonly Task listenerTask;
|
|
private readonly string baseUrl;
|
|
|
|
private FlightPosition currentPosition;
|
|
private readonly AsyncLock currentPositionLock = new AsyncLock();
|
|
|
|
private byte[] entryDocument;
|
|
private Document dynamicDocument;
|
|
private Placemark dynamicPlacemark;
|
|
|
|
|
|
public KMLLiveFlightLogger(int port)
|
|
{
|
|
baseUrl = $"http://127.0.0.1:{port}/";
|
|
PrepareEntryDocument();
|
|
|
|
listener = new HttpListener();
|
|
listener.Prefixes.Add(baseUrl);
|
|
listener.Start();
|
|
|
|
listenerTask = Task.Run(RunServer);
|
|
}
|
|
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
listener?.Stop();
|
|
listenerCancellationTokenSource.Cancel();
|
|
|
|
await listenerTask;
|
|
}
|
|
|
|
|
|
protected void PrepareEntryDocument()
|
|
{
|
|
var networkLink = new NetworkLink
|
|
{
|
|
Name = "Refreshes every 5 seconds",
|
|
Link = new Link
|
|
{
|
|
Href = new Uri($"{baseUrl}live/dynamic", UriKind.Absolute),
|
|
RefreshMode = RefreshMode.OnInterval,
|
|
RefreshInterval = 5
|
|
}
|
|
};
|
|
|
|
var output = new Document();
|
|
output.AddFeature(networkLink);
|
|
|
|
var serializer = new Serializer();
|
|
serializer.Serialize(output);
|
|
entryDocument = Encoding.UTF8.GetBytes(serializer.Xml);
|
|
}
|
|
|
|
|
|
protected void PrepareDynamicDocument(Vector coordinate, float altitudeInFeet)
|
|
{
|
|
var name = $"Live location ({altitudeInFeet:#} feet)";
|
|
|
|
if (dynamicPlacemark != null)
|
|
{
|
|
dynamicPlacemark.Name = name;
|
|
((Point)dynamicPlacemark.Geometry).Coordinate = coordinate;
|
|
return;
|
|
}
|
|
|
|
var point = new Point { Coordinate = coordinate, AltitudeMode = AltitudeMode.Absolute };
|
|
dynamicPlacemark = new Placemark
|
|
{
|
|
Name = name,
|
|
//StyleUrl = new Uri("#" + styleMapId, UriKind.Relative),
|
|
Geometry = point,
|
|
Visibility = true
|
|
};
|
|
|
|
dynamicDocument = new Document();
|
|
dynamicDocument.AddFeature(dynamicPlacemark);
|
|
}
|
|
|
|
|
|
public Task NewLog()
|
|
{
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
public async Task LogPosition(DateTime eventTime, FlightPosition position)
|
|
{
|
|
using (await currentPositionLock.LockAsync())
|
|
{
|
|
currentPosition = position;
|
|
}
|
|
}
|
|
|
|
|
|
private const float MetersPerFoot = 0.3048f;
|
|
|
|
|
|
private async Task RunServer()
|
|
{
|
|
while (!listenerCancellationTokenSource.IsCancellationRequested)
|
|
{
|
|
var context = await listener.GetContextAsync();
|
|
|
|
switch (context.Request.Url.AbsolutePath)
|
|
{
|
|
case "/live":
|
|
context.Response.StatusCode = 200;
|
|
await context.Response.OutputStream.WriteAsync(entryDocument, 0, entryDocument.Length);
|
|
break;
|
|
|
|
case "/live/dynamic":
|
|
{
|
|
byte[] buffer;
|
|
|
|
using (await currentPositionLock.LockAsync())
|
|
{
|
|
var altitudeFeet = currentPosition?.Altitude ?? -10;
|
|
var altitudeMeters = altitudeFeet * MetersPerFoot;
|
|
|
|
PrepareDynamicDocument(new Vector
|
|
{
|
|
Latitude = currentPosition?.Latitude ?? 24.999979,
|
|
Longitude = currentPosition?.Longitude ?? -70.999997,
|
|
Altitude = altitudeMeters
|
|
},
|
|
altitudeFeet);
|
|
|
|
var serializer = new Serializer();
|
|
serializer.Serialize(dynamicDocument);
|
|
buffer = Encoding.UTF8.GetBytes(serializer.Xml);
|
|
}
|
|
|
|
context.Response.StatusCode = 200;
|
|
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
context.Response.StatusCode = 404;
|
|
break;
|
|
}
|
|
|
|
context.Response.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|