forked from steger/pr3-ws202526
airport starting point
parent
2d1eec66ea
commit
c3fed07a8f
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Assignment - Airport Simulation
|
||||||
|
This go-program is a simulation of the departure process of an airport that consists of *aircrafts*, *passengers*, *checkin-counter*, *security-checks*, *gates*, a *baggage handling system*, a *runway*, a *flight schedule* and *gates*. Passengers go through the following steps:
|
||||||
|
- **Checkin**: The ticket is verified, baggage is checked in and handed over to the baggage handling system, and a boarding pass is issued.
|
||||||
|
- **Security-Check**: The boarding pass is verified and the passenger is checked for prohibited items.
|
||||||
|
- **Gate**: The passenger walks to the correct gate and waits there until boarding time. The gate takes then care of boarding the aircraft, while simultaneously loading baggage into the aircraft.
|
||||||
|
|
||||||
|
Each step has a processing time and there are several waiting times in between.
|
||||||
|
|
||||||
|
Aircrafts arrive at the gate on boarding time and leave to the runway on departure time according to the flight schedule.
|
||||||
|
|
||||||
|
## Task
|
||||||
|
|
||||||
|
Your task is to complete the implementation by going through the **TODO** statements in the files `baggageHandlingSystem.go`, `securityCheck.go`, and `gate.go`. All other files already contain an implementation.
|
||||||
|
|
||||||
|
**Hint**: The expected behavior is defined in detail in the corresponding unit tests.
|
||||||
|
|
||||||
|
**Hint**: Be aware of synchronization issues between the different goroutines for all actors. Use `channels` whenever applicable. See `checkin.go` for inspiration.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```
|
||||||
|
Flight Number Destination Gate Boarding Time Departure Time
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
AF2062 ZAG 7 13:59:27 13:59:57
|
||||||
|
BA8851 SOF 9 13:59:47 14:00:17
|
||||||
|
SK4243 FRA 5 14:01:46 14:02:16
|
||||||
|
KL5728 WAW 9 14:03:06 14:03:36
|
||||||
|
SK8183 PRG 8 14:03:12 14:03:42
|
||||||
|
AF8488 LIS 5 13:59:45 14:00:15
|
||||||
|
KL5937 RIX 1 14:01:26 14:01:56
|
||||||
|
AY1065 BRU 7 14:00:37 14:01:07
|
||||||
|
BA1529 ZRH 5 14:02:03 14:02:33
|
||||||
|
IB2405 IST 3 14:01:51 14:02:21
|
||||||
|
What/Who arrives at airport? (p for passenger, c for car, t for train, q to quit): p
|
||||||
|
stoic_dijkstra has arrived at the airport for the flight number IB2405 to IST in 3.11 minutes with 2 bags and carry-on items: [Medicine Knife Sharpener]
|
||||||
|
stoic_dijkstra completed the checkin procedure and is now heading to security
|
||||||
|
stoic_dijkstra has successfully passed the security check and is now walking to the gate
|
||||||
|
elated_hellman has arrived at the airport for the flight number KL5728 to WAW in 4.19 minutes with 2 bags and carry-on items: [Explosives]
|
||||||
|
vibrant_maxwell has arrived at the airport for the flight number BA8851 to SOF in 0.88 minutes with 2 bags and carry-on items: [Keys Matches]
|
||||||
|
dazzling_lewin has arrived at the airport for the flight number AF2062 to ZAG in 0.54 minutes with 1 bags and carry-on items: []
|
||||||
|
nifty_hermann has arrived at the airport for the flight number IB2405 to IST in 2.94 minutes with 1 bags and carry-on items: [Resistance Bands Gloves]
|
||||||
|
elated_hellman completed the checkin procedure and is now heading to security
|
||||||
|
vibrant_maxwell completed the checkin procedure and is now heading to security
|
||||||
|
elated_hellman: prohibited item detected: [Explosives]
|
||||||
|
flight AF2062 is ready for boarding
|
||||||
|
dazzling_lewin completed the checkin procedure and is now heading to security
|
||||||
|
vibrant_maxwell has successfully passed the security check and is now walking to the gate
|
||||||
|
nifty_hermann completed the checkin procedure and is now heading to security
|
||||||
|
dazzling_lewin has successfully passed the security check and is now walking to the gate
|
||||||
|
nifty_hermann has successfully passed the security check and is now walking to the gate
|
||||||
|
dazzling_lewin has successfully boarded flight AF2062 to ZAG
|
||||||
|
flight AF8488 is ready for boarding
|
||||||
|
flight BA8851 is ready for boarding
|
||||||
|
loaded baggage {[Goggles Fire Extinguisher Controller]} onto flight BA8851
|
||||||
|
loaded baggage {[Book Clipboard]} onto flight BA8851
|
||||||
|
vibrant_maxwell has successfully boarded flight BA8851 to SOF
|
||||||
|
boarding completed for flight number AF2062
|
||||||
|
Flight AF2062 is waiting for takeoff
|
||||||
|
Flight AF2062 to ZAG is taking off
|
||||||
|
Flight AF2062 to ZAG has taken off with 1 passengers and 0 bags on board
|
||||||
|
Closing airport
|
||||||
|
The following baggage was lost today: []
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Aircraft struct {
|
||||||
|
Flight Flight
|
||||||
|
Passengers []Passenger
|
||||||
|
Baggage []Baggage
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BaggageProcessor = &Aircraft{}
|
||||||
|
|
||||||
|
func NewAircraft(flight Flight) Aircraft {
|
||||||
|
return Aircraft{flight, []Passenger{}, []Baggage{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *Aircraft) ProcessBaggage(fn FlightNumber, b Baggage) error {
|
||||||
|
if fn != ac.Flight.Id {
|
||||||
|
return fmt.Errorf("wrong flight %s", fn)
|
||||||
|
}
|
||||||
|
ac.Baggage = append(ac.Baggage, b)
|
||||||
|
fmt.Printf("loaded baggage %v onto flight %v\n", b, fn)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *Aircraft) Process(p Passenger) error {
|
||||||
|
if p.BoardingPass.FlightNumber != ac.Flight.Id {
|
||||||
|
return fmt.Errorf("%s is unauthorized to enter aircraft", p.Name)
|
||||||
|
}
|
||||||
|
ac.Passengers = append(ac.Passengers, p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *Aircraft) Start(gate *Gate, rwy *Runway) {
|
||||||
|
gate.ProcessAircraft(ac)
|
||||||
|
rwy.ProcessAircraft(*ac)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Airport struct {
|
||||||
|
Checkin Checkin
|
||||||
|
Security SecurityCheck
|
||||||
|
Gates map[GateNumber]*Gate
|
||||||
|
Runway Runway
|
||||||
|
Bhs *BaggageHandlingSystem
|
||||||
|
FlightSchedule FlightSchedule
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultAirport() Airport {
|
||||||
|
|
||||||
|
ap := Airport{
|
||||||
|
Security: NewSecurityCheck(time.Duration(1 * time.Second)),
|
||||||
|
Gates: NewGates(10),
|
||||||
|
Runway: NewRunway(time.Duration(3 * time.Second)),
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := GenerateRandomFlightSchedule(ap.Gates, 10)
|
||||||
|
ap.FlightSchedule = fs
|
||||||
|
|
||||||
|
baggageSinks := make(map[FlightNumber](BaggageProcessor))
|
||||||
|
for _, g := range ap.Gates {
|
||||||
|
|
||||||
|
var flightsForGate []Flight
|
||||||
|
for _, f := range fs.AllFlights() {
|
||||||
|
if f.GateNumber == g.id {
|
||||||
|
flightsForGate = append(flightsForGate, f)
|
||||||
|
}
|
||||||
|
baggageSinks[f.Id] = g
|
||||||
|
}
|
||||||
|
|
||||||
|
g.SetFlights(flightsForGate)
|
||||||
|
ap.Gates[g.id] = g
|
||||||
|
}
|
||||||
|
|
||||||
|
bhs := NewBaggageHandlingSystem(time.Duration(1*time.Second), baggageSinks)
|
||||||
|
ap.Bhs = &bhs
|
||||||
|
ap.Checkin = NewCheckin(time.Duration(1*time.Second), &fs, &bhs)
|
||||||
|
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Airport) Start() {
|
||||||
|
go a.Checkin.Start()
|
||||||
|
go a.Security.Start()
|
||||||
|
go a.Bhs.Start()
|
||||||
|
go a.Runway.Start()
|
||||||
|
|
||||||
|
for _, g := range a.Gates {
|
||||||
|
go g.Start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Airport) NewPassenger() Passenger {
|
||||||
|
return NewRandomPassenger(a.FlightSchedule.AllFlights())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaggageProcessor interface {
|
||||||
|
ProcessBaggage(FlightNumber, Baggage) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaggageHandlingSystem struct {
|
||||||
|
processingTime time.Duration
|
||||||
|
sinks map[FlightNumber]BaggageProcessor
|
||||||
|
//TODO: extend
|
||||||
|
}
|
||||||
|
|
||||||
|
type LostBaggage struct {
|
||||||
|
Baggage
|
||||||
|
FlightNumber FlightNumber
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BaggageProcessor = &BaggageHandlingSystem{}
|
||||||
|
|
||||||
|
func NewBaggageHandlingSystem(processingTime time.Duration, sinks map[FlightNumber]BaggageProcessor) BaggageHandlingSystem {
|
||||||
|
return BaggageHandlingSystem{
|
||||||
|
processingTime: processingTime,
|
||||||
|
sinks: sinks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bhs *BaggageHandlingSystem) ProcessBaggage(fn FlightNumber, b Baggage) error {
|
||||||
|
//TODO: implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bhs *BaggageHandlingSystem) CollectLostBaggage() []LostBaggage {
|
||||||
|
//TODO: implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bhs *BaggageHandlingSystem) Start() {
|
||||||
|
//TODO: implement
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
package airport_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/06-airport/airport"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaggageSpyData struct {
|
||||||
|
fn airport.FlightNumber
|
||||||
|
b airport.Baggage
|
||||||
|
t time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaggageProcessorSpy struct {
|
||||||
|
Baggage []BaggageSpyData
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bps *BaggageProcessorSpy) ProcessBaggage(fn airport.FlightNumber, b airport.Baggage) error {
|
||||||
|
bps.Baggage = append(bps.Baggage, BaggageSpyData{fn, b, time.Now()})
|
||||||
|
return bps.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ airport.BaggageProcessor = &BaggageProcessorSpy{}
|
||||||
|
|
||||||
|
func compareSink(t *testing.T, expected []airport.Baggage, expectedFlightNumber airport.FlightNumber, got BaggageProcessorSpy, start time.Time) {
|
||||||
|
for _, b := range got.Baggage {
|
||||||
|
if elapsed := b.t.Sub(start); elapsed < 1*time.Millisecond {
|
||||||
|
t.Errorf("processing time too short: got %v, want at least 1ms", elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.fn != expectedFlightNumber {
|
||||||
|
t.Errorf("expected flight number: %v, got: %v", expectedFlightNumber, b.fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(got.Baggage) != len(expected) {
|
||||||
|
t.Errorf("expected number of baggage: %d, got: %d", len(expected), len(got.Baggage))
|
||||||
|
}
|
||||||
|
|
||||||
|
deliveredBaggage := []airport.Baggage{}
|
||||||
|
for _, b := range got.Baggage {
|
||||||
|
deliveredBaggage = append(deliveredBaggage, b.b)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(deliveredBaggage, expected) {
|
||||||
|
t.Errorf("expected delivered baggage for %v: %v, got: %v", expectedFlightNumber, expected, deliveredBaggage)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBaggageHandlingSystem(t *testing.T) {
|
||||||
|
|
||||||
|
sink123 := BaggageProcessorSpy{nil, nil}
|
||||||
|
sink456 := BaggageProcessorSpy{nil, fmt.Errorf("aircraft already gone")}
|
||||||
|
sinks := map[airport.FlightNumber]airport.BaggageProcessor{
|
||||||
|
airport.FlightNumber("LH123"): &sink123,
|
||||||
|
airport.FlightNumber("LH456"): &sink456,
|
||||||
|
}
|
||||||
|
sut := airport.NewBaggageHandlingSystem(1*time.Millisecond, sinks)
|
||||||
|
go sut.Start()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
baggage airport.Baggage
|
||||||
|
fn airport.FlightNumber
|
||||||
|
expectErr bool
|
||||||
|
expectDeliveredBaggage123 []airport.Baggage
|
||||||
|
expectDeliveredBaggage456 []airport.Baggage
|
||||||
|
expectLostBaggage []airport.LostBaggage
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid baggage",
|
||||||
|
baggage: airport.Baggage{[]string{"Socks", "Shirts"}},
|
||||||
|
fn: "LH123",
|
||||||
|
expectErr: false,
|
||||||
|
expectDeliveredBaggage123: []airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
expectDeliveredBaggage456: []airport.Baggage{},
|
||||||
|
expectLostBaggage: []airport.LostBaggage{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Aircraft already gone",
|
||||||
|
baggage: airport.Baggage{[]string{"Socks", "Shirts"}},
|
||||||
|
fn: "LH456",
|
||||||
|
expectErr: false,
|
||||||
|
expectDeliveredBaggage123: []airport.Baggage{},
|
||||||
|
expectDeliveredBaggage456: []airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
expectLostBaggage: []airport.LostBaggage{
|
||||||
|
{
|
||||||
|
airport.Baggage{[]string{"Socks", "Shirts"}},
|
||||||
|
"LH456",
|
||||||
|
fmt.Errorf("aircraft already gone"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Unknown flight",
|
||||||
|
baggage: airport.Baggage{[]string{"Socks", "Shirts"}},
|
||||||
|
fn: "XX999",
|
||||||
|
expectErr: true,
|
||||||
|
expectDeliveredBaggage123: []airport.Baggage{},
|
||||||
|
expectDeliveredBaggage456: []airport.Baggage{},
|
||||||
|
expectLostBaggage: []airport.LostBaggage{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
sink123.Baggage = []BaggageSpyData{}
|
||||||
|
sink456.Baggage = []BaggageSpyData{}
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
err := sut.ProcessBaggage(tc.fn, tc.baggage)
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond) //not really threadsafe because the 10 milliseconds have never been guaranteed. TODO: replace by sync mechanism
|
||||||
|
|
||||||
|
if (err != nil) != tc.expectErr {
|
||||||
|
t.Errorf("expected error: %v, got: %v", tc.expectErr, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareSink(t, tc.expectDeliveredBaggage123, "LH123", sink123, start)
|
||||||
|
compareSink(t, tc.expectDeliveredBaggage456, "LH456", sink456, start)
|
||||||
|
|
||||||
|
lostBaggage := sut.CollectLostBaggage()
|
||||||
|
if !reflect.DeepEqual(lostBaggage, tc.expectLostBaggage) {
|
||||||
|
t.Errorf("expected lost baggage: %v, got: %v", tc.expectLostBaggage, lostBaggage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Checkin struct {
|
||||||
|
processingTime time.Duration
|
||||||
|
queue chan passengerCheckinData
|
||||||
|
flightProvider FlightProvider
|
||||||
|
baggageProcessor BaggageProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
type passengerCheckinData struct {
|
||||||
|
p *Passenger
|
||||||
|
err chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCheckin(processingTime time.Duration, fp FlightProvider, bp BaggageProcessor) Checkin {
|
||||||
|
return Checkin{
|
||||||
|
processingTime: processingTime,
|
||||||
|
queue: make(chan passengerCheckinData),
|
||||||
|
flightProvider: fp,
|
||||||
|
baggageProcessor: bp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Checkin) Process(p *Passenger) error {
|
||||||
|
err := make(chan error)
|
||||||
|
c.queue <- passengerCheckinData{p: p, err: err}
|
||||||
|
return <-err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Checkin) Start() {
|
||||||
|
defer fmt.Println("checkin terminated")
|
||||||
|
for element := range c.queue {
|
||||||
|
|
||||||
|
time.Sleep(c.processingTime)
|
||||||
|
|
||||||
|
//1. check ticket
|
||||||
|
if element.p.Name != element.p.Ticket.Name {
|
||||||
|
element.err <- fmt.Errorf("name %s on ticket does not match", element.p.Ticket.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
flight, err := c.flightProvider.GetFlight(element.p.Ticket.FlightNumber)
|
||||||
|
if err != nil {
|
||||||
|
element.err <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//2. issue boarding pass
|
||||||
|
element.p.BoardingPass = &BoardingPass{element.p.Ticket, flight.GateNumber}
|
||||||
|
|
||||||
|
//3. check in bags
|
||||||
|
var baggageErr error
|
||||||
|
for _, bag := range element.p.Bags {
|
||||||
|
if err := c.baggageProcessor.ProcessBaggage(flight.Id, bag); err != nil {
|
||||||
|
baggageErr = errors.Join(baggageErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if baggageErr != nil {
|
||||||
|
element.err <- baggageErr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
element.p.Bags = []Baggage{}
|
||||||
|
|
||||||
|
element.err <- nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
package airport_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/06-airport/airport"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlightProviderStub struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fps FlightProviderStub) GetFlight(fn airport.FlightNumber) (airport.Flight, error) {
|
||||||
|
if fn == "LH123" {
|
||||||
|
return airport.Flight{"LH123", "FRA", airport.GateNumber(1), time.Now(), time.Now()}, nil
|
||||||
|
}
|
||||||
|
return airport.Flight{}, fmt.Errorf("unknown flight %v", fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ airport.FlightProvider = FlightProviderStub{}
|
||||||
|
|
||||||
|
func TestCheckin(t *testing.T) {
|
||||||
|
|
||||||
|
fp := FlightProviderStub{}
|
||||||
|
bps := BaggageProcessorSpy{}
|
||||||
|
sut := airport.NewCheckin(1*time.Millisecond, &fp, &bps)
|
||||||
|
go sut.Start()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
passenger airport.Passenger
|
||||||
|
expectErr bool
|
||||||
|
expectBaggage []airport.Baggage
|
||||||
|
expectBoardingpass *airport.BoardingPass
|
||||||
|
expectBPSBaggage []airport.Baggage
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid passenger with valid flight",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"Max Mustermann",
|
||||||
|
airport.Ticket{"Max Mustermann", "FRA", "LH123"},
|
||||||
|
nil,
|
||||||
|
[]airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
expectBaggage: []airport.Baggage{},
|
||||||
|
expectBoardingpass: &airport.BoardingPass{airport.Ticket{"Max Mustermann", "FRA", "LH123"}, airport.GateNumber(1)},
|
||||||
|
expectBPSBaggage: []airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Passenger with unknown flight",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"John Doe",
|
||||||
|
airport.Ticket{"John Doe", "MUC", "LH999"},
|
||||||
|
nil,
|
||||||
|
[]airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
expectBaggage: []airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
expectBoardingpass: nil,
|
||||||
|
expectBPSBaggage: []airport.Baggage{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Passenger with wrong ticket",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"Max Mustermann",
|
||||||
|
airport.Ticket{"Someone else", "FRA", "LH123"},
|
||||||
|
nil,
|
||||||
|
[]airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
expectBaggage: []airport.Baggage{{[]string{"Socks", "Shirts"}}},
|
||||||
|
expectBoardingpass: nil,
|
||||||
|
expectBPSBaggage: []airport.Baggage{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
bps.Baggage = []BaggageSpyData{}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
err := sut.Process(&tc.passenger)
|
||||||
|
|
||||||
|
if elapsed := time.Since(start); elapsed < 1*time.Millisecond {
|
||||||
|
t.Errorf("processing time too short: got %v, want at least 1ms", elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != nil) != tc.expectErr {
|
||||||
|
t.Errorf("expected error: %v, got: %v", tc.expectErr, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(tc.passenger.BoardingPass, tc.expectBoardingpass) {
|
||||||
|
t.Errorf("expected boarding pass: %v, got: %v", tc.expectBoardingpass, tc.passenger.BoardingPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(tc.passenger.Bags, tc.expectBaggage) {
|
||||||
|
t.Errorf("expected baggage: %v, got: %v", tc.expectBaggage, tc.passenger.Bags)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotBPSBaggage := []airport.Baggage{}
|
||||||
|
for _, b := range bps.Baggage {
|
||||||
|
gotBPSBaggage = append(gotBPSBaggage, b.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(gotBPSBaggage, tc.expectBPSBaggage) {
|
||||||
|
t.Errorf("expected baggage processor baggage: %v, got: %v", tc.expectBPSBaggage, gotBPSBaggage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlightNumber string
|
||||||
|
|
||||||
|
type Flight struct {
|
||||||
|
Id FlightNumber
|
||||||
|
Destination string
|
||||||
|
GateNumber GateNumber
|
||||||
|
BoardingTime time.Time
|
||||||
|
DepartureTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlightProvider interface {
|
||||||
|
GetFlight(FlightNumber) (Flight, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlightSchedule struct {
|
||||||
|
flights map[FlightNumber]Flight
|
||||||
|
}
|
||||||
|
|
||||||
|
var possibleDestinations = []string{
|
||||||
|
"AMS", "ATH", "BCN", "BRU", "BUD", "CPH", "DUB", "DUS", "EDI", "FCO",
|
||||||
|
"FRA", "GVA", "HEL", "IST", "LIS", "LYS", "MAD", "MUC", "OSL", "PRG",
|
||||||
|
"RIX", "SOF", "STO", "TLL", "VIE", "WAW", "ZAG", "ZRH", "LUX", "MLA",
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomFlightNumbers(count int) []FlightNumber {
|
||||||
|
|
||||||
|
airlines := []string{"LH", "AF", "KL", "BA", "SK", "AZ", "IB", "TK", "AY", "OS"}
|
||||||
|
flightNumbers := make([]FlightNumber, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
airline := airlines[rand.Intn(len(airlines))]
|
||||||
|
number := rand.Intn(9000) + 1000 // Random 4-digit number
|
||||||
|
flightNumbers[i] = FlightNumber(fmt.Sprintf("%s%04d", airline, number))
|
||||||
|
}
|
||||||
|
|
||||||
|
return flightNumbers
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomFlightSchedule(gates map[GateNumber]*Gate, count int) FlightSchedule {
|
||||||
|
flightNumbers := GenerateRandomFlightNumbers(count)
|
||||||
|
flights := make(map[FlightNumber]Flight)
|
||||||
|
|
||||||
|
gateNumbers := make([]GateNumber, 0, len(gates))
|
||||||
|
for gn, _ := range gates {
|
||||||
|
gateNumbers = append(gateNumbers, gn)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, flightNumber := range flightNumbers {
|
||||||
|
destination := possibleDestinations[rand.Intn(len(possibleDestinations))]
|
||||||
|
|
||||||
|
gateNumber := gateNumbers[rand.Intn(len(gateNumbers))]
|
||||||
|
departureTime := time.Now().Add(time.Duration(rand.Intn(5*60)) * time.Second)
|
||||||
|
boardingTime := departureTime.Add(-30 * time.Second)
|
||||||
|
|
||||||
|
flights[flightNumber] = Flight{
|
||||||
|
Id: flightNumber,
|
||||||
|
Destination: destination,
|
||||||
|
GateNumber: gateNumber,
|
||||||
|
BoardingTime: boardingTime,
|
||||||
|
DepartureTime: departureTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlightSchedule{flights: flights}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FlightSchedule) AllFlights() []Flight {
|
||||||
|
flights := make([]Flight, 0, len(fs.flights))
|
||||||
|
for _, flight := range fs.flights {
|
||||||
|
flights = append(flights, flight)
|
||||||
|
}
|
||||||
|
return flights
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FlightSchedule) GetFlight(flightNumber FlightNumber) (Flight, error) {
|
||||||
|
if flight, ok := fs.flights[flightNumber]; ok {
|
||||||
|
return flight, nil
|
||||||
|
} else {
|
||||||
|
return Flight{}, fmt.Errorf("invalid flight number %v", flightNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FlightSchedule) Print() {
|
||||||
|
fmt.Printf("%-15s %-15s %-10s %-20s %-20s\n", "Flight Number", "Destination", "Gate", "Boarding Time", "Departure Time")
|
||||||
|
fmt.Println(strings.Repeat("-", 80))
|
||||||
|
for _, flight := range fs.AllFlights() {
|
||||||
|
fmt.Printf("%-15s %-15s %-10d %-20s %-20s\n",
|
||||||
|
flight.Id,
|
||||||
|
flight.Destination,
|
||||||
|
flight.GateNumber,
|
||||||
|
flight.BoardingTime.Format("15:04:05"),
|
||||||
|
flight.DepartureTime.Format("15:04:05"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var distantFuture = time.Unix(1<<63-1, 0) // Max value for Unix timestamp
|
||||||
|
|
||||||
|
type GateNumber int
|
||||||
|
|
||||||
|
type Gate struct {
|
||||||
|
id GateNumber
|
||||||
|
walkingTimeFromSecurity time.Duration
|
||||||
|
taxiTimeToRunway time.Duration
|
||||||
|
//TODO: extend
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BaggageProcessor = &Gate{}
|
||||||
|
|
||||||
|
func NewGate(id GateNumber, walkingTimeFromSecurity time.Duration, taxiTimeToRunway time.Duration) Gate {
|
||||||
|
|
||||||
|
return Gate{id,
|
||||||
|
walkingTimeFromSecurity,
|
||||||
|
taxiTimeToRunway,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGates(count int) map[GateNumber]*Gate {
|
||||||
|
gates := make(map[GateNumber]*Gate)
|
||||||
|
for i := range count {
|
||||||
|
walkingTime := time.Duration(5+rand.Intn(6)) * time.Second
|
||||||
|
taxiTime := time.Duration(5+rand.Intn(6)) * time.Second
|
||||||
|
|
||||||
|
gate := NewGate(GateNumber(i+1), walkingTime, taxiTime)
|
||||||
|
gates[GateNumber(i+1)] = &gate
|
||||||
|
}
|
||||||
|
return gates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gate) SetFlights(flights []Flight) {
|
||||||
|
//TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blocks until departure (includes taxi time to runway)
|
||||||
|
func (g *Gate) ProcessAircraft(ac *Aircraft) {
|
||||||
|
//TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gate) ProcessBaggage(fn FlightNumber, b Baggage) error {
|
||||||
|
//TODO: implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Gate) Start() {
|
||||||
|
//TODO: implement
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
package airport_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/06-airport/airport"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGateSingleFlight(t *testing.T) {
|
||||||
|
|
||||||
|
sut := airport.NewGate(airport.GateNumber(25), time.Duration(3*time.Millisecond), time.Duration(1*time.Millisecond))
|
||||||
|
flight := airport.Flight{"LH123", "FRA", 25, time.Now().Add(5 * time.Millisecond), time.Now().Add(10 * time.Millisecond)}
|
||||||
|
sut.SetFlights([]airport.Flight{
|
||||||
|
flight,
|
||||||
|
})
|
||||||
|
go sut.Start()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
//the aircraft
|
||||||
|
wg.Add(1)
|
||||||
|
ac := airport.NewAircraft(flight)
|
||||||
|
departureTime := time.Now()
|
||||||
|
go func() { sut.ProcessAircraft(&ac); departureTime = time.Now(); wg.Done() }()
|
||||||
|
|
||||||
|
//the baggage
|
||||||
|
wg.Add(1)
|
||||||
|
baggage := airport.Baggage{[]string{"socks", "shirt"}}
|
||||||
|
var bag1err error
|
||||||
|
go func() { bag1err = sut.ProcessBaggage("LH123", baggage); wg.Done() }()
|
||||||
|
|
||||||
|
//a passenger arriving on time
|
||||||
|
wg.Add(1)
|
||||||
|
ticket1 := airport.Ticket{"John Doe", "FRA", "LH123"}
|
||||||
|
pax1 := airport.Passenger{"John Doe", ticket1, &airport.BoardingPass{ticket1, 25}, []airport.Baggage{}, []string{}}
|
||||||
|
pax1boardingTime := time.Now()
|
||||||
|
var pax1err error
|
||||||
|
go func() { pax1err = sut.Process(pax1); pax1boardingTime = time.Now(); wg.Done() }()
|
||||||
|
|
||||||
|
//another passenger arriving too late
|
||||||
|
wg.Add(1)
|
||||||
|
ticket2 := airport.Ticket{"Jane Doe", "FRA", "LH123"}
|
||||||
|
pax2 := airport.Passenger{"Jane Doe", ticket2, &airport.BoardingPass{ticket2, 25}, []airport.Baggage{}, []string{}}
|
||||||
|
var pax2err error
|
||||||
|
go func() { time.Sleep(8 * time.Millisecond); pax2err = sut.Process(pax2); wg.Done() }()
|
||||||
|
|
||||||
|
//another passenger arriving sometime during boarding
|
||||||
|
wg.Add(1)
|
||||||
|
ticket3 := airport.Ticket{"Alex Smith", "FRA", "LH123"}
|
||||||
|
pax3 := airport.Passenger{"Alex Smith", ticket3, &airport.BoardingPass{ticket3, 25}, []airport.Baggage{}, []string{}}
|
||||||
|
pax3boardingTime := time.Now()
|
||||||
|
var pax3err error
|
||||||
|
go func() {
|
||||||
|
time.Sleep(6 * time.Millisecond)
|
||||||
|
pax3err = sut.Process(pax3)
|
||||||
|
pax3boardingTime = time.Now()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
//------------------------FROM HERE: checking ----------------------------------
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
//--------------------------check aircraft-------------------
|
||||||
|
if elapsed := departureTime.Sub(start); elapsed < 11*time.Millisecond {
|
||||||
|
t.Errorf("departure time too short: got %v, want at least 11ms", elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedBoardedPax := []airport.Passenger{pax1, pax3}
|
||||||
|
if !reflect.DeepEqual(expectedBoardedPax, ac.Passengers) {
|
||||||
|
t.Errorf("boarded passengers mismatch: got %v, want %v", ac.Passengers, expectedBoardedPax)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedBaggageOnboard := []airport.Baggage{baggage}
|
||||||
|
if !reflect.DeepEqual(expectedBaggageOnboard, ac.Baggage) {
|
||||||
|
t.Errorf("baggage on board mismatch: got %v, want %v", ac.Baggage, expectedBaggageOnboard)
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------check baggage--------------------
|
||||||
|
|
||||||
|
if bag1err != nil {
|
||||||
|
t.Errorf("baggage encountered an error during processing: %v", bag1err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------check passengers-------------------
|
||||||
|
|
||||||
|
//boarding can only happen between boarding (5ms from start) and departure (10ms from start)
|
||||||
|
if elapsed := pax1boardingTime.Sub(start); elapsed < 5*time.Millisecond || elapsed > 10*time.Millisecond {
|
||||||
|
t.Errorf("passenger 1 boarding time out of range: got %v, want between 5ms and 10ms", elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pax1err != nil {
|
||||||
|
t.Errorf("passenger 1 encountered an error during boarding: %v", pax1err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pax2err == nil {
|
||||||
|
t.Errorf("passenger 2 did not encounter an error during boarding, but was expected to")
|
||||||
|
}
|
||||||
|
|
||||||
|
//boarding can only happen after boarding (5ms from start)
|
||||||
|
if elapsed := pax3boardingTime.Sub(start); elapsed < 5*time.Millisecond {
|
||||||
|
t.Errorf("passenger 3 boarding time out of range: got %v, want more than 5ms", elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pax3err != nil {
|
||||||
|
t.Errorf("passenger 3 encountered an error during boarding: %v", pax3err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/namesgenerator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ticket struct {
|
||||||
|
Name string
|
||||||
|
Destination string
|
||||||
|
FlightNumber FlightNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoardingPass struct {
|
||||||
|
Ticket
|
||||||
|
Gate GateNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
type Baggage struct {
|
||||||
|
Items []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Passenger struct {
|
||||||
|
Name string
|
||||||
|
Ticket Ticket
|
||||||
|
BoardingPass *BoardingPass
|
||||||
|
Bags []Baggage
|
||||||
|
CarryOnItems []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var PossibleItems = []string{
|
||||||
|
"Laptop", "Tablet", "Smartphone", "Headphones", "Charger", "Book", "Magazine", "Notebook", "Pen", "Pencil", "Carabiner",
|
||||||
|
"Water Bottle", "Snacks", "Sandwich", "Camera", "Tripod", "Gun", "Clothes", "Shoes", "Toothbrush", "Toothpaste",
|
||||||
|
"Shampoo", "Conditioner", "Soap", "Deodorant", "Perfume", "Makeup Kit", "Hairbrush", "Comb", "Razor", "Shaving Cream",
|
||||||
|
"First Aid Kit", "Medicine", "Umbrella", "Hat", "Sunglasses", "Scarf", "Gloves", "Wallet", "Passport", "Camping Gear",
|
||||||
|
"Keys", "Watch", "Jewelry", "Flashlight", "Power Bank", "Knife", "USB Drive", "External Hard Drive", "Gaming Console", "Controller",
|
||||||
|
"Board Game", "Puzzle", "Toy", "Explosives", "Stuffed Animal", "Blanket", "Pillow", "Sleeping Bag", "Towel", "Swimsuit",
|
||||||
|
"Goggles", "Flip Flops", "Backpack", "Handbag", "Suitcase", "Briefcase", "Binoculars", "Map", "Guidebook", "Notebook",
|
||||||
|
"Sketchpad", "Paints", "Brushes", "Musical Instrument", "Sheet Music", "Sports Equipment", "Yoga Mat", "Jump Rope", "Resistance Bands",
|
||||||
|
"Tent", "Cooking Utensils", "Portable Stove", "Food Containers", "Water Filter", "Fishing Rod", "Knife Sharpener", "Multi-tool", "Rope",
|
||||||
|
"Compass", "GPS Device", "Safety Vest", "Helmet", "Protective Gear", "Fire Extinguisher", "Matches", "Lighter", "Candles", "Notebook",
|
||||||
|
"Clipboard", "Calculator", "Documents", "Folders", "Envelopes",
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRandomPassenger(flights []Flight) Passenger {
|
||||||
|
|
||||||
|
// Generate random ticket details
|
||||||
|
name := namesgenerator.GetRandomName(0)
|
||||||
|
flight := flights[rand.Intn(len(flights))]
|
||||||
|
|
||||||
|
ticket := Ticket{
|
||||||
|
Name: name,
|
||||||
|
Destination: flight.Destination,
|
||||||
|
FlightNumber: flight.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate random baggage
|
||||||
|
numBags := rand.Intn(3) // Random number of bags (0 to 2)
|
||||||
|
bags := make([]Baggage, numBags)
|
||||||
|
for i := 0; i < numBags; i++ {
|
||||||
|
numItems := rand.Intn(5) + 1 // Random number of items (1 to 5)
|
||||||
|
items := make([]string, numItems)
|
||||||
|
for j := 0; j < numItems; j++ {
|
||||||
|
items[j] = PossibleItems[rand.Intn(len(PossibleItems))]
|
||||||
|
}
|
||||||
|
bags[i] = Baggage{
|
||||||
|
Items: items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate random carry-on items
|
||||||
|
numCarryOnItems := rand.Intn(3) // Random number of carry-on items (0 to 2)
|
||||||
|
carryOnItems := make([]string, numCarryOnItems)
|
||||||
|
for i := 0; i < numCarryOnItems; i++ {
|
||||||
|
carryOnItems[i] = PossibleItems[rand.Intn(len(PossibleItems))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return Passenger{
|
||||||
|
Name: name,
|
||||||
|
Ticket: ticket,
|
||||||
|
BoardingPass: nil,
|
||||||
|
Bags: bags,
|
||||||
|
CarryOnItems: carryOnItems,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Passenger) Start(ap *Airport) {
|
||||||
|
|
||||||
|
fmt.Printf("%s has arrived at the airport for the flight number %s to %s in %.02f minutes with %d bags and carry-on items: %v\n",
|
||||||
|
p.Name,
|
||||||
|
p.Ticket.FlightNumber,
|
||||||
|
p.Ticket.Destination,
|
||||||
|
time.Until(ap.FlightSchedule.flights[p.Ticket.FlightNumber].DepartureTime).Minutes(),
|
||||||
|
len(p.Bags),
|
||||||
|
p.CarryOnItems)
|
||||||
|
|
||||||
|
err := ap.Checkin.Process(p)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("\033[31m%s: %v\033[0m\n", p.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("%s completed the checkin procedure and is now heading to security\n", p.Name)
|
||||||
|
|
||||||
|
err = ap.Security.Process(*p)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("\033[31m%s: %v\033[0m\n", p.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s has successfully passed the security check and is now walking to the gate\n", p.Name)
|
||||||
|
|
||||||
|
gate, ok := ap.Gates[p.BoardingPass.Gate]
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("\033[31m%s: invalid gate number %v\033[0m\n", p.Name, p.BoardingPass.Gate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gate.Process(*p)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("\033[31mError: %s: %v\033[0m\n", p.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\033[32m%s has successfully boarded flight %s to %s\033[0m\n", p.Name, p.Ticket.FlightNumber, p.Ticket.Destination)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Runway struct {
|
||||||
|
takeoffDuration time.Duration
|
||||||
|
queue chan Aircraft
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRunway(takeoffDuration time.Duration) Runway {
|
||||||
|
return Runway{
|
||||||
|
takeoffDuration: takeoffDuration,
|
||||||
|
queue: make(chan Aircraft),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runway) Start() {
|
||||||
|
defer fmt.Println("runway terminated")
|
||||||
|
|
||||||
|
for ac := range r.queue {
|
||||||
|
fmt.Printf("Flight %v to %v is taking off\n", ac.Flight.Id, ac.Flight.Destination)
|
||||||
|
time.Sleep(r.takeoffDuration)
|
||||||
|
fmt.Printf("\033[32mFlight %v to %v has taken off with %v passengers and %v bags on board\033[0m\n", ac.Flight.Id, ac.Flight.Destination, int(len(ac.Passengers)), int(len(ac.Baggage)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runway) ProcessAircraft(ac Aircraft) {
|
||||||
|
fmt.Printf("Flight %v is waiting for takeoff\n", ac.Flight.Id)
|
||||||
|
r.queue <- ac
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package airport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prohibitedItems = map[string]bool{"Knife": true, "Gun": true, "Explosives": true}
|
||||||
|
|
||||||
|
type SecurityCheck struct {
|
||||||
|
processingTime time.Duration
|
||||||
|
//TODO: extend
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSecurityCheck(processingTime time.Duration) SecurityCheck {
|
||||||
|
return SecurityCheck{
|
||||||
|
processingTime: processingTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processes one passenger at a time. Each passenger must at least spend the processingTime at the security check
|
||||||
|
func (sc *SecurityCheck) Start() {
|
||||||
|
//TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns an error if
|
||||||
|
// - the passenger has no boarding pass
|
||||||
|
// - 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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
package airport_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/06-airport/airport"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecurityCheck(t *testing.T) {
|
||||||
|
|
||||||
|
sut := airport.NewSecurityCheck(1 * time.Millisecond)
|
||||||
|
go sut.Start()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
passenger airport.Passenger
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid passenger",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"Max Mustermann",
|
||||||
|
airport.Ticket{"Max Mustermann", "FRA", "LH123"},
|
||||||
|
&airport.BoardingPass{airport.Ticket{"Max Mustermann", "FRA", "LH123"}, airport.GateNumber(1)},
|
||||||
|
[]airport.Baggage{},
|
||||||
|
[]string{"Laptop", "Hat"},
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Passenger without boarding pass",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"Max Mustermann",
|
||||||
|
airport.Ticket{"Max Mustermann", "FRA", "LH123"},
|
||||||
|
nil,
|
||||||
|
[]airport.Baggage{},
|
||||||
|
[]string{"Laptop", "Hat"},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Passenger with wrong boarding pass",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"Max Mustermann",
|
||||||
|
airport.Ticket{"Max Mustermann", "FRA", "LH123"},
|
||||||
|
&airport.BoardingPass{airport.Ticket{"Someone else", "FRA", "LH123"}, airport.GateNumber(1)},
|
||||||
|
[]airport.Baggage{},
|
||||||
|
[]string{"Laptop", "Hat"},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Passenger with prohibited item",
|
||||||
|
passenger: airport.Passenger{
|
||||||
|
"Max Mustermann",
|
||||||
|
airport.Ticket{"Max Mustermann", "FRA", "LH123"},
|
||||||
|
&airport.BoardingPass{airport.Ticket{"Max Mustermann", "FRA", "LH123"}, airport.GateNumber(1)},
|
||||||
|
[]airport.Baggage{},
|
||||||
|
[]string{"Laptop", "Hat", "Knife"},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
err := sut.Process(tc.passenger)
|
||||||
|
|
||||||
|
if elapsed := time.Since(start); elapsed < 1*time.Millisecond {
|
||||||
|
t.Errorf("processing time too short: got %v, want at least 1ms", elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != nil) != tc.expectErr {
|
||||||
|
t.Errorf("expected error: %v, got: %v", tc.expectErr, err != nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
module gitty.informatik.hs-mannheim.de/steger/pr3-code/go/06-airport
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require github.com/docker/docker v28.3.3+incompatible
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
||||||
|
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitty.informatik.hs-mannheim.de/steger/pr3-code/go/06-airport/airport"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ap := airport.DefaultAirport()
|
||||||
|
|
||||||
|
ap.FlightSchedule.Print()
|
||||||
|
defer func() {
|
||||||
|
lostBaggage := ap.Bhs.CollectLostBaggage()
|
||||||
|
fmt.Println("The following baggage was lost today: ", lostBaggage)
|
||||||
|
}()
|
||||||
|
|
||||||
|
ap.Start()
|
||||||
|
|
||||||
|
for _, flight := range ap.FlightSchedule.AllFlights() {
|
||||||
|
ac := airport.NewAircraft(flight)
|
||||||
|
gate := ap.Gates[flight.GateNumber]
|
||||||
|
go ac.Start(gate, &ap.Runway)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
fmt.Print("What/Who arrives at airport? (p for passenger, c for car, t for train, q to quit): ")
|
||||||
|
for {
|
||||||
|
input, _ := reader.ReadString('\n')
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
|
||||||
|
switch input {
|
||||||
|
case "q":
|
||||||
|
fmt.Println("Closing airport")
|
||||||
|
return
|
||||||
|
case "p":
|
||||||
|
p := ap.NewPassenger()
|
||||||
|
go p.Start(&ap)
|
||||||
|
case "c":
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
p := ap.NewPassenger()
|
||||||
|
go p.Start(&ap)
|
||||||
|
}
|
||||||
|
case "t":
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
p := ap.NewPassenger()
|
||||||
|
go p.Start(&ap)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Println("Invalid command.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue