diff --git a/app/Http/Controllers/ClxMessagesController.php b/app/Http/Controllers/ClxMessagesController.php index 840bea7..f197eca 100644 --- a/app/Http/Controllers/ClxMessagesController.php +++ b/app/Http/Controllers/ClxMessagesController.php @@ -8,6 +8,7 @@ use App\Events\ClxIssuedEvent; use App\Http\Requests\ClxMessageRequest; use App\Models\ClxMessage; +use App\Models\DatalinkAuthority; use App\Models\RclMessage; use App\Models\Track; use App\Services\CpdlcService; @@ -99,9 +100,9 @@ public function showRclMessage(RclMessage $rclMessage) return view('controllers.clx.rcl-messages.show', [ 'message' => $rclMessage, - 'dlAuthorities' => DatalinkAuthorities::cases(), + 'dlAuthorities' => DatalinkAuthority::notSystem()->get(), 'tracks' => $rclMessage->is_concorde ? Track::concorde()->get() : Track::active()->get(), - 'activeDlAuthority' => $this->dataService->getActiveControllerAuthority(Auth::user()) ?? DatalinkAuthorities::NAT, + 'activeDlAuthority' => $this->dataService->getActiveControllerAuthority(Auth::user()) ?? DatalinkAuthority::find('NAT'), '_pageTitle' => $rclMessage->callsign, ]); } @@ -160,7 +161,7 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request) 'entry_time_restriction' => $entryRequirement ?? null, 'raw_entry_time_restriction' => $request->get('entry_time_requirement'), 'free_text' => $isReclearance ? '** RECLEARANCE '.now()->format('Hi').' ** '.$request->get('free_text') : $request->get('free_text'), - 'datalink_authority' => DatalinkAuthorities::from($request->get('datalink_authority')), + 'datalink_authority_id' => $request->get('datalink_authority_id'), 'is_concorde' => $rclMessage->is_concorde, ]); @@ -179,7 +180,7 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request) * Create datalink messages */ $array = [ - 'CLX '.now()->format('Hi dmy').' '.$clxMessage->datalink_authority->name.' CLRNCE '.$clxMessage->id, + 'CLX '.now()->format('Hi dmy').' '.$clxMessage->datalinkAuthority->id.' CLRNCE '.$clxMessage->id, $rclMessage->callsign.' CLRD TO '.$rclMessage->destination.' VIA '.$clxMessage->entry_fix, $clxMessage->track ? 'NAT '.$clxMessage->track->identifier : 'RANDOM ROUTE', $clxMessage->track ? $clxMessage->track->last_routeing : $clxMessage->random_routeing, @@ -213,9 +214,9 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request) $clxMessage->datalink_message = $array; $msg = ''; if ($clxMessage->track) { - $msg = "{$clxMessage->datalink_authority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, track {$clxMessage->track->identifier}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; + $msg = "{$clxMessage->datalinkAuthority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, track {$clxMessage->track->identifier}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; } else { - $msg = "{$clxMessage->datalink_authority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, random routeing {$clxMessage->random_routeing}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; + $msg = "{$clxMessage->datalinkAuthority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, random routeing {$clxMessage->random_routeing}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; } // Only show crossing restriction if entry time =/= the restriction due to the bodge if ($clxMessage->entry_time_restriction && ($clxMessage->raw_entry_time_restriction != $rclMessage->entry_time)) { @@ -259,7 +260,7 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request) ->causedBy($clxMessage->vatsimAccount) ->performedOn($rclMessage) ->withProperties(['datalink' => $clxMessage->data_link_message]) - ->log('CLX Message Transmitted By '.$clxMessage->datalink_authority->name); + ->log('CLX Message Transmitted By '.$clxMessage->datalinkAuthority->id); flashAlert(type: 'success', title: null, message: 'Clearance transmitted.', toast: true, timer: true); @@ -280,7 +281,7 @@ public function deleteRclMessage(Request $request, RclMessage $rclMessage) public function revertToVoice(Request $request, RclMessage $rclMessage) { - $datalinkAuthority = $this->dataService->getActiveControllerAuthority ?? DatalinkAuthorities::NAT; + $datalinkAuthority = $this->dataService->getActiveControllerAuthority ?? DatalinkAuthority::find('NAT'); $this->cpdlcService->sendMessage( author: $datalinkAuthority, recipient: $rclMessage->callsign, @@ -296,14 +297,14 @@ public function revertToVoice(Request $request, RclMessage $rclMessage) public function moveToProcessed(Request $request, RclMessage $rclMessage) { - $datalinkAuthority = $this->dataService->getActiveControllerAuthority ?? DatalinkAuthorities::NAT; - $this->cpdlcService->sendMessage( - author: $datalinkAuthority, - recipient: $rclMessage->callsign, - recipientAccount: $rclMessage->vatsimAccount, - message: sprintf(RclResponsesEnum::AcknowledgeMoved->value, strtoupper($datalinkAuthority->description())), - caption: RclResponsesEnum::Acknowledge->text() - ); + $datalinkAuthority = $this->dataService->getActiveControllerAuthority ?? DatalinkAuthority::find('NAT'); +// $this->cpdlcService->sendMessage( +// author: $datalinkAuthority, +// recipient: $rclMessage->callsign, +// recipientAccount: $rclMessage->vatsimAccount, +// message: sprintf(RclResponsesEnum::AcknowledgeMoved->value, strtoupper($datalinkAuthority->description())), +// caption: RclResponsesEnum::Acknowledge->text() +// ); $clxMessage = new ClxMessage([ 'vatsim_account_id' => $request->user()->id, @@ -349,7 +350,7 @@ public function moveToProcessed(Request $request, RclMessage $rclMessage) ->causedBy($clxMessage->vatsimAccount) ->performedOn($rclMessage) ->withProperties(['datalink' => $clxMessage->data_link_message]) - ->log('CLX Message Transmitted By '.$clxMessage->datalink_authority->name); + ->log('CLX Message Transmitted By '.$clxMessage->datalinkAuthority->name); flashAlert(type: 'success', title: null, message: 'Clearance moved.', toast: true, timer: true); @@ -361,7 +362,7 @@ public function create() return view('controllers.clx.rcl-messages.create', [ 'dlAuthorities' => DatalinkAuthorities::cases(), 'tracks' => Track::where('active', true)->orWhere('concorde', true)->get(), - 'activeDlAuthority' => $this->dataService->getActiveControllerAuthority(Auth::user()) ?? DatalinkAuthorities::NAT, + 'activeDlAuthority' => $this->dataService->getActiveControllerAuthority(Auth::user()) ?? DatalinkAuthority::find('NAT'), '_pageTitle' => 'Create Manual Clearance', ]); } diff --git a/app/Http/Controllers/RclMessagesController.php b/app/Http/Controllers/RclMessagesController.php index bb68694..e9e9625 100644 --- a/app/Http/Controllers/RclMessagesController.php +++ b/app/Http/Controllers/RclMessagesController.php @@ -5,6 +5,7 @@ use App\Enums\DatalinkAuthorities; use App\Enums\RclResponsesEnum; use App\Http\Requests\RclMessageRequest; +use App\Models\DatalinkAuthority; use App\Models\RclMessage; use App\Models\Track; use App\Services\CpdlcService; @@ -39,6 +40,7 @@ public function create() 'flight_level' => substr($data?->flight_plan?->altitude, 0, 3) ?? null, 'arrival_icao' => $data?->flight_plan?->arrival ?? null, 'tracks' => Track::whereActive(true)->when($isConcorde, fn ($query) => $query->orWhere('concorde', true))->get(), + 'datalinkAuthorities' => DatalinkAuthority::whereValidRclTarget(true)->pluck('name', 'id'), 'isConcorde' => $isConcorde, '_pageTitle' => 'Request Oceanic Clearance', ]); @@ -59,12 +61,12 @@ public function store(RclMessageRequest $request) $rclMessage->save(); // If RCL auto acknowledgement enabled, send CPDLC acknowledgement - if (config('app.rcl_auto_acknowledgement_enabled')) { + if (config('app.rcl_auto_acknowledgement_enabled') && $rclMessage->targetDatalinkAuthority->auto_acknowledge_participant) { $rclMessage->acknowledged_at = now(); $rclMessage->is_acknowledged = true; $rclMessage->save(); $this->cpdlcService->sendMessage( - author: DatalinkAuthorities::SYS, + author: $rclMessage->targetDatalinkAuthority, recipient: $rclMessage->callsign, recipientAccount: $rclMessage->vatsimAccount, message: sprintf(RclResponsesEnum::Acknowledge->value, strtoupper(DatalinkAuthorities::SYS->description())), diff --git a/app/Http/Livewire/Controllers/CreateManualClx.php b/app/Http/Livewire/Controllers/CreateManualClx.php index c68e342..b45e51d 100644 --- a/app/Http/Livewire/Controllers/CreateManualClx.php +++ b/app/Http/Livewire/Controllers/CreateManualClx.php @@ -7,6 +7,7 @@ use App\Enums\RclResponsesEnum; use App\Events\ClxIssuedEvent; use App\Models\ClxMessage; +use App\Models\DatalinkAuthority; use App\Models\RclMessage; use App\Models\Track; use App\Models\VatsimAccount; @@ -36,6 +37,8 @@ class CreateManualClx extends Component public function mount() { + $dataService = new VatsimDataService(); + $this->activeDlAuthority = $dataService->getActiveControllerAuthority(Auth::user()) ?? DatalinkAuthority::find('NAT'); $this->tmi = current_tmi(); } public function render() @@ -164,7 +167,7 @@ public function submit() 'entry_time_restriction' => null, //TODO implement 'raw_entry_time_restriction' => $rclMessage->entry_time, 'free_text' => '** RCL/CLX MANUALLY ENTERED BY ATC **', - 'datalink_authority' => $this->activeDlAuthority, + 'datalink_authority_id' => $this->activeDlAuthority->id, 'is_concorde' => $rclMessage->is_concorde, ]); if ($rclMessage->trac) { @@ -176,7 +179,7 @@ public function submit() } $array = [ - 'CLX '.now()->format('Hi dmy').' '.$clxMessage->datalink_authority->name.' CLRNCE '.$clxMessage->id, + 'CLX '.now()->format('Hi dmy').' '.$clxMessage->datalinkAuthority->id.' CLRNCE '.$clxMessage->id, $rclMessage->callsign.' CLRD TO '.$rclMessage->destination.' VIA '.$clxMessage->entry_fix, $clxMessage->track ? 'NAT '.$clxMessage->track->identifier : 'RANDOM ROUTE', $clxMessage->track ? $clxMessage->track->last_routeing : $clxMessage->random_routeing, @@ -210,9 +213,9 @@ public function submit() $clxMessage->datalink_message = $array; $msg = ''; if ($clxMessage->track) { - $msg = "{$clxMessage->datalink_authority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, track {$clxMessage->track->identifier}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; + $msg = "{$clxMessage->datalinkAuthority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, track {$clxMessage->track->identifier}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; } else { - $msg = "{$clxMessage->datalink_authority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, random routeing {$clxMessage->random_routeing}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; + $msg = "{$clxMessage->datalinkAuthority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, random routeing {$clxMessage->random_routeing}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}."; } // Only show crossing restriction if entry time =/= the restriction due to the bodge if ($clxMessage->entry_time_restriction && ($clxMessage->raw_entry_time_restriction != $rclMessage->entry_time)) { @@ -256,7 +259,7 @@ public function submit() ->causedBy($clxMessage->vatsimAccount) ->performedOn($rclMessage) ->withProperties(['datalink' => $clxMessage->data_link_message]) - ->log('CLX Message Transmitted By '.$clxMessage->datalink_authority->name); + ->log('CLX Message Transmitted By '.$clxMessage->datalinkAuthority->name); flashAlert(type: 'success', title: null, message: 'Clearance transmitted.', toast: true, timer: true); diff --git a/app/Http/Livewire/Controllers/LtNewPending.php b/app/Http/Livewire/Controllers/LtNewPending.php index 53bba2a..020d040 100644 --- a/app/Http/Livewire/Controllers/LtNewPending.php +++ b/app/Http/Livewire/Controllers/LtNewPending.php @@ -2,7 +2,9 @@ namespace App\Http\Livewire\Controllers; +use App\Enums\DatalinkAuthorities; use App\Http\Controllers\ClxMessagesController; +use App\Models\DatalinkAuthority; use App\Models\Track; use Illuminate\Database\Eloquent\Builder; use phpDocumentor\Reflection\Types\Boolean; @@ -60,6 +62,10 @@ public function columns(): array ->sortable(), Column::make("Request time", "request_time") ->sortable(), + Column::make("Target") + ->label(function ($row, Column $column) { + return Rclmessage::whereId($row->id)->first()->targetDatalinkAuthority->id ?? 'N/A'; + }), LinkColumn::make('View') // make() has no effect in this case but needs to be set anyway ->title(fn($row) => 'View ' . $row->callsign) ->location(function($row) { @@ -93,23 +99,27 @@ private function scopeWhereRandomRouteing($query) public function filters(): array { - $options = Track::query() + $trackOptions = Track::query() ->orderBy('identifier') ->get() ->keyBy('id') ->map(fn($track) => $track->identifier) ->toArray(); - $options[100] = 'RR'; + $trackOptions[100] = 'RR'; + $authorityOptions = DatalinkAuthority::query() + ->orderBy('id') + ->get() + ->keyBy('id') + ->map(fn($authority) => $authority->name) + ->toArray(); return [ - MultiSelectDropdownFilter::make('Track') - ->options( - $options - ) + MultiSelectFilter::make('Track') + ->options($trackOptions) ->setFirstOption('All') - ->filter(function(Builder $builder, array $value) use ($options) { + ->filter(function(Builder $builder, array $value) use ($trackOptions) { $selections = []; foreach ($value as $selection) { - $selections[] = $options[$selection]; + $selections[] = $trackOptions[$selection]; } if (in_array('RR', $selections)) { unset($value[array_search('100', $value)]); @@ -123,6 +133,12 @@ public function filters(): array $builder->whereIn('track_id', array_values($value)); } }), + MultiSelectFilter::make('Target OCA') + ->options($authorityOptions) + ->setFirstOption('All') + ->filter(function(Builder $builder, array $value) { + $builder->whereIn('target_datalink_authority_id', array_values($value)); + }), SelectFilter::make('Acknowledged') ->options([ '' => 'All', diff --git a/app/Http/Requests/RclMessageRequest.php b/app/Http/Requests/RclMessageRequest.php index c5d4093..56a7b4e 100644 --- a/app/Http/Requests/RclMessageRequest.php +++ b/app/Http/Requests/RclMessageRequest.php @@ -4,6 +4,7 @@ use App\Enums\DatalinkAuthorities; use App\Enums\RclResponsesEnum; +use App\Models\DatalinkAuthority; use App\Models\Track; use App\Services\CpdlcService; use Carbon\Carbon; @@ -34,6 +35,7 @@ public function rules(): array 'tmi' => 'required|numeric|min:001|max:366', 'random_routeing' => 'nullable|regex:/^[A-Z\/0-9 _]*[A-Z\/0-9][A-Z\/0-9 _]*$/', 'is_concorde' => 'nullable', + 'target_datalink_authority_id' => 'required', ]; } @@ -42,6 +44,7 @@ public function rules(): array 'flight_level.max' => 'You must file a valid flight level.', 'max_flight_level.max' => 'You must file a valid maximum flight level.', 'callsign.alpha_num' => 'Your callsign must be valid with no spaces as you would enter it into your pilot client. E.g. BAW14LA, AAL134', + 'target_datalink_authority_id.required' => 'You must select the first oceanic sector you will be flying through.' ]; public function prepareForValidation() @@ -88,8 +91,8 @@ public function withValidator($validator) /** Entry fix time requirement */ if (config('app.rcl_time_constraints_enabled') && strlen($this->entry_time) == 4) { if (!$this->entryTimeWithinRange($this->entry_time)) { - if (config('app.rcl_auto_acknowledgement_enabled')) { - $this->cpdlcService->sendMessage(author: DatalinkAuthorities::SYS, recipient: $this->callsign, recipientAccount: Auth::user(), message: sprintf(RclResponsesEnum::Contact->value, strtoupper(DatalinkAuthorities::OCEN->description())), caption: RclResponsesEnum::Contact->text()); + if (config('app.rcl_auto_acknowledgement_enabled') && DatalinkAuthority::find($this->target_datalink_authority_id)->auto_acknowledge_participant) { + $this->cpdlcService->sendMessage(author: DatalinkAuthority::find('SYST'), recipient: $this->callsign, recipientAccount: Auth::user(), message: sprintf(RclResponsesEnum::Contact->value, strtoupper(DatalinkAuthorities::OCEN->description())), caption: RclResponsesEnum::Contact->text()); } $lower = config('app.rcl_lower_limit') + 1; $upper = config('app.rcl_upper_limit') - 1; diff --git a/app/Models/ClxMessage.php b/app/Models/ClxMessage.php index 3f612c6..69a93a8 100644 --- a/app/Models/ClxMessage.php +++ b/app/Models/ClxMessage.php @@ -69,6 +69,13 @@ * @property string|null $cancellation_reason * @method static Builder|ClxMessage whereCancellationReason($value) * @method static Builder|ClxMessage whereCancelled($value) + * @property string $datalink_authority_id + * @property string|null $entry_time_restriction_interval_callsign + * @property int|null $entry_time_restriction_interval_minutes + * @property-read \App\Models\DatalinkAuthority $datalinkAuthority + * @method static Builder|ClxMessage whereDatalinkAuthorityId($value) + * @method static Builder|ClxMessage whereEntryTimeRestrictionIntervalCallsign($value) + * @method static Builder|ClxMessage whereEntryTimeRestrictionIntervalMinutes($value) * @mixin \Eloquent */ class ClxMessage extends Model @@ -92,7 +99,7 @@ public function prunable(): Builder * @var array */ protected $fillable = [ - 'vatsim_account_id', 'rcl_message_id', 'flight_level', 'mach', 'track_id', 'random_routeing', 'entry_fix', 'entry_time_restriction', 'free_text', 'datalink_authority', 'simple_datalink_message', 'datalink_message', 'upper_flight_level', 'raw_entry_time_restriction', 'overwritten_by_clx_message_id', 'overwritten', 'is_concorde', 'cancelled', 'cancellation_reason' + 'vatsim_account_id', 'rcl_message_id', 'flight_level', 'mach', 'track_id', 'random_routeing', 'entry_fix', 'entry_time_restriction', 'free_text', 'datalink_authority_id', 'simple_datalink_message', 'datalink_message', 'upper_flight_level', 'raw_entry_time_restriction', 'overwritten_by_clx_message_id', 'overwritten', 'is_concorde', 'cancelled', 'cancellation_reason' ]; /** @@ -111,7 +118,6 @@ public function getActivitylogOptions(): LogOptions * @var string[] */ protected $casts = [ - 'datalink_authority' => DatalinkAuthorities::class, 'datalink_message' => 'array', 'cancellation_reason' => ClxCancellationReasons::class, ]; @@ -126,6 +132,16 @@ public function rclMessage(): BelongsTo return $this->belongsTo(RclMessage::class); } + /** + * Returns the authority that issued the message. + * + * @return BelongsTo + */ + public function datalinkAuthority(): BelongsTo + { + return $this->belongsTo(DatalinkAuthority::class); + } + /** * Returns the track the CLX was for. * @@ -184,8 +200,8 @@ public function toMessageHistoryFormat(): array 'simple_datalink_message' => $this->simple_datalink_message, 'datalink_message' => $this->datalink_message, 'datalink_authority' => [ - 'id' => $this->datalink_authority->name, - 'description' => $this->datalink_authority->description(), + 'id' => $this->datalinkAuthority->id, + 'description' => $this->datalinkAuthority->name, ], 'cancelled' => $this->cancelled, 'cancellation_reason' => $this->cancellation_reason?->text(), diff --git a/app/Models/CpdlcMessage.php b/app/Models/CpdlcMessage.php index 9f2d922..32b5ef9 100644 --- a/app/Models/CpdlcMessage.php +++ b/app/Models/CpdlcMessage.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Prunable; +use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * App\Models\CpdlcMessage @@ -33,6 +34,9 @@ * @property string|null $caption * @method static Builder|CpdlcMessage whereCaption($value) * @method static Builder|CpdlcMessage whereMessage($value) + * @property string $datalink_authority_id + * @property-read \App\Models\DatalinkAuthority $datalinkAuthority + * @method static Builder|CpdlcMessage whereDatalinkAuthorityId($value) * @mixin \Eloquent */ class CpdlcMessage extends Model @@ -50,25 +54,29 @@ public function prunable(): Builder } protected $fillable = [ - 'pilot_id', 'pilot_callsign', 'datalink_authority', 'message', 'caption', + 'pilot_id', 'pilot_callsign', 'datalink_authority_id', 'message', 'caption', ]; - protected $casts = [ - 'datalink_authority' => DatalinkAuthorities::class, - ]; +// protected $casts = [ +// ]; public function pilot() { return $this->belongsTo(VatsimAccount::class, 'pilot_id'); } + public function datalinkAuthority(): BelongsTo + { + return $this->belongsTo(DatalinkAuthority::class); + } + public function toMessageHistoryFormat(): array { return [ 'id' => $this->id, 'datalink_authority' => [ - 'id' => $this->datalink_authority->name, - 'description' => $this->datalink_authority->description(), + 'id' => $this->datalinkAuthority->id, + 'description' => $this->datalinkAuthority->name, ], 'message' => $this->message, 'caption' => $this->caption, diff --git a/app/Models/DatalinkAuthority.php b/app/Models/DatalinkAuthority.php new file mode 100644 index 0000000..3a4e358 --- /dev/null +++ b/app/Models/DatalinkAuthority.php @@ -0,0 +1,56 @@ + 'boolean', + 'valid_rcl_target' => 'boolean', + 'system' => 'boolean', + ]; + + protected $keyType = 'string'; + + public function scopeNotSystem(Builder $query): Builder + { + return $query->where('system', false); + } +} diff --git a/app/Models/RclMessage.php b/app/Models/RclMessage.php index 67c5236..2dbd35a 100644 --- a/app/Models/RclMessage.php +++ b/app/Models/RclMessage.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Enums\DatalinkAuthorities; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -99,6 +100,15 @@ * @property string|null $acknowledged_at * @method static Builder|RclMessage whereAcknowledgedAt($value) * @method static Builder|RclMessage whereIsAcknowledged($value) + * @property string $target_datalink_authority + * @method static Builder|RclMessage acknowledged() + * @method static Builder|RclMessage notAcknowledged() + * @method static Builder|RclMessage whereTargetDatalinkAuthority($value) + * @property string|null $datalink_authority_id + * @property string|null $target_datalink_authority_id + * @method static Builder|RclMessage whereDatalinkAuthorityId($value) + * @method static Builder|RclMessage whereTargetDatalinkAuthorityId($value) + * @property-read \App\Models\DatalinkAuthority|null $targetDatalinkAuthority * @mixin \Eloquent */ class RclMessage extends Model @@ -139,7 +149,7 @@ public function prunable(): Builder * @var string[] */ protected $fillable = [ - 'vatsim_account_id', 'callsign', 'destination', 'flight_level', 'max_flight_level', 'mach', 'track_id', 'random_routeing', 'entry_fix', 'entry_time', 'tmi', 'request_time', 'free_text', 'atc_rejected', 'upper_flight_level', 'is_concorde', 'previous_entry_time', 'new_entry_time', 'previous_clx_message', 'new_entry_time_notified_at', 'is_acknowledged', 'acknowledged_at' + 'vatsim_account_id', 'callsign', 'destination', 'flight_level', 'max_flight_level', 'mach', 'track_id', 'random_routeing', 'entry_fix', 'entry_time', 'tmi', 'request_time', 'free_text', 'atc_rejected', 'upper_flight_level', 'is_concorde', 'previous_entry_time', 'new_entry_time', 'previous_clx_message', 'new_entry_time_notified_at', 'is_acknowledged', 'acknowledged_at', 'target_datalink_authority_id' ]; /** @@ -263,15 +273,15 @@ public function getDataLinkMessageAttribute(): string { if ($this->is_concorde) { if ($this->track) { - return "{$this->callsign} REQ CONC CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} CONC TRACK {$this->track->identifier} BLOCK LOWER F{$this->flight_level} UPPER F{$this->upper_flight_level} M{$this->mach} TMI {$this->tmi}"; + return "{$this->callsign} TO {$this->targetDatalinkAuthority->name} REQ CONC CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} CONC TRACK {$this->track->identifier} BLOCK LOWER F{$this->flight_level} UPPER F{$this->upper_flight_level} M{$this->mach} TMI {$this->tmi}"; } else { - return "{$this->callsign} REQ CONC CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} {$this->random_routeing} BLOCK LOWER F{$this->flight_level} UPPER F{$this->upper_flight_level} M{$this->mach} TMI {$this->tmi}"; + return "{$this->callsign} TO {$this->targetDatalinkAuthority->name} REQ CONC CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} {$this->random_routeing} BLOCK LOWER F{$this->flight_level} UPPER F{$this->upper_flight_level} M{$this->mach} TMI {$this->tmi}"; } } else { if ($this->track) { - return "{$this->callsign} REQ CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} TRACK {$this->track->identifier} F{$this->flight_level} M{$this->mach} MAX F{$this->max_flight_level} TMI {$this->tmi}"; + return "{$this->callsign} TO {$this->targetDatalinkAuthority->name} REQ CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} TRACK {$this->track->identifier} F{$this->flight_level} M{$this->mach} MAX F{$this->max_flight_level} TMI {$this->tmi}"; } else { - return "{$this->callsign} REQ CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} {$this->random_routeing} F{$this->flight_level} M{$this->mach} MAX F{$this->max_flight_level} TMI {$this->tmi}"; + return "{$this->callsign} TO {$this->targetDatalinkAuthority->name} REQ CLRNCE {$this->destination} VIA {$this->entry_fix}/{$this->entry_time} {$this->random_routeing} F{$this->flight_level} M{$this->mach} MAX F{$this->max_flight_level} TMI {$this->tmi}"; } } } @@ -292,4 +302,9 @@ protected function routeIdentifier(): Attribute get: fn () => $this->track ? $this->track->id : 'RR' ); } + + public function targetDatalinkAuthority(): BelongsTo + { + return $this->belongsTo(DatalinkAuthority::class, 'target_datalink_authority_id'); + } } diff --git a/app/Services/CpdlcService.php b/app/Services/CpdlcService.php index 49ddb9b..74a8931 100644 --- a/app/Services/CpdlcService.php +++ b/app/Services/CpdlcService.php @@ -4,6 +4,7 @@ use App\Enums\DatalinkAuthorities; use App\Models\CpdlcMessage; +use App\Models\DatalinkAuthority; use App\Models\VatsimAccount; class CpdlcService @@ -15,10 +16,10 @@ class CpdlcService * @param string $message * @return CpdlcMessage */ - public function sendMessage(DatalinkAuthorities $author, string $recipient, VatsimAccount $recipientAccount, string $message, ?string $caption): CpdlcMessage + public function sendMessage(DatalinkAuthority $author, string $recipient, VatsimAccount $recipientAccount, string $message, ?string $caption): CpdlcMessage { return CpdlcMessage::create([ - 'datalink_authority' => $author, + 'datalink_authority_id' => $author->id, 'pilot_id' => $recipientAccount->id, 'pilot_callsign' => $recipient, 'message' => $message, diff --git a/app/Services/VatsimDataService.php b/app/Services/VatsimDataService.php index 9b07167..579c418 100644 --- a/app/Services/VatsimDataService.php +++ b/app/Services/VatsimDataService.php @@ -5,6 +5,7 @@ use App\Enums\AccessLevelEnum; use App\Enums\DatalinkAuthorities; use App\Enums\DomesticAuthorities; +use App\Models\DatalinkAuthority; use App\Models\VatsimAccount; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; @@ -128,12 +129,10 @@ public function getActiveControllerAuthority(VatsimAccount $vatsimAccount) $callsignPrefix = strtok($this->getActiveControllerData($vatsimAccount)->callsign, '_'); - foreach (DatalinkAuthorities::cases() as $authority) { - if ($callsignPrefix == $authority->value) { - return $authority; - } + $authority = DatalinkAuthority::wherePrefix($callsignPrefix)->first(); + if ($authority) { + return $authority; } - return null; } diff --git a/database/migrations/2024_10_20_124639_create_datalink_authorities_table.php b/database/migrations/2024_10_20_124639_create_datalink_authorities_table.php new file mode 100644 index 0000000..709d658 --- /dev/null +++ b/database/migrations/2024_10_20_124639_create_datalink_authorities_table.php @@ -0,0 +1,25 @@ +string('id')->unique()->primary(); + $table->string('name'); + $table->string('prefix')->unique(); + $table->boolean('auto_acknowledge_participant')->default(false); + $table->boolean('valid_rcl_target')->default(true); + $table->boolean('system')->default(false); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('datalink_authorities'); + } +}; diff --git a/database/migrations/2024_10_23_104818_implement_database_dl_authorities_on_models.php b/database/migrations/2024_10_23_104818_implement_database_dl_authorities_on_models.php new file mode 100644 index 0000000..8a4b759 --- /dev/null +++ b/database/migrations/2024_10_23_104818_implement_database_dl_authorities_on_models.php @@ -0,0 +1,29 @@ +renameColumn('datalink_authority', 'datalink_authority_id'); + }); + + Schema::table('cpdlc_messages', function (Blueprint $table) { + $table->renameColumn('datalink_authority', 'datalink_authority_id'); + }); + } + + public function down(): void + { + Schema::table('clx_messages', function (Blueprint $table) { + $table->renameColumn('datalink_authority_id', 'datalink_authority'); + }); + + Schema::table('cpdlc_messages', function (Blueprint $table) { + $table->renameColumn('datalink_authority_id', 'datalink_authority'); + }); + } +}; diff --git a/database/migrations/2024_10_23_105816_implement_dl_authority_foreign_keys.php b/database/migrations/2024_10_23_105816_implement_dl_authority_foreign_keys.php new file mode 100644 index 0000000..4a209e8 --- /dev/null +++ b/database/migrations/2024_10_23_105816_implement_dl_authority_foreign_keys.php @@ -0,0 +1,33 @@ +string('datalink_authority_id')->default(null)->change(); + $table->foreign('datalink_authority_id')->references('id')->on('datalink_authorities'); + }); + + Schema::table('cpdlc_messages', function (Blueprint $table) { + $table->string('datalink_authority_id')->default(null)->change(); + $table->foreign('datalink_authority_id')->references('id')->on('datalink_authorities'); + }); + } + + public function down(): void + { + Schema::table('clx_messages', function (Blueprint $table) { + $table->dropForeign('clx_messages_datalink_authority_id_foreign'); + $table->string('datalink_authority', 4)->default('NATX')->change(); + }); + + Schema::table('cpdlc_messages', function (Blueprint $table) { + $table->dropForeign('cpdlc_messages_datalink_authority_id_foreign'); + $table->string('datalink_authority', 4)->default(null)->change(); + }); + } +}; diff --git a/database/migrations/2024_10_23_112029_add_target_datalink_authority_to_rcl_messages.php b/database/migrations/2024_10_23_112029_add_target_datalink_authority_to_rcl_messages.php new file mode 100644 index 0000000..81354a5 --- /dev/null +++ b/database/migrations/2024_10_23_112029_add_target_datalink_authority_to_rcl_messages.php @@ -0,0 +1,23 @@ +string('target_datalink_authority_id')->nullable(); + $table->foreign('target_datalink_authority_id')->references('id')->on('datalink_authorities')->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('rcl_messages', function (Blueprint $table) { + $table->dropForeign('rcl_messages_target_datalink_authority_id_foreign'); + $table->dropColumn('target_datalink_authority_id'); + }); + } +}; diff --git a/database/seeders/DatalinkAuthoritiesSeeder.php b/database/seeders/DatalinkAuthoritiesSeeder.php new file mode 100644 index 0000000..41b3ab2 --- /dev/null +++ b/database/seeders/DatalinkAuthoritiesSeeder.php @@ -0,0 +1,112 @@ + 'CZQO', + 'name' => 'Gander', + 'prefix' => 'CZQO', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => true, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'EGGX', + 'name' => 'Shanwick', + 'prefix' => 'EGGX', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => true, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'BIRD', + 'name' => 'Reykjavik', + 'prefix' => 'BIRD', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => true, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'KZNY', + 'name' => 'New York', + 'prefix' => 'NY', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => false, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'LPPO', + 'name' => 'Santa Maria', + 'prefix' => 'LPPO', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => false, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'NAT', + 'name' => 'North Atlantic Bandbox', + 'prefix' => 'NAT', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => false, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'CZQXD', + 'name' => 'Gander Domestic', + 'prefix' => 'CZQX', + 'auto_acknowledge_participant' => false, + 'valid_rcl_target' => false, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'CZQXM', + 'name' => 'Moncton Domestic', + 'prefix' => 'CZQM', + 'auto_acknowledge_participant' => false, + 'valid_rcl_target' => false, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'EISN', + 'name' => 'Shannon Domestic', + 'prefix' => 'EISN', + 'auto_acknowledge_participant' => false, + 'valid_rcl_target' => false, + 'system' => false, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'SYST', + 'name' => 'natTrak System', + 'prefix' => 'SYST', + 'auto_acknowledge_participant' => false, + 'valid_rcl_target' => false, + 'system' => true, + ]); + + DatalinkAuthority::updateOrCreate([ + 'id' => 'OCEN', + 'name' => 'Oceanic Controller', + 'prefix' => 'OCEN', + 'auto_acknowledge_participant' => true, + 'valid_rcl_target' => false, + 'system' => true, + ]); + } +} diff --git a/resources/views/controllers/clx/rcl-messages/show.blade.php b/resources/views/controllers/clx/rcl-messages/show.blade.php index ed63f15..8f382c5 100644 --- a/resources/views/controllers/clx/rcl-messages/show.blade.php +++ b/resources/views/controllers/clx/rcl-messages/show.blade.php @@ -100,6 +100,10 @@ CID {{ $message->vatsimAccount->full_name }} {{ $message->vatsimAccount->id }} + + Target OCA + {{ $message->targetDatalinkAuthority->id }} + @@ -146,7 +150,7 @@
diff --git a/resources/views/pilots/rcl/create.blade.php b/resources/views/pilots/rcl/create.blade.php index b06df95..2118e88 100644 --- a/resources/views/pilots/rcl/create.blade.php +++ b/resources/views/pilots/rcl/create.blade.php @@ -64,7 +64,7 @@
- +
@if (config('app.ctp_info_enabled'))
Ensure you enter your assigned oceanic flight level as per your booking!
@@ -84,7 +84,7 @@
- +
@if (config('app.ctp_info_enabled'))
Ensure you enter your max flight level as per your booking!
@@ -126,6 +126,17 @@
Oceanic entry
+
+
+ + +
+