From 1a40f2acba2507108d4e920fb6ae177782d26776 Mon Sep 17 00:00:00 2001 From: Oliver Stolle 3024383 <3024383@stud.hs-mannheim.de> Date: Mon, 27 Apr 2026 20:51:31 +0000 Subject: [PATCH] Implemented the whole exercise Co-authored-by: Copilot --- .../airport/baggageHandlingSystem.go | 4 +- go/06-airport/airport/gate.go | 323 +++++++++++++++++- go/06-airport/airport/securityCheck.go | 44 ++- 3 files changed, 354 insertions(+), 17 deletions(-) diff --git a/go/06-airport/airport/baggageHandlingSystem.go b/go/06-airport/airport/baggageHandlingSystem.go index a30fcec..9920928 100644 --- a/go/06-airport/airport/baggageHandlingSystem.go +++ b/go/06-airport/airport/baggageHandlingSystem.go @@ -59,7 +59,7 @@ func (bhs *BaggageHandlingSystem) CollectLostBaggage() []LostBaggage { } } } - func (bhs *BaggageHandlingSystem) Start() { - + defer fmt.Println("baggage handling terminated") + select {} } diff --git a/go/06-airport/airport/gate.go b/go/06-airport/airport/gate.go index d746d93..0f3a9ef 100644 --- a/go/06-airport/airport/gate.go +++ b/go/06-airport/airport/gate.go @@ -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) + }() + } } diff --git a/go/06-airport/airport/securityCheck.go b/go/06-airport/airport/securityCheck.go index 85ff4ad..b96ead5 100644 --- a/go/06-airport/airport/securityCheck.go +++ b/go/06-airport/airport/securityCheck.go @@ -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 }