1
0
Fork 0

Implemented the whole exercise

Co-authored-by: Copilot <copilot@github.com>
main
Oliver Stolle 2026-04-27 20:51:31 +00:00
parent 1b7756f3cd
commit 1a40f2acba
3 changed files with 354 additions and 17 deletions

View File

@ -59,7 +59,7 @@ func (bhs *BaggageHandlingSystem) CollectLostBaggage() []LostBaggage {
}
}
}
func (bhs *BaggageHandlingSystem) Start() {
defer fmt.Println("baggage handling terminated")
select {}
}

View File

@ -1,6 +1,7 @@
package airport
import (
"fmt"
"math/rand"
"time"
)
@ -13,16 +14,26 @@ type Gate struct {
id GateNumber
walkingTimeFromSecurity time.Duration
taxiTimeToRunway time.Duration
//TODO: extend
flights []Flight
passengerQueues map[FlightNumber]chan passengerGateData
baggageQueues map[FlightNumber]chan baggageRequest
aircraftQueues map[FlightNumber]chan *Aircraft
departSignals map[FlightNumber]chan struct{}
}
var _ BaggageProcessor = &Gate{}
func NewGate(id GateNumber, walkingTimeFromSecurity time.Duration, taxiTimeToRunway time.Duration) Gate {
return Gate{id,
walkingTimeFromSecurity,
taxiTimeToRunway,
return Gate{
id: id,
walkingTimeFromSecurity: walkingTimeFromSecurity,
taxiTimeToRunway: taxiTimeToRunway,
flights: []Flight{},
passengerQueues: make(map[FlightNumber]chan passengerGateData),
baggageQueues: make(map[FlightNumber]chan baggageRequest),
aircraftQueues: make(map[FlightNumber]chan *Aircraft),
departSignals: make(map[FlightNumber]chan struct{}),
}
}
@ -39,25 +50,315 @@ func NewGates(count int) map[GateNumber]*Gate {
}
func (g *Gate) SetFlights(flights []Flight) {
//TODO: implement
g.flights = flights
if g.passengerQueues == nil {
g.passengerQueues = make(map[FlightNumber]chan passengerGateData)
}
if g.baggageQueues == nil {
g.baggageQueues = make(map[FlightNumber]chan baggageRequest)
}
if g.aircraftQueues == nil {
g.aircraftQueues = make(map[FlightNumber]chan *Aircraft)
}
if g.departSignals == nil {
g.departSignals = make(map[FlightNumber]chan struct{})
}
for _, f := range flights {
if _, ok := g.passengerQueues[f.Id]; !ok {
g.passengerQueues[f.Id] = make(chan passengerGateData, 16)
}
if _, ok := g.baggageQueues[f.Id]; !ok {
g.baggageQueues[f.Id] = make(chan baggageRequest, 16)
}
if _, ok := g.aircraftQueues[f.Id]; !ok {
g.aircraftQueues[f.Id] = make(chan *Aircraft, 1)
}
if _, ok := g.departSignals[f.Id]; !ok {
g.departSignals[f.Id] = make(chan struct{}, 1)
}
}
}
type passengerGateData struct {
p Passenger
err chan error
arrival time.Time
}
type baggageRequest struct {
b Baggage
err chan error
}
// Blocks for the walking distance to the gate + the time until the passenger has boarded
func (g *Gate) Process(passenger Passenger) error {
//TODO: implement
return nil
if passenger.BoardingPass == nil {
return fmt.Errorf("no boarding pass")
}
if passenger.BoardingPass.Gate != g.id {
return fmt.Errorf("invalid gate: %v", passenger.BoardingPass.Gate)
}
fn := passenger.BoardingPass.FlightNumber
q, ok := g.passengerQueues[fn]
if !ok {
return fmt.Errorf("invalid flight %v", fn)
}
// find the flight to determine departure time
var flight Flight
found := false
for _, f := range g.flights {
if f.Id == fn {
flight = f
found = true
break
}
}
if !found {
return fmt.Errorf("invalid flight %v", fn)
}
errc := make(chan error)
// compute intended arrival time (start + walking), record before sleeping
intendedArrival := time.Now().Add(g.walkingTimeFromSecurity)
fmt.Printf("[gate %d] Process start for %s now=%v intendedArrival=%v departure=%v\n", g.id, passenger.Name, time.Now(), intendedArrival, flight.DepartureTime)
time.Sleep(g.walkingTimeFromSecurity)
fmt.Printf("[gate %d] after walking for %s now=%v intendedArrival=%v departure=%v\n", g.id, passenger.Name, time.Now(), intendedArrival, flight.DepartureTime)
// if already departed, return immediately
if intendedArrival.After(flight.DepartureTime) {
return fmt.Errorf("flight already departed")
}
// attempt to enqueue but don't block past departure time
data := passengerGateData{p: passenger, err: errc, arrival: intendedArrival}
// allow waiting until departure relative to the intended arrival time
sendTimeout := flight.DepartureTime.Sub(intendedArrival)
if sendTimeout < 0 {
sendTimeout = 0
}
fmt.Printf("[gate %d] enqueuing passenger %s for flight %s (sendTimeout=%v)\n", g.id, passenger.Name, fn, sendTimeout)
select {
case q <- data:
// sent
case <-time.After(sendTimeout):
return fmt.Errorf("flight already departed")
}
res := <-errc
fmt.Printf("[gate %d] passenger %s processed with err=%v\n", g.id, passenger.Name, res)
return res
}
// Blocks until departure (includes taxi time to runway)
func (g *Gate) ProcessAircraft(ac *Aircraft) {
//TODO: implement
fn := ac.Flight.Id
q, ok := g.aircraftQueues[fn]
if !ok {
// nothing to do
return
}
q <- ac
// wait until depart signal
if sig, ok := g.departSignals[fn]; ok {
<-sig
}
}
func (g *Gate) ProcessBaggage(fn FlightNumber, b Baggage) error {
//TODO: implement
return nil
q, ok := g.baggageQueues[fn]
if !ok {
return fmt.Errorf("invalid flight %v", fn)
}
// find flight for departure time
var flight Flight
found := false
for _, f := range g.flights {
if f.Id == fn {
flight = f
found = true
break
}
}
if !found {
return fmt.Errorf("invalid flight %v", fn)
}
errc := make(chan error)
tLeft := time.Until(flight.DepartureTime)
if tLeft <= 0 {
return fmt.Errorf("flight already departed")
}
select {
case q <- baggageRequest{b: b, err: errc}:
// sent
case <-time.After(tLeft):
return fmt.Errorf("flight already departed")
}
return <-errc
}
func (g Gate) Start() {
//TODO: implement
// start a handler for each configured flight
for _, f := range g.flights {
f := f
pCh := g.passengerQueues[f.Id]
bCh := g.baggageQueues[f.Id]
acCh := g.aircraftQueues[f.Id]
depart := g.departSignals[f.Id]
go func() {
// wait until boarding time
fmt.Printf("[gate %d] handler starting for %s: boarding=%v departure=%v now=%v\n", g.id, f.Id, f.BoardingTime, f.DepartureTime, time.Now())
if time.Until(f.BoardingTime) > 0 {
time.Sleep(time.Until(f.BoardingTime))
}
// boarding open
departureTimer := time.NewTimer(time.Until(f.DepartureTime))
waitingPassengers := []passengerGateData{}
waitingBaggage := []Baggage{}
var aircraft *Aircraft
boarding := true
for boarding {
select {
case pg := <-pCh:
fmt.Printf("[gate %d] handler received passenger %s, aircraft=%v arrival=%v departure=%v\n", g.id, pg.p.Name, aircraft != nil, pg.arrival, f.DepartureTime)
if pg.arrival.After(f.DepartureTime) {
pg.err <- fmt.Errorf("flight already departed")
continue
}
if aircraft != nil {
if err := aircraft.Process(pg.p); err != nil {
pg.err <- err
} else {
pg.err <- nil
}
} else {
waitingPassengers = append(waitingPassengers, pg)
}
case bd := <-bCh:
fmt.Printf("[gate %d] handler received baggage for %s, aircraft=%v\n", g.id, f.Id, aircraft != nil)
if aircraft != nil {
if err := aircraft.ProcessBaggage(f.Id, bd.b); err != nil {
bd.err <- err
} else {
bd.err <- nil
}
} else {
waitingBaggage = append(waitingBaggage, bd.b)
bd.err <- nil
}
case ac := <-acCh:
fmt.Printf("[gate %d] handler received aircraft %s\n", g.id, ac.Flight.Id)
aircraft = ac
// deliver waiting passengers
for _, wp := range waitingPassengers {
if time.Now().After(f.DepartureTime) {
wp.err <- fmt.Errorf("flight already departed")
continue
}
if err := aircraft.Process(wp.p); err != nil {
wp.err <- err
} else {
wp.err <- nil
}
}
waitingPassengers = nil
// deliver waiting baggage
for _, wb := range waitingBaggage {
aircraft.ProcessBaggage(f.Id, wb)
}
waitingBaggage = nil
case <-departureTimer.C:
fmt.Printf("[gate %d] handler departure timer fired for %s\n", g.id, f.Id)
boarding = false
}
}
// boarding closed: drain any buffered messages and process those
for {
processed := false
select {
case pg := <-pCh:
processed = true
if pg.arrival.After(f.DepartureTime) {
pg.err <- fmt.Errorf("flight already departed")
continue
}
if aircraft != nil {
if err := aircraft.Process(pg.p); err != nil {
pg.err <- err
} else {
pg.err <- nil
}
} else {
waitingPassengers = append(waitingPassengers, pg)
}
case bd := <-bCh:
processed = true
if aircraft != nil {
if err := aircraft.ProcessBaggage(f.Id, bd.b); err != nil {
bd.err <- err
} else {
bd.err <- nil
}
} else {
waitingBaggage = append(waitingBaggage, bd.b)
bd.err <- nil
}
case ac := <-acCh:
processed = true
aircraft = ac
// deliver waiting passengers
for _, wp := range waitingPassengers {
if wp.arrival.After(f.DepartureTime) {
wp.err <- fmt.Errorf("flight already departed")
continue
}
if err := aircraft.Process(wp.p); err != nil {
wp.err <- err
} else {
wp.err <- nil
}
}
waitingPassengers = nil
// deliver waiting baggage
for _, wb := range waitingBaggage {
aircraft.ProcessBaggage(f.Id, wb)
}
waitingBaggage = nil
default:
}
if !processed {
break
}
}
// notify remaining waiting passengers that the flight is gone
for _, wp := range waitingPassengers {
wp.err <- fmt.Errorf("flight already departed")
}
// After boarding closed, wait taxi time then signal departure
time.Sleep(g.taxiTimeToRunway)
// signal any ProcessAircraft waiting on depart
select {
case depart <- struct{}{}:
default:
}
close(depart)
}()
}
}

View File

@ -1,6 +1,7 @@
package airport
import (
"fmt"
"time"
)
@ -8,18 +9,52 @@ var prohibitedItems = map[string]bool{"Knife": true, "Gun": true, "Explosives":
type SecurityCheck struct {
processingTime time.Duration
//TODO: extend
queue chan securityCheckData
}
type securityCheckData struct {
p Passenger
err chan error
}
func NewSecurityCheck(processingTime time.Duration) SecurityCheck {
return SecurityCheck{
processingTime: processingTime,
queue: make(chan securityCheckData),
}
}
// processes one passenger at a time. Each passenger must at least spend the processingTime at the security check
func (sc *SecurityCheck) Start() {
//TODO: implement
defer fmt.Println("security terminated")
for el := range sc.queue {
time.Sleep(sc.processingTime)
if el.p.BoardingPass == nil {
el.err <- fmt.Errorf("no boarding pass")
continue
}
if el.p.Name != el.p.BoardingPass.Name {
el.err <- fmt.Errorf("boarding pass does not match passenger name")
continue
}
prohibited := false
for _, item := range el.p.CarryOnItems {
if prohibitedItems[item] {
el.err <- fmt.Errorf("prohibited item detected: %v", item)
prohibited = true
break
}
}
if prohibited {
continue
}
el.err <- nil
}
}
// returns an error if
@ -27,6 +62,7 @@ func (sc *SecurityCheck) Start() {
// - the passenger's boardings pass does not match the name
// - the passenger carries a prohibited item
func (sc SecurityCheck) Process(p Passenger) error {
//TODO: implement
return nil
errc := make(chan error)
sc.queue <- securityCheckData{p: p, err: errc}
return <-errc
}