Save PIREP route to ACARS data table #102
This commit is contained in:
parent
216d686cc7
commit
b456dc1a71
@ -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,
|
||||
]
|
||||
|
15
app/Database/factories/NavdataFactory.php
Normal file
15
app/Database/factories/NavdataFactory.php
Normal 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),
|
||||
];
|
||||
});
|
@ -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),
|
||||
];
|
||||
});
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -24,7 +24,7 @@ class PirepController extends BaseController
|
||||
$airlineRepo,
|
||||
$pirepRepo,
|
||||
$aircraftRepo,
|
||||
$pirepSvc;
|
||||
$pirepSvc;
|
||||
|
||||
public function __construct(
|
||||
AirportRepository $airportRepo,
|
||||
@ -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'));
|
||||
|
@ -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',
|
||||
];
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 ($this->flight_id) {
|
||||
$flight_id .= $this->flight->flight_number;
|
||||
} else {
|
||||
if(!empty($this->flight_number)) {
|
||||
$flight_id .= $this->flight_number;
|
||||
} else {
|
||||
if ($this->flight_id) {
|
||||
$flight_id .= $this->flight->flight_number;
|
||||
}
|
||||
}
|
||||
|
||||
return $flight_id;
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user