forked from steger/pr3-sose2026
parent
1b7756f3cd
commit
1a40f2acba
|
|
@ -59,7 +59,7 @@ func (bhs *BaggageHandlingSystem) CollectLostBaggage() []LostBaggage {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bhs *BaggageHandlingSystem) Start() {
|
||||
|
||||
defer fmt.Println("baggage handling terminated")
|
||||
select {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue