forked from steger/pr3-sose2026
parent
1b7756f3cd
commit
1a40f2acba
|
|
@ -59,7 +59,7 @@ func (bhs *BaggageHandlingSystem) CollectLostBaggage() []LostBaggage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bhs *BaggageHandlingSystem) Start() {
|
func (bhs *BaggageHandlingSystem) Start() {
|
||||||
|
defer fmt.Println("baggage handling terminated")
|
||||||
|
select {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package airport
|
package airport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -13,16 +14,26 @@ type Gate struct {
|
||||||
id GateNumber
|
id GateNumber
|
||||||
walkingTimeFromSecurity time.Duration
|
walkingTimeFromSecurity time.Duration
|
||||||
taxiTimeToRunway 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{}
|
var _ BaggageProcessor = &Gate{}
|
||||||
|
|
||||||
func NewGate(id GateNumber, walkingTimeFromSecurity time.Duration, taxiTimeToRunway time.Duration) Gate {
|
func NewGate(id GateNumber, walkingTimeFromSecurity time.Duration, taxiTimeToRunway time.Duration) Gate {
|
||||||
|
|
||||||
return Gate{id,
|
return Gate{
|
||||||
walkingTimeFromSecurity,
|
id: id,
|
||||||
taxiTimeToRunway,
|
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) {
|
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
|
// Blocks for the walking distance to the gate + the time until the passenger has boarded
|
||||||
func (g *Gate) Process(passenger Passenger) error {
|
func (g *Gate) Process(passenger Passenger) error {
|
||||||
//TODO: implement
|
if passenger.BoardingPass == nil {
|
||||||
return 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)
|
// Blocks until departure (includes taxi time to runway)
|
||||||
func (g *Gate) ProcessAircraft(ac *Aircraft) {
|
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 {
|
func (g *Gate) ProcessBaggage(fn FlightNumber, b Baggage) error {
|
||||||
//TODO: implement
|
q, ok := g.baggageQueues[fn]
|
||||||
return nil
|
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() {
|
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
|
package airport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -8,18 +9,52 @@ var prohibitedItems = map[string]bool{"Knife": true, "Gun": true, "Explosives":
|
||||||
|
|
||||||
type SecurityCheck struct {
|
type SecurityCheck struct {
|
||||||
processingTime time.Duration
|
processingTime time.Duration
|
||||||
//TODO: extend
|
queue chan securityCheckData
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityCheckData struct {
|
||||||
|
p Passenger
|
||||||
|
err chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSecurityCheck(processingTime time.Duration) SecurityCheck {
|
func NewSecurityCheck(processingTime time.Duration) SecurityCheck {
|
||||||
return SecurityCheck{
|
return SecurityCheck{
|
||||||
processingTime: processingTime,
|
processingTime: processingTime,
|
||||||
|
queue: make(chan securityCheckData),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// processes one passenger at a time. Each passenger must at least spend the processingTime at the security check
|
// processes one passenger at a time. Each passenger must at least spend the processingTime at the security check
|
||||||
func (sc *SecurityCheck) Start() {
|
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
|
// returns an error if
|
||||||
|
|
@ -27,6 +62,7 @@ func (sc *SecurityCheck) Start() {
|
||||||
// - the passenger's boardings pass does not match the name
|
// - the passenger's boardings pass does not match the name
|
||||||
// - the passenger carries a prohibited item
|
// - the passenger carries a prohibited item
|
||||||
func (sc SecurityCheck) Process(p Passenger) error {
|
func (sc SecurityCheck) Process(p Passenger) error {
|
||||||
//TODO: implement
|
errc := make(chan error)
|
||||||
return nil
|
sc.queue <- securityCheckData{p: p, err: errc}
|
||||||
|
return <-errc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue