22 Commits

Author SHA1 Message Date
1fc6d2bbcd Updated port on docker-compose.yml file 2022-02-23 22:16:23 +00:00
3f9bf384b0 Setup for docker use 2022-02-19 21:26:31 +00:00
e783f8c614 Added metar to route page 2022-02-18 22:47:00 +00:00
c418188481 Completed app page + added metar class 2022-02-18 22:43:01 +00:00
ec5ca51602 Added double negation to clean code 2022-02-18 22:01:53 +00:00
eff67b7697 Added flight sim view 2022-02-18 21:46:00 +00:00
e271e49374 Added closest waypoint function 2022-02-18 21:13:21 +00:00
5316df08a5 Several changes - updated string to route to include coordinates - updated model for flight sim to only contain the closest point - User model now tracks route status... departure etc 2022-02-18 20:38:45 +00:00
10af903706 Added MongoDB Methods and introduced flight sim controller 2022-02-18 18:49:56 +00:00
eda71069a4 Updated user model to allow for storing of departure and arrival points 2022-02-18 18:36:10 +00:00
8be3b0fa02 Added SimPositionModel from flight tracker + setup mongo files 2022-02-18 17:18:00 +00:00
0252f115c6 Updated navdata to take table headings as opposed to index values from database 2022-02-17 20:21:50 +00:00
8ac4a3d693 Chart Viewer now complete 2022-02-15 16:03:46 +00:00
39cc7caef7 Tried sending model back to controller but now unsure of how to do so 2022-02-11 20:55:53 +00:00
a4e7a30a9f Force user to be logged in before accessing routes 2022-02-11 20:14:57 +00:00
d8a40e64ef Adapted ChartModel to accept an ICAO Code 2022-02-11 19:54:11 +00:00
0c68d05af1 Added Current Chart to user model 2022-02-11 19:41:46 +00:00
dd2a367a58 Added and Started on views for Chart Viewer 2022-02-11 19:40:31 +00:00
76878e7745 Updated Chart Fetch functions in order to create a more generic template 2022-02-11 17:00:08 +00:00
35a6686cd5 Updated layout to use EMail when logged in instead of API Token 2022-02-09 20:48:10 +00:00
39c18ee9bf Updated NavdataModel and RouteController to user environment Variables 2022-02-09 20:41:39 +00:00
30ac25c8fb Added addition constructor for JSON Serialisation 2022-02-09 20:40:24 +00:00
30 changed files with 1013 additions and 231 deletions

24
.dockerignore Normal file
View File

@ -0,0 +1,24 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
README.md

14
.vscode/launch.json vendored
View File

@ -2,19 +2,14 @@
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net5.0/EFB.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
@ -30,6 +25,15 @@
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"name": "Docker .NET Core Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"netCore": {
"appProject": "${workspaceFolder}/EFB.csproj"
}
}
]
}

56
.vscode/tasks.json vendored
View File

@ -37,6 +37,62 @@
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"type": "docker-build",
"label": "docker-build: debug",
"dependsOn": [
"build"
],
"dockerBuild": {
"tag": "efb:dev",
"target": "base",
"dockerfile": "${workspaceFolder}/Dockerfile",
"context": "${workspaceFolder}",
"pull": true
},
"netCore": {
"appProject": "${workspaceFolder}/EFB.csproj"
}
},
{
"type": "docker-build",
"label": "docker-build: release",
"dependsOn": [
"build"
],
"dockerBuild": {
"tag": "efb:latest",
"dockerfile": "${workspaceFolder}/Dockerfile",
"context": "${workspaceFolder}",
"pull": true
},
"netCore": {
"appProject": "${workspaceFolder}/EFB.csproj"
}
},
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": [
"docker-build: debug"
],
"dockerRun": {},
"netCore": {
"appProject": "${workspaceFolder}/EFB.csproj",
"enableDebugging": true
}
},
{
"type": "docker-run",
"label": "docker-run: release",
"dependsOn": [
"docker-build: release"
],
"dockerRun": {},
"netCore": {
"appProject": "${workspaceFolder}/EFB.csproj"
}
}
]
}

View File

@ -23,18 +23,17 @@ namespace EFB.Controllers
public IActionResult Index()
{
//Check to see what point on the application the user is at and where they should be sent
UserModel User = HttpContext.Session.GetObject<UserModel>("User");
if (User != null)
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if (user == null)
{
if (User.Route == null)
{
return RedirectToAction("Index", "Route");
}else{
return RedirectToAction("Index", "App");
}
}else{
return RedirectToAction("Index", "Home");
}
if (user.Route == null)
{
return RedirectToAction("Index", "Route");
}
return View(user);
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using EFB.Models;
using EFB.Models.JSON;
using EFB.Controllers.Form;
using EFB.Sessions;
namespace EFB.Controllers
{
//[Route("[controller]")]
public class ChartsController : Controller
{
private readonly ILogger<ChartsController> _logger;
public ChartsController(ILogger<ChartsController> logger)
{
_logger = logger;
}
public async Task<IActionResult> Index(string ICAO)
{
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if (user == null)
{
TempData["Error"] = "Must be logged in to view charts";
return RedirectToAction("Index", "Home");
}
if(ICAO == null)
return View();
if (FormAuthenticator.ValidateICAOCode(ICAO))
{
var charts = await ChartModel.FetchAsync(ICAO);
if (charts != null)
{
ChartModel chartModel = new ChartModel(ICAO, charts);
//Save the current chart into user model for later access
user.CurrentCharts = chartModel;
HttpContext.Session.SetObject("User", user);
return RedirectToAction("ViewCharts");
}
}else
{
TempData["Error"] = "Invalid ICAO";
}
return View();
}
public IActionResult ViewCharts(string chart){
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if (user != null)
{
ViewChartModel charts = new ViewChartModel(user.CurrentCharts, chart);
if (charts != null)
{
return View("ViewCharts", charts);
}
return RedirectToAction("Index");
}
return RedirectToAction("Index", "Home");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View("Error!");
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using EFB.Sessions;
using Microsoft.Extensions.Logging;
using EFB.Models;
using EFB.MongoData;
using EFB.Models.Route;
namespace EFB.Controllers
{
//[Route("[controller]")]
public class FlightsimController : Controller
{
private readonly ILogger<FlightsimController> _logger;
public FlightsimController(ILogger<FlightsimController> logger)
{
_logger = logger;
}
public async Task<IActionResult> Index()
{
//Retrieve and Check current user status
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if(user == null){
TempData["Error"] = "You must be logged in before you are able to view the FlightSim Page";
return RedirectToAction("Index", "Home");
}
//Retrieve the user's latest sim position and construct it into FlightsimModel
if (user.Route == null){
TempData["Error"] = "You must have a route planned before you are able to view the Flightsim page";
return RedirectToAction("Index", "Route");
}
SimPositionModel latestPositionModel = await Mongo.GetLatestData(user.EMail);
RouteModel route = await RouteModel.StringToRoute(user.Departure, user.Arrival, user.Cruise, user.Route);
IWaypoint closest = DetermineClosest(route, latestPositionModel.LatestPosition);
return View(new FlightsimModel(latestPositionModel, closest));
}
private IWaypoint DetermineClosest(RouteModel route, SimPosition currentPosition){
IWaypoint closest = null;
float closestDistance = float.MaxValue;
//Assuming that we are on the earth for simplicity
IWaypoint waypoint = route.Departure;
while (waypoint.Next != null)
{
int earthRadius = 6371;
float distanceLat = DegreesToRadians(waypoint.Latitude - currentPosition.Latitude);
float distanceLon = DegreesToRadians(waypoint.Longitude - currentPosition.Longitude);
float latitude1 = DegreesToRadians(currentPosition.Latitude);
float latitude2 = DegreesToRadians(waypoint.Latitude);
var a = Math.Sin(distanceLat/2) * Math.Sin(distanceLat/2) + Math.Sin(distanceLon/2) * Math.Sin(distanceLon/2) * Math.Cos(latitude1) * Math.Cos(latitude2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1-a));
float distance = (float)(earthRadius * c);
if (distance < closestDistance)
{
closest = waypoint;
closestDistance = distance;
}
waypoint = waypoint.Next;
}
return closest;
}
private float DegreesToRadians(float degrees){
return (float)(degrees * Math.PI / 180);
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View("Error!");
}
}
}

View File

@ -46,163 +46,150 @@ namespace EFB.Controllers
public async Task<IActionResult> New(string departure, string arrival, string cruise)
{
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if (!(user == null || user.UserToken.IsExpired()))
if (user == null || user.UserToken.IsExpired())
{//If the user is still authenticated
if (FormAuthenticator.ValidateICAOCode(departure) && FormAuthenticator.ValidateICAOCode(arrival))
{//If the user has entered valid ICAOs
return RedirectToAction("Index", "Home");
}
uint cruiseAlt;
if (!FormAuthenticator.ValidateICAOCode(departure) || !FormAuthenticator.ValidateICAOCode(arrival))
{//If the user has entered valid ICAOs
TempData["Error"] = "Invalid Departure or Arrival ICAO";
return RedirectToAction("Index", "Route");
}
if (uint.TryParse(cruise, out cruiseAlt) && FormAuthenticator.ValidateCruiseAlt(cruiseAlt))
{//If the cruise altitude if within limits.
uint cruiseAlt;
//Submit route request...
APIInterface API = new APIInterface();
if (uint.TryParse(cruise, out cruiseAlt) && FormAuthenticator.ValidateCruiseAlt(cruiseAlt))
{//If the cruise altitude if within limits.
//Prepare data to be send off with request (route)
Dictionary<string, string> headerData = new Dictionary<string, string>();
headerData.Add("Authorization", $"Bearer {user.UserToken.TokenValue}");
//Submit route request...
APIInterface API = new APIInterface();
RouteRequest routeRequest = new RouteRequest()
{
departure = departure,
destination = arrival,
preferredminlevel = cruiseAlt / 1000,
preferredmaxlevel = cruiseAlt / 1000,
};
StringContent content = new StringContent(JsonConvert.SerializeObject(routeRequest), Encoding.UTF8, "application/json");
//Prepare data to be send off with request (route)
Dictionary<string, string> headerData = new Dictionary<string, string>();
headerData.Add("Authorization", $"Bearer {user.UserToken.TokenValue}");
//Make initial Route Request
var requestRoute = API.Post<string>("https://api.autorouter.aero/v1.0/router", headerData, content);
RouteRequest routeRequest = new RouteRequest()
{
departure = departure,
destination = arrival,
preferredminlevel = cruiseAlt / 1000,
preferredmaxlevel = cruiseAlt / 1000,
};
StringContent content = new StringContent(JsonConvert.SerializeObject(routeRequest), Encoding.UTF8, "application/json");
ResponseModel<string> responseRoute = await requestRoute;
//Make initial Route Request
var requestRoute = API.Post<string>("https://api.autorouter.aero/v1.0/router", headerData, content);
if (responseRoute.Error == null)
{//Update User session and add route ID
TokenModel routeToken = new TokenModel()
{
TokenValue = responseRoute.Result.ToString()
};
ResponseModel<string> responseRoute = await requestRoute;
user.RouteToken = routeToken;
HttpContext.Session.SetObject("User", user);
if (responseRoute.Error == null)
{//Update User session and add route ID
TokenModel routeToken = new TokenModel()
{
TokenValue = responseRoute.Result.ToString()
};
return await Poll(departure, arrival, cruiseAlt);
user.RouteToken = routeToken;
HttpContext.Session.SetObject("User", user);
}
TempData["Error"] = responseRoute.Error;
return RedirectToAction("Index", "Route");
}
TempData["Error"] = "Invalid Cruise Altitude";
TempData["Departure"] = departure;
TempData["Arrival"] = arrival;
return RedirectToAction("Index", "Route");
return await Poll(departure, arrival, cruiseAlt);
}
TempData["Error"] = "Invalid Departure or Arrival ICAO";
TempData["Error"] = responseRoute.Error;
return RedirectToAction("Index", "Route");
}
return RedirectToAction("Index", "Home");
TempData["Error"] = "Invalid Cruise Altitude";
TempData["Departure"] = departure;
TempData["Arrival"] = arrival;
return RedirectToAction("Index", "Route");
}
public async Task<IActionResult> Poll(string departure, string arrival, uint cruise)
{
if (HttpContext.Session.GetString("User") != null)
{//If the user is currently logged in
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if (user.RouteToken != null)
{//If the user has a route object (e.g, they have been to the route page)
//Make calls to the server to fetch route
bool collected = false;
int pollCount = 0;
string routeString = "";
APIInterface API = new APIInterface();
Dictionary<string, string> headerData = new Dictionary<string, string>();
/*-----Chart Fetching Operations--------*/
//Prepare data to be send off with request(charts)
headerData = new Dictionary<string, string>();
headerData.Add("referer", "luke-else.co.uk");
Dictionary<string, string> formData = new Dictionary<string, string>();
formData.Add("token", "");
var body = new FormUrlEncodedContent(formData);
//make Charts request
var requestDepartureCharts = API.Post<ChartResponse>($"https://chartfox.org/api/charts/grouped/{departure}", headerData, body);
var requestArrivalCharts = API.Post<ChartResponse>($"https://chartfox.org/api/charts/grouped/{arrival}", headerData, body);
/*----------------------------------*/
//Run route Polling
headerData.Add("Authorization", $"Bearer {user.UserToken.TokenValue}");
while (collected == false && pollCount < 15)
{
//Make Polling Request
var pollingRequest = API.Put<List<PollResponse>>($"https://api.autorouter.aero/v1.0/router/{user.RouteToken.TokenValue}/longpoll", headerData, null);
ResponseModel<List<PollResponse>> responsePoll = await pollingRequest;
foreach (var item in responsePoll.Result)
{
if (item.Command == "fpl" || item.Command == "solution")
{
collected = true;
routeString = item.FlightPlan;
break;
}
}
Thread.Sleep(3000);
pollCount++;
}
if (collected)
{
//Get Response from Charts
ResponseModel<ChartResponse> responseDepartureCharts = await requestDepartureCharts;
if (responseDepartureCharts.Error == null && responseDepartureCharts.Result.Status == "success")
{
user.DepartureCharts = new ChartModel(responseDepartureCharts.Result.Response);
}
ResponseModel<ChartResponse> responseArrivalCharts = await requestArrivalCharts;
if (responseArrivalCharts.Error == null && responseArrivalCharts.Result.Status == "success")
{
user.ArrivalCharts = new ChartModel(responseArrivalCharts.Result.Response);
}
//fill in route
string finalRoute = RouteModel.ParseRoute(routeString);
user.Route = finalRoute;
HttpContext.Session.SetObject("User", user);
return RedirectToAction("Index", "Route");
}
TempData["Error"] = $"Unable to get route after {pollCount} Attempts!";
return RedirectToAction("Index", "Route");
}
else
{
return RedirectToAction("Index", "Route");
}
}
else
{
if (HttpContext.Session.GetString("User") == null)
{//If the user is not currently logged in
TempData["Error"] = "Please login before trying to plan a route";
return RedirectToAction("Index", "Route");
}
UserModel user = HttpContext.Session.GetObject<UserModel>("User");
if (user.RouteToken == null)
{//If the user has a route object (e.g, they have been to the route page)
return RedirectToAction("Index", "Route");
}
//Make calls to the server to fetch route
bool collected = false;
int pollCount = 0;
string routeString = "";
APIInterface API = new APIInterface();
Dictionary<string, string> headerData = new Dictionary<string, string>();
/*-----Chart Fetching Operations--------*/
var requestDepartureCharts = ChartModel.FetchAsync(departure);
var requestArrivalCharts = ChartModel.FetchAsync(arrival);
/*----------------------------------*/
//Run route Polling
headerData.Add("Authorization", $"Bearer {user.UserToken.TokenValue}");
while (collected == false && pollCount < 15)
{
//Make Polling Request
var pollingRequest = API.Put<List<PollResponse>>($"https://api.autorouter.aero/v1.0/router/{user.RouteToken.TokenValue}/longpoll", headerData, null);
ResponseModel<List<PollResponse>> responsePoll = await pollingRequest;
foreach (var item in responsePoll.Result)
{
if (item.Command == "notvalid" || item.Command == "solution")
{
collected = true;
routeString = item.FlightPlan;
break;
}
}
Thread.Sleep(3000);
pollCount++;
}
if (collected)
{
//Get Response from Charts
ChartList departureCharts = await requestDepartureCharts;
if (departureCharts != null)
{
user.DepartureCharts = new ChartModel(departure, departureCharts);
}
ChartList arrivalCharts = await requestArrivalCharts;
if (arrivalCharts != null)
{
user.ArrivalCharts = new ChartModel(arrival, arrivalCharts);
}
//fill in route
string finalRoute = RouteModel.ParseRoute(routeString);
user.Route = finalRoute;
user.Departure = departure;
user.Arrival = arrival;
user.Cruise = cruise;
HttpContext.Session.SetObject("User", user);
return RedirectToAction("Index", "App");
}
TempData["Error"] = $"Unable to get route after {pollCount} Attempts!";
return RedirectToAction("Index", "Route");
}

View File

@ -31,63 +31,58 @@ namespace EFB.Controllers
public async Task<IActionResult> Login(string email, string password){
if (Form.FormAuthenticator.ValidateEMail(email))
if (!Form.FormAuthenticator.ValidateEMail(email))
{
//API Helper
APIInterface API = new APIInterface();
//Dictionary of Formdata to be encoded
Dictionary<string, string> formData = new Dictionary<string, string>();
formData.Add("grant_type", "client_credentials");
formData.Add("client_id", email);
formData.Add("client_secret", password);
HttpContent content = new FormUrlEncodedContent(formData);
var request = API.Post<Models.JSON.LoginResponse>("https://api.autorouter.aero/v1.0/oauth2/token", null, content);
//Wait for the response to come through
ResponseModel<LoginResponse> response = await request;
if (response.Error != null)
{
TempData["Error"] = response.Error;
TempData["email"] = email;
return RedirectToAction("Index", "Home");
}else{
//Type cast required but we know response will be of known type
LoginResponse login = response.Result;
//Generate User Session
if (login.error == null)
{
UserModel user = new UserModel{
EMail = email,
UserToken = new TokenModel{
TokenValue = login.access_token,
Expiration = DateTime.UtcNow.AddSeconds(login.expires_in)
}
};
//Using Session Extensions (Store the user session)
HttpContext.Session.SetObject("User", user);
return RedirectToAction("Index", "App");
}else{
TempData["Error"] = login.error_description;
TempData["email"] = email;
return RedirectToAction("Index", "Home");
}
}
}else{
TempData["Error"] = "Please enter a valid E-Mail";
return RedirectToAction("Index", "Home");
}
//API Helper
APIInterface API = new APIInterface();
//Dictionary of Formdata to be encoded
Dictionary<string, string> formData = new Dictionary<string, string>();
formData.Add("grant_type", "client_credentials");
formData.Add("client_id", email);
formData.Add("client_secret", password);
HttpContent content = new FormUrlEncodedContent(formData);
var request = API.Post<Models.JSON.LoginResponse>("https://api.autorouter.aero/v1.0/oauth2/token", null, content);
//Wait for the response to come through
ResponseModel<LoginResponse> response = await request;
if (response.Error != null)
{
TempData["Error"] = response.Error;
TempData["email"] = email;
return RedirectToAction("Index", "Home");
}
//Type cast required but we know response will be of known type
LoginResponse login = response.Result;
//Generate User Session
if (login.error != null)
{
TempData["Error"] = login.error_description;
TempData["email"] = email;
return RedirectToAction("Index", "Home");
}
UserModel user = new UserModel{
EMail = email,
UserToken = new TokenModel{
TokenValue = login.access_token,
Expiration = DateTime.UtcNow.AddSeconds(login.expires_in)
}
};
//Using Session Extensions (Store the user session)
HttpContext.Session.SetObject("User", user);
return RedirectToAction("Index", "App");
}
public IActionResult Logout(){

26
Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM mcr.microsoft.com/dotnet/aspnet:5.0-focal AS base
WORKDIR /app
EXPOSE 5000
ENV ASPNETCORE_URLS=http://+:5000
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser
FROM mcr.microsoft.com/dotnet/sdk:5.0-focal AS build
WORKDIR /src
COPY ["EFB.csproj", "./"]
RUN dotnet restore "EFB.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "EFB.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "EFB.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "EFB.dll"]

View File

@ -5,5 +5,8 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
<PackageReference Include="MySql.Data" Version="*"/>
<PackageReference Include="MongoDB.Driver" Version="*"/>
<PackageReference Include="MongoDB.Bson" Version="*"/>
<PackageReference Include="libmetar" Version="*"/>
</ItemGroup>
</Project>

32
Metar/Metar.cs Normal file
View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using libmetar.Services;
namespace EFB.Metar
{
public static class Metar
{
private static MetarService metarService { get; set; } = new MetarService();
private static TafService tafService { get; set; } = new TafService();
public static async Task<string> GetMETAR(string ICAO){
return (await metarService.GetRawAsync(ICAO)).Raw;
}
public static async Task<List<string>> GetTAF(string ICAO){
var downloadedTAF = (await tafService.GetRawAsync(ICAO)).RawSplit;
List<string> TAF = new List<string>();
foreach (var line in downloadedTAF)
{
TAF.Add(line);
}
return TAF;
}
}
}

View File

@ -1,13 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using System.Text;
using System.Net.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using EFB.Models;
using EFB.Models.JSON;
using EFB.Sessions;
using EFB.Controllers.Form;
using EFB.Controllers.API;
namespace EFB.Models
{
public class ChartModel
{
public string ICAO { get; set; }
public Chart[] General { get; set; }
public Chart[] TextualData { get; set; }
public Chart[] GroundLayout { get; set; }
@ -17,8 +28,13 @@ namespace EFB.Models
public Chart[] Transition { get; set; }
public Chart[] PilotBriefing { get; set; }
public ChartModel(ChartList response)
[JsonConstructor]
public ChartModel(){
//Empty constructor for JSON Serialisation Purposes
}
public ChartModel(string ICAO, ChartList response)
{
this.ICAO = ICAO;
General = FillChart(response.General);
TextualData = FillChart(response.TextualData);
GroundLayout = FillChart(response.GroundLayout);
@ -36,6 +52,30 @@ namespace EFB.Models
}
return new Chart[]{};
}
public static async Task<ChartList> FetchAsync(string ICAO){
Console.WriteLine("Start");
if (FormAuthenticator.ValidateICAOCode(ICAO))
{
APIInterface API = new APIInterface();
Dictionary <string, string> headerData = new Dictionary<string, string>();
headerData.Add("referer", "luke-else.co.uk");
Dictionary<string, string> formData = new Dictionary<string, string>();
formData.Add("token", Environment.GetEnvironmentVariable("ChartFoxAPIKey"));
FormUrlEncodedContent body = new FormUrlEncodedContent(formData);
//make Charts request
var requestCharts = await API.Post<ChartResponse>($"https://chartfox.org/api/charts/grouped/{ICAO}", headerData, body);
Console.WriteLine("End");
if (requestCharts.Result.Status == "success")
{
return requestCharts.Result.Response;
}
}
return null;
}
}
public class Pinned

19
Models/FlightsimModel.cs Normal file
View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EFB.Models.Route;
namespace EFB.Models
{
public class FlightsimModel
{
public SimPositionModel CurrentPosition { get; set; }
public IWaypoint Closest { get; set; }
public FlightsimModel(SimPositionModel position, IWaypoint closest)
{
CurrentPosition = position;
Closest = closest;
}
}
}

View File

@ -39,7 +39,8 @@ namespace EFB.Models
}
public static async Task<NavdataModel[]> Populate(){
MySqlConnection con = new MySqlConnection("server=server.luke-else.co.uk;userid=root;password=;database=EFB");
string password = Environment.GetEnvironmentVariable("MySQLPassword");
MySqlConnection con = new MySqlConnection($"server=server.luke-else.co.uk;userid=root;password={password};database=EFB");
con.Open();
// Console.WriteLine($"MySQL version : {con.ServerVersion}");
@ -53,11 +54,11 @@ namespace EFB.Models
while (reader.Read())
{
int id = reader.GetInt32(0);
string name = reader.GetString(1);
string type = reader.GetString(2);
string latitude = reader.GetString(4);
string longitude = reader.GetString(5);
int id = reader.GetInt32("id");
string name = reader.GetString("name");
string type = reader.GetString("type");
string latitude = reader.GetString("latitude");
string longitude = reader.GetString("longitude");
if (type == "VOR" || type == "NDB")
{

View File

@ -8,8 +8,8 @@ namespace EFB.Models.Route
public interface IWaypoint
{
public string Name { get; set; }
public string Longitude { get; set; }
public string Latitude { get; set; }
public float Longitude { get; set; }
public float Latitude { get; set; }
public string Airway { get; set; }
public IWaypoint Next { get; set; }

View File

@ -8,8 +8,8 @@ namespace EFB.Models.Route
public class NavaidModel:IWaypoint
{
public string Name { get; set; }
public string Longitude { get; set; }
public string Latitude { get; set; }
public float Longitude { get; set; }
public float Latitude { get; set; }
public int Frequency { get; set; }
public string Airway { get; set; }
@ -17,9 +17,11 @@ namespace EFB.Models.Route
public IWaypoint Previous { get; set; } = null;
public bool Visited { get; set; } = false;
public NavaidModel(string name, string airway){
public NavaidModel(string name, string airway, float longitude, float latitude){
Name = name;
Airway = airway;
Longitude = longitude;
Latitude = latitude;
}
}
}

View File

@ -8,17 +8,19 @@ namespace EFB.Models.Route
public class WaypointModel:IWaypoint
{
public string Name { get; set; }
public string Longitude { get; set; }
public string Latitude { get; set; }
public float Longitude { get; set; }
public float Latitude { get; set; }
public string Airway { get; set; }
public IWaypoint Next { get; set; } = null;
public IWaypoint Previous { get; set; } = null;
public bool Visited { get; set; }
public WaypointModel(string name, string airway){
public WaypointModel(string name, string airway, float longitude, float latitude){
Name = name;
Airway = airway;
Longitude = longitude;
Latitude = latitude;
}
}

View File

@ -22,12 +22,12 @@ namespace EFB.Models
public RouteModel(string departure, string departureRoute, string arrival, string arrivalRoute, uint cruise){
if (FormAuthenticator.ValidateICAOCode(departure))
{
Departure = new WaypointModel(departure, departureRoute);
Departure = new WaypointModel(departure, departureRoute, 0, 0);
}
if (FormAuthenticator.ValidateICAOCode(arrival))
{
Arrival = new WaypointModel(arrival, arrivalRoute);
Arrival = new WaypointModel(arrival, arrivalRoute, 0, 0);
}
if (FormAuthenticator.ValidateCruiseAlt(cruise))
@ -53,7 +53,8 @@ namespace EFB.Models
}
//Generate a route Object
public static RouteModel StringToRoute(string departure, string arrival, uint cruise, string routeString){
public static async Task<RouteModel> StringToRoute(string departure, string arrival, uint cruise, string routeString){
var navdataFetch = NavdataModel.Populate();
string[] routeTemp = routeString.Split(" ");
//Set departure and arrival route
@ -65,17 +66,34 @@ namespace EFB.Models
route.Current = route.Departure;
NavdataModel[] navdata = await navdataFetch;
navdata = NavdataModel.MergeSort(ref navdata, 0, navdata.Length - 1);
for (var i = 1; i < routeTemp.Length-1; i+=2)
{//Already used first item, continue itterating over every other item
IWaypoint next;
NavdataModel currentWaypoint = NavdataModel.BinarySearch(ref navdata, 0, navdata.Length-1, routeTemp[i]);
if (currentWaypoint == null)
{
currentWaypoint = new NavdataModel(0, routeTemp[i], null, "0", "0");
}
//Populate 'next' waypoint
if (routeTemp[i].Length > 3)
{//waypoint Type
next = new WaypointModel(routeTemp[i], routeTemp[i+1]);
next = new WaypointModel(
routeTemp[i],
routeTemp[i+1],
float.Parse(currentWaypoint.Longitude),
float.Parse(currentWaypoint.Latitude)
);
}else
{//Navaid Type
next = new NavaidModel(routeTemp[i], routeTemp[i+1]);
next = new NavaidModel(
routeTemp[i],
routeTemp[i+1],
float.Parse(currentWaypoint.Longitude),
float.Parse(currentWaypoint.Latitude)
);
}
next.Previous = route.Current;
@ -90,6 +108,15 @@ namespace EFB.Models
route.Current = route.Departure;
//Assign departure and arrival coordinate positions
NavdataModel departureNav = NavdataModel.BinarySearch(ref navdata, 0, navdata.Length - 1, departure);
NavdataModel arrivalNav = NavdataModel.BinarySearch(ref navdata, 0, navdata.Length - 1, arrival);
route.Departure.Latitude = float.Parse(departureNav.Latitude);
route.Departure.Longitude = float.Parse(departureNav.Longitude);
route.Arrival.Latitude = float.Parse(arrivalNav.Latitude);
route.Arrival.Latitude = float.Parse(arrivalNav.Longitude);
return route;
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;
using MongoDB.Driver;
namespace EFB.Models
{
public class SimPositionModel
{
[BsonId]
public ObjectId Id { get; set; }
public string EMail { get; set; } = "";
public DateTime LatestPacketUpdate { get; set; }
public SimPosition LatestPosition { get; set; } = null;
public SimPositionModel(string email, SimPosition position){
EMail = email;
LatestPacketUpdate = DateTime.Now;
LatestPosition = position;
}
}
public class SimPosition
{
public float Latitude { get; set; }
public float Longitude { get; set; }
public int Altitude { get; set; }
public SimPosition(float latitude, float longitude, int altitude){
Latitude = latitude;
Longitude = longitude;
Altitude = altitude;
}
//**Packet Processing not required**//
// public SimPosition(Packet[] data){
// if (data[0].Data != null)
// {
// //Use Linq to search through the packets for a given id and use that data
// Latitude = (data.Where(x => x.Id == 22).Select(x => x.Data[0]).ToArray())[0];
// Longitude = (data.Where(x => x.Id == 23).Select(x => x.Data[0]).ToArray())[0];
// Altitude = Convert.ToInt32((data.Where(x => x.Id == 24).Select(x => x.Data[0]).ToArray())[0]);
// }
// }
}
}

View File

@ -21,12 +21,18 @@ namespace EFB.Models
public TokenModel UserToken { get; set; } = null;
//Contains the most recent route generated by the user through the App
public string Departure { get; set; }
public string Route { get; set; }
public string Arrival { get; set; }
public uint Cruise { get; set; }
public TokenModel RouteToken { get; set; } = null;
//Contains the Departure and Arrival Charts for the user's route
public ChartModel DepartureCharts { get; set; }
public ChartModel ArrivalCharts { get; set; }
public ChartModel CurrentCharts { get; set; }
//Contains the most recently stored position of the user in the simulator
public object SimPosition { get; set; } = null;

28
Models/ViewChartModel.cs Normal file
View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Razor;
using Newtonsoft.Json;
using EFB.Models.JSON;
using Microsoft.AspNetCore;
using EFB.Sessions;
namespace EFB.Models
{
public class ViewChartModel
{
public ChartModel Charts { get; set; }
public Chart Selected { get; set; }
public ViewChartModel(ChartModel current, string selected){
Charts = current;
if (selected != null)
{
Selected = JsonConvert.DeserializeObject<Chart>(selected);
}
}
}
}

30
Mongo/Mongo.cs Normal file
View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Bson;
using EFB.Models;
namespace EFB.MongoData
{
public class Mongo
{
//function that is responsible to getting the user's latest sim position from the MongoDB
public static async Task<SimPositionModel> GetLatestData(string email){
MongoClient client = new MongoClient(
Environment.GetEnvironmentVariable("MongoDBConnectionString")
);
MongoDatabaseBase database = (MongoDatabaseBase)client.GetDatabase("EFB");
MongoCollectionBase<SimPositionModel> collection = (MongoCollectionBase<SimPositionModel>)database.GetCollection<SimPositionModel>("Simdata");
FilterDefinition<SimPositionModel> filter = Builders<SimPositionModel>.Filter.Eq(x => x.EMail, email);
var data = await collection.FindAsync<SimPositionModel>(filter).Result.ToListAsync();
if (data.Count > 0)
{
return data[0];
}
return null;
}
}
}

View File

@ -54,6 +54,12 @@ namespace EFB
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// endpoints.MapControllerRoute(
// name: "Navdata",
// pattern: "{controller=Navdata}/{action=Index}/{identifier?}");
// endpoints.MapControllerRoute(
// name: "Charts",
// pattern: "{controller=Charts}/{action=Index}/{ICAO?}");
});
}
}

48
Views/App/Index.cshtml Normal file
View File

@ -0,0 +1,48 @@
@using EFB.Metar;
@model EFB.Models.UserModel;
@{
ViewData["Title"] = "Welcome";
}
<div class="row d-flex">
<div class="card-body col-md-12 bg-primary">
<div class="container jumbotron">
<h3>Current Session - @Model.EMail</h3>
<br />
<br />
<div class="row d-flex">
<div class="col-md-6">
<h4>Departure</h4>
@Model.Departure
<br />
@await Metar.GetMETAR(Model.Departure)
<h4>Cruise</h4>
@Model.Cruise ft
</div>
<div class="col-md-6">
<h4>Arrival</h4>
@Model.Arrival
<br />
@await Metar.GetMETAR(Model.Arrival)
</div>
</div>
<br />
<h4>Route</h4>
@Model.Route
</div>
</div>
</div>

37
Views/Charts/Index.cshtml Normal file
View File

@ -0,0 +1,37 @@
@{
ViewData["Title"] = "Welcome";
}
<div class="row d-flex justify-content-center">
<div class="card-body col-md-6">
<div class="container jumbotron">
<h3>Chart Lookup</h3>
<br />
<br />
<form asp-controller="Charts" asp-action="Index">
<div class="form-group">
<input type="text" class="form-control" placeholder="ICAO Code" name="ICAO" value="@TempData["ICAO"]">
</div>
<button type="submit" class="btn btn-secondary">Search</button>
@{
if (TempData["Error"] != null)
{//If an error has been flagged, information will be displayed to the user
<br />
<br />
<div class="alert alert-danger">
<strong>Warning!</strong> @TempData["Error"] <button type='button' class='close' data-dismiss='alert' aria-hidden='true' />&times;
</div>
}
}
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,109 @@
@using Newtonsoft.Json;
@using EFB.Metar;
@model EFB.Models.ViewChartModel;
@{
ViewData["Title"] = "Welcome";
}
<div class="row d-flex justify-content-center">
<div class="card-body col-md-4">
<div class="container jumbotron">
<form asp-controller="Charts" asp-action="Index">
<div class="form-group">
<input type="text" class="form-control" placeholder="ICAO Code" name="ICAO" value="@TempData["ICAO"]">
</div>
<button type="submit" class="btn btn-secondary">Search</button>
@{
if (TempData["Error"] != null)
{//If an error has been flagged, information will be displayed to the user
<br />
<br />
<div class="alert alert-danger">
<strong>Warning!</strong> @TempData["Error"] <button type='button' class='close' data-dismiss='alert' aria-hidden='true' />&times;
</div>
}
}
</form>
@{
<br />
<h4>Charts for: @Model.Charts.ICAO</h4>
<form asp-action="ViewCharts" method="post">
<div class="form-group">
<label>Select Charts</label><br />
<select name="chart" class="form-control">
@if (Model.Charts != null) {
<option value=""></option>
foreach(var item in Model.Charts.GroundLayout) {
var itemString = JsonConvert.SerializeObject(item);
<option value="@itemString">@item.Name</option>
}
<option value=""></option>
foreach(var item in Model.Charts.SID) {
var itemString = JsonConvert.SerializeObject(item);
<option value="@itemString">@item.Name</option>
}
<option value=""></option>
foreach(var item in Model.Charts.STAR) {
var itemString = JsonConvert.SerializeObject(item);
<option value="@itemString">@item.Name</option>
}
<option value=""></option>
foreach(var item in Model.Charts.Approach) {
var itemString = JsonConvert.SerializeObject(item);
<option value="@itemString">@item.Name</option>
}
<option value=""></option>
foreach(var item in Model.Charts.TextualData) {
var itemString = JsonConvert.SerializeObject(item);
<option value="@itemString">@item.Name</option>
}
}
</select>
<button type="submit" class="btn btn-primary">View</button>
</div>
</form>
}
<br />
<h4>Current Weather</h4>
@await Metar.GetMETAR(@Model.Charts.ICAO);
</div>
</div>
<div class="card-body col-md-8 vh-80">
<div class="container jumbotron vh-100">
@{
if (Model.Selected != null)
{
<h3>@Model.Selected.Name</h3>
<br />
<br />
<iframe src="@Model.Selected.URL" width="100%" height="90%"></iframe>
}
}
</div>
</div>
</div>

View File

@ -0,0 +1,53 @@
@model EFB.Models.FlightsimModel;
@{
ViewData["Title"] = "Welcome";
}
<div class="row d-flex">
<div class="card-body col-md-6 bg-primary">
<div class="container jumbotron">
<h3>Current Position</h3>
<br />
<br />
<h5 style="color: gray;">Last Updated at: @Model.CurrentPosition.LatestPacketUpdate.ToString()</h5>
<div class="row d-flex">
<div class="col-md-6">
<h4>Latitude</h4>
@Model.CurrentPosition.LatestPosition.Latitude
<h4>Longitude</h4>
@Model.CurrentPosition.LatestPosition.Longitude
</div>
<div class="col-md-6">
<h4>Altitude</h4>
@Model.CurrentPosition.LatestPosition.Altitude ft
</div>
</div>
</div>
</div>
<div class="card-body col-md-6 bg-success">
<div class="container jumbotron">
<h3>Closest Waypoint</h3>
<br />
<br />
<h4>@Model.Closest.Name -> @Model.Closest.Airway</h4>
<h4>Latitude</h4>
@Model.Closest.Latitude
<h4>Longitude</h4>
@Model.Closest.Longitude
</div>
</div>
</div>

View File

@ -32,10 +32,10 @@
<a class="nav-link text-light" asp-area="" asp-controller="Navdata" asp-action="Index">Navdata</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" asp-area="" asp-controller="" asp-action="Index">Charts</a>
<a class="nav-link text-light" asp-area="" asp-controller="Charts" asp-action="Index">Charts</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" asp-area="" asp-controller="" asp-action="Index">FlightSim</a>
<a class="nav-link text-light" asp-area="" asp-controller="Flightsim" asp-action="Index">FlightSim</a>
</li>
@{
@ -44,7 +44,7 @@
{
<div class="ml-auto">
<li class="nav-item">
<a class="nav-link text-light" asp-area="" asp-controller="User" asp-action="Logout">Logout (@user.UserToken.TokenValue)</a>
<a class="nav-link text-light" asp-area="" asp-controller="User" asp-action="Logout">Logout (@user.EMail)</a>
</li>
</div>
}

16
docker-compose.debug.yml Normal file
View File

@ -0,0 +1,16 @@
# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
version: '3.4'
services:
efb:
image: efb
build:
context: .
dockerfile: ./Dockerfile
ports:
- 5000:5000
environment:
- ASPNETCORE_ENVIRONMENT=Development
volumes:
- ~/.vsdbg:/remote_debugger:rw

16
docker-compose.yml Normal file
View File

@ -0,0 +1,16 @@
# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
version: '3.4'
services:
efb:
image: efb
build:
context: .
dockerfile: ./Dockerfile
ports:
- 80:5000
environment:
- MongoDBConnectionString=
- MySQLPassword=
- ChartFoxAPIKey=