Save PIREP route to ACARS data table #102

This commit is contained in:
Nabeel Shahzad 2018-01-01 13:48:02 -06:00
parent 216d686cc7
commit b456dc1a71
16 changed files with 258 additions and 43 deletions

View File

@ -79,7 +79,7 @@ class AcarsReplay extends BaseCommand
'aircraft_id' => 1,
'dpt_airport_id' => $flight->planned_depairport,
'arr_airport_id' => $flight->planned_destairport,
'altitude' => $flight->planned_altitude,
'level' => $flight->planned_altitude,
'planned_flight_time' => $pft,
'route' => $flight->planned_route,
]

View File

@ -0,0 +1,15 @@
<?php
use Faker\Generator as Faker;
use \App\Models\Enums\NavaidType;
$factory->define(App\Models\Navdata::class, function (Faker $faker) {
return [
'id' => substr($faker->unique()->word, 0, 5),
'name' => $faker->unique()->text(10),
'type' => $faker->randomElement([NavaidType::VOR, NavaidType::NDB]),
'lat' => $faker->latitude,
'lon' => $faker->longitude,
'freq' => $faker->randomFloat(2, 100, 1000),
];
});

View File

@ -1,15 +0,0 @@
<?php
use Faker\Generator as Faker;
$factory->define(App\Models\Navpoint::class, function (Faker $faker) {
return [
'id' => $faker->unique()->numberBetween(10, 100000),
'name' => $faker->unique()->text(10),
'title' => $faker->unique()->text(25),
'airway' => $faker->unique()->text(7),
'lat' => $faker->latitude,
'lon' => $faker->longitude,
'freq' => $faker->randomFloat(2, 100, 1000),
];
});

View File

@ -28,6 +28,7 @@ class CreatePirepTables extends Migration
$table->string('dpt_airport_id', 5);
$table->string('arr_airport_id', 5);
$table->unsignedInteger('altitude')->nullable();
$table->unsignedInteger('level')->nullable();
$table->unsignedDecimal('flight_time', 19)->nullable();
$table->unsignedDecimal('planned_flight_time', 19)->nullable();
$table->unsignedDecimal('gross_weight', 19)->nullable();

View File

@ -18,7 +18,7 @@ class CreateNavdataTables extends Migration
* https://github.com/skiselkov/openfmc/blob/master/airac.h
*/
Schema::create('navdata', function (Blueprint $table) {
$table->string('id', 4);
$table->string('id', 5);
$table->string('name', 24);
$table->unsignedInteger('type');
$table->float('lat', 7, 4)->default(0.0);

View File

@ -17,6 +17,8 @@ class CreateAcarsTables extends Migration
$table->string('id', 12);
$table->string('pirep_id', 12);
$table->unsignedTinyInteger('type');
$table->unsignedInteger('nav_type')->nullable();
$table->string('name')->nullable();
$table->string('log')->nullable();
$table->float('lat', 7, 4)->default(0.0);
$table->float('lon', 7, 4)->default(0.0);

View File

@ -280,6 +280,7 @@ pireps:
user_id: 1
airline_id: 1
flight_id: flightid_1
flight_number: 100
aircraft_id: 1
dpt_airport_id: KAUS
arr_airport_id: KJFK
@ -293,6 +294,7 @@ pireps:
user_id: 1
airline_id: 1
flight_id: flightid_2
flight_number: 101
aircraft_id: 1
dpt_airport_id: KJFK
arr_airport_id: KAUS
@ -306,6 +308,7 @@ pireps:
user_id: 1
airline_id: 1
flight_id: flightid_2
flight_number: 101
aircraft_id: 1
dpt_airport_id: KJFK
arr_airport_id: KAUS

View File

@ -179,7 +179,15 @@ class PirepController extends BaseController
return redirect(route('admin.pireps.index'));
}
$pirep = $this->pirepRepo->update($request->all(), $id);
$attrs = $request->all();
$orig_route = $pirep->route;
$pirep = $this->pirepRepo->update($attrs, $id);
// A route change in the PIREP, so update the saved points
// in the ACARS table
if($pirep->route !== $orig_route) {
$this->pirepSvc->saveRoute($pirep);
}
Flash::success('Pirep updated successfully.');
return redirect(route('admin.pireps.index'));

View File

@ -69,7 +69,7 @@ class PirepController extends AppBaseController
'route_code',
'flight_time',
'planned_flight_time',
'altitude',
'level',
'route',
'notes',
];
@ -123,7 +123,7 @@ class PirepController extends AppBaseController
'route_code',
'flight_time',
'planned_flight_time',
'altitude',
'level',
'route',
'notes',
];

View File

@ -14,6 +14,8 @@ class Acars extends BaseModel
public $fillable = [
'pirep_id',
'type',
'nav_type',
'name',
'log',
'lat',
'lon',
@ -29,6 +31,7 @@ class Acars extends BaseModel
public $casts = [
'type' => 'integer',
'nav_type' => 'integer',
'lat' => 'float',
'lon' => 'float',
'heading' => 'integer',

View File

@ -18,9 +18,23 @@ class Navdata extends BaseModel
];
public $casts = [
'id' => 'string',
'type' => 'integer',
'lat' => 'float',
'lon' => 'float',
'freq' => 'float',
];
protected static function boot()
{
parent::boot();
/**
* Make sure the ID is all caps
*/
static::creating(function (Navdata $model) {
if (!empty($model->id)) {
$model->id = strtoupper($model->id);
}
});
}
}

View File

@ -61,6 +61,8 @@ class Pirep extends BaseModel
'flight_number' => 'required',
'dpt_airport_id' => 'required',
'arr_airport_id' => 'required',
'notes' => 'nullable',
'route' => 'nullable',
];
/**
@ -70,10 +72,12 @@ class Pirep extends BaseModel
public function getIdentAttribute()
{
$flight_id = $this->airline->code;
if(!empty($this->flight_number)) {
$flight_id .= $this->flight_number;
} else {
if ($this->flight_id) {
$flight_id .= $this->flight->flight_number;
} else {
$flight_id .= $this->flight_number;
}
}
return $flight_id;

View File

@ -7,19 +7,27 @@ use Hashids\Hashids;
trait HashId
{
/**
* @return string
* @throws \Hashids\HashidsException
*/
protected static function createNewHashId()
{
$hashids = new Hashids('', 12);
$mt = str_replace('.', '', microtime(true));
return $hashids->encode($mt);
}
/**
* Register callbacks
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model)
{
static::creating(function ($model) {
$key = $model->getKeyName();
if (empty($model->{$key})) {
$hashids = new Hashids('', 12);
$mt = str_replace('.', '', microtime(true));
$id = $hashids->encode($mt);
$id = static::createNewHashId();
$model->{$key} = $id;
}
});

View File

@ -3,6 +3,9 @@
namespace App\Services;
use App\Models\Acars;
use App\Models\Airport;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Collection;
use Log;
use \GeoJson\Geometry\Point;
@ -34,6 +37,64 @@ class GeoService extends BaseService
$this->navRepo = $navRepo;
}
/**
* Parse a route string into a collection of Navadata points
* TODO: Add the distance calculation in here
* @param $route
* @param Airport|null $dep
* @param Airport|null $arr
* @return array|Collection
*/
public function routeToNavPoints($route, Airport $dep=null, Airport $arr=null)
{
$route = trim($route);
if(empty($route)) {
return collect();
}
$skip_points = ['SID', 'STAR'];
if($dep !== null) {
$skip_points[] = $dep->icao;
}
if($arr !== null) {
$skip_points[] = $arr->icao;
}
# Iterate through the route
$route = collect(explode(' ', $route))
->transform(function($point) use ($skip_points) {
$point = trim($point);
if(empty($point)) {
return false;
}
if(\in_array($point, $skip_points, true)) {
return false;
}
try {
$navpoints = $this->navRepo->findWhere(['id'=>$point]);
} catch(ModelNotFoundException $e) {
return false;
}
if($navpoints->count() === 0) {
return false;
} elseif ($navpoints->count() === 1) {
return $navpoints[0];
}
# find the closest waypoint...
})
->filter(function($value, $key) {
return !empty($value);
});
return $route;
}
/**
* Determine the closest set of coordinates from the starting position
* @param array $coordStart

View File

@ -2,37 +2,94 @@
namespace App\Services;
use App\Models\Enums\PirepSource;
use App\Models\Enums\PirepState;
use Log;
use App\Models\Acars;
use App\Models\Navdata;
use App\Models\Pirep;
use App\Models\PirepFieldValues;
use App\Models\User;
use App\Models\Enums\AcarsType;
use App\Models\Enums\PirepSource;
use App\Models\Enums\PirepState;
use App\Events\PirepAccepted;
use App\Events\PirepFiled;
use App\Events\PirepRejected;
use App\Events\UserStatsChanged;
use App\Models\User;
use App\Repositories\NavdataRepository;
use App\Repositories\PirepRepository;
use Log;
class PIREPService extends BaseService
{
protected $pilotSvc, $pirepRepo;
protected $geoSvc,
$navRepo,
$pilotSvc,
$pirepRepo;
/**
* PIREPService constructor.
* @param UserService $pilotSvc
* @param GeoService $geoSvc
* @param NavdataRepository $navRepo
* @param PirepRepository $pirepRepo
*/
public function __construct(
UserService $pilotSvc,
GeoService $geoSvc,
NavdataRepository $navRepo,
PirepRepository $pirepRepo
) {
$this->geoSvc = $geoSvc;
$this->pilotSvc = $pilotSvc;
$this->navRepo = $navRepo;
$this->pirepRepo = $pirepRepo;
}
/**
* Save the route into the ACARS table with AcarsType::ROUTE
* @param Pirep $pirep
* @return Pirep
*/
public function saveRoute(Pirep $pirep): Pirep
{
# Delete all the existing nav points
Acars::where([
'pirep_id' => $pirep->id,
'type' => AcarsType::ROUTE,
])->delete();
# Delete the route
if(empty($pirep->route)) {
return $pirep;
}
$route = $this->geoSvc->routeToNavPoints(
$pirep->route,
$pirep->dep_airport,
$pirep->arr_airport
);
/**
* @var $point Navdata
*/
foreach($route as $point) {
$acars = new Acars();
$acars->pirep_id = $pirep->id;
$acars->type = AcarsType::ROUTE;
$acars->nav_type = $point->type;
$acars->name = $point->id;
$acars->lat = $point->lat;
$acars->lon = $point->lon;
$acars->save();
}
return $pirep;
}
/**
* Create a new PIREP with some given fields
*
@ -43,7 +100,7 @@ class PIREPService extends BaseService
*/
public function create(Pirep $pirep, array $field_values=[]): Pirep
{
if($field_values === null) {
if(empty($field_values)) {
$field_values = [];
}
@ -60,6 +117,9 @@ class PIREPService extends BaseService
}
}
# Save the PIREP route
$pirep = $this->saveRoute($pirep);
$pirep->save();
$pirep->refresh();
@ -73,7 +133,6 @@ class PIREPService extends BaseService
}
Log::info('New PIREP filed', [$pirep]);
event(new PirepFiled($pirep));
# only update the pilot last state if they are accepted

View File

@ -1,8 +1,11 @@
<?php
use App\Models\Enums\PirepState;
use App\Models\User;
use App\Models\Acars;
use App\Models\Enums\AcarsType;
use App\Models\Navdata;
use App\Models\Pirep;
use App\Models\User;
use App\Models\Enums\PirepState;
use Illuminate\Foundation\Testing\WithoutMiddleware;
@ -20,11 +23,40 @@ class PIREPTest extends TestCase
$this->pirepSvc = app('App\Services\PIREPService');
}
protected function createNewRoute()
{
$route = [];
$navpoints = factory(App\Models\Navdata::class, 5)->create();
foreach ($navpoints as $point) {
$route[] = $point->id;
}
return $route;
}
protected function getAcarsRoute($pirep)
{
$saved_route = [];
$route_points = Acars::where(
['pirep_id' => $pirep->id, 'type' => AcarsType::ROUTE]
)->orderBy('created_at', 'asc')->get();
foreach ($route_points as $point) {
$saved_route[] = $point->name;
}
return $saved_route;
}
/**
*/
public function testAddPirep()
{
$pirep = factory(App\Models\Pirep::class)->create();
$route = $this->createNewRoute();
$pirep = factory(App\Models\Pirep::class)->create([
'route' => implode(' ', $route)
]);
$pirep = $this->pirepSvc->create($pirep, []);
/**
@ -54,6 +86,26 @@ class PIREPTest extends TestCase
$this->assertEquals($new_pirep_count, $pirep->pilot->flights);
$this->assertEquals($new_flight_time, $pirep->pilot->flight_time);
$this->assertEquals($pirep->arr_airport_id, $pirep->pilot->curr_airport_id);
/**
* Check the ACARS table
*/
$saved_route = $this->getAcarsRoute($pirep);
$this->assertEquals($route, $saved_route);
/**
* Recreate the route with new options points. Make sure that the
* old route is erased from the ACARS table and then recreated
*/
$route = $this->createNewRoute();
$pirep->route = implode(' ', $route);
$pirep->save();
# this should delete the old route from the acars table
$this->pirepSvc->saveRoute($pirep);
$saved_route = $this->getAcarsRoute($pirep);
$this->assertEquals($route, $saved_route);
}
/**