4. Model: event (kejadian)

Event adalah kejadian yang mengubah keadaan sistem. Simulasi berjalan dengan memproses event satu per satu menurut waktunya. Berkas ini penting untuk statistik karena di sinilah hook dipanggil.

Pola yang berulang di semua event:

if (try_start_...()) return;   /* kalau satu proses berhasil dimulai, selesai untuk sekarang */

Artinya: fungsi try_start_... mencoba memulai suatu proses dan mengembalikan 1 (berhasil) atau 0 (tidak bisa). Bila berhasil, event ini selesai dan event berikutnya (yang sudah dijadwalkan) yang melanjutkan.

4.1 Kedatangan (baris 378 sampai 405)

src/bus-sim.c baris 378-405

/* Event: passenger arrival. */
 
void schedule_next_arrival(int location)
{
    event_schedule(sim_time + expon(locations[location].mean_interarrival,
                                    STREAM_INTERARRIVAL(location)),
                   location);
}
 
void event_arrival(int location)
{
    Passenger p;
 
    /* Schedule the next arrival at this location. */
 
    schedule_next_arrival(location);
 
    /* The new passenger joins the queue. */
 
    p = passenger_create(location);
    passenger_file(&p, LIST_QUEUE(location));
 
    /* The bus is still waiting at this stop, so the passenger can board. */
 
    if (bus.status == BUS_IDLE && bus.location == location)
        try_start_load();
}
 
SintaksArti
event_schedule(sim_time + expon(rata-rata, stream), location);jadwalkan kejadian bernomor location di waktu = sekarang + angka acak eksponensial
p = passenger_create(location);buat satu orang baru
passenger_file(&p, LIST_QUEUE(location));&p = alamat p; masukkan ke antrian lokasi itu
if (bus.status == BUS_IDLE && bus.location == location)&& = “dan”: bus idle dan sedang di lokasi ini

Logika: (1) jadwalkan kedatangan berikutnya di lokasi yang sama, (2) buat orang dan masukkan ke antrian, (3) bila bus sedang menganggur di lokasi ini, langsung coba muat orang itu.

4.2 Memuat (baris 406 sampai 436)

src/bus-sim.c baris 406-436

/* Event: loading, one person at a time until the queue is empty or the bus is full. */
 
int try_start_load(void)
{
    if (list_size[LIST_QUEUE(bus.location)] == 0 || bus_is_full())
        return 0;
 
    /* Take the first person from the queue; the delay in queue ends here. */
 
    bus.current = passenger_remove_first(LIST_QUEUE(bus.location));
    stats_queue_delay(bus.location, sim_time - bus.current.arrival_time);
 
    bus.status = BUS_LOADING;
    event_schedule(sim_time + uniform(LOAD_TIME_MIN, LOAD_TIME_MAX, STREAM_LOAD),
                   EVENT_END_LOAD);
    return 1;
}
 
void event_end_load(void)
{
    bus_board(&bus.current);
 
    /* Load the next person if possible, otherwise the bus becomes idle. */
 
    if (try_start_load())
        return;
 
    bus.status = BUS_IDLE;
    try_depart();
}
 
SintaksArti
list_size[LIST_QUEUE(bus.location)] == 0antrian di lokasi bus kosong
||”atau”
bus.current = passenger_remove_first(...);ambil orang pertama dari antrian, taruh “di pintu”
stats_queue_delay(bus.location, sim_time - bus.current.arrival_time);hook statistik (b): delay = sekarang − waktu tiba
uniform(LOAD_TIME_MIN, LOAD_TIME_MAX, STREAM_LOAD)waktu naik acak antara 15 dan 25 detik (dalam menit) dari stream 5

Logika try_start_load: bila antrian kosong atau bus penuh, kembalikan 0. Bila tidak, ambil orang pertama, catat delay-nya, ubah status menjadi BUS_LOADING, jadwalkan EVENT_END_LOAD setelah waktu naik, kembalikan 1.

Logika event_end_load: orang itu sekarang naik (bus_board). Lalu coba muat orang berikutnya; bila tidak bisa, bus menjadi BUS_IDLE dan dicek apakah boleh berangkat (try_depart).

Rantai try_start_load → event_end_load → try_start_load membuat pemuatan “mengular” satu orang per event, tanpa loop while, karena dalam simulasi waktu hanya bergerak lewat event.

4.3 Menurunkan (baris 437 sampai 469)

src/bus-sim.c baris 437-469

/* Event: unloading, one person at a time. */
 
int try_start_unload(void)
{
    if (!bus_has_passenger_for(bus.location))
        return 0;
 
    bus.current = bus_alight(bus.location);
 
    bus.status = BUS_UNLOADING;
    event_schedule(sim_time + uniform(UNLOAD_TIME_MIN, UNLOAD_TIME_MAX, STREAM_UNLOAD),
                   EVENT_END_UNLOAD);
    return 1;
}
 
void event_end_unload(void)
{
    /* The passenger leaves the system here. */
 
    stats_time_in_system(bus.current.origin, sim_time - bus.current.arrival_time);
 
    /* Unload the next person; when nobody is left, start loading. */
 
    if (try_start_unload())
        return;
 
    if (try_start_load())
        return;
 
    bus.status = BUS_IDLE;
    try_depart();
}
 

Logika try_start_unload: bila tak ada penumpang yang tujuannya lokasi ini, kembalikan 0. Bila ada, ambil yang pertama (bus_alight; jumlah di bus langsung berkurang), ubah status menjadi BUS_UNLOADING, jadwalkan EVENT_END_UNLOAD, kembalikan 1.

Logika event_end_unload: orang itu keluar dari sistem sekarang, jadi catat sim_time - bus.current.arrival_time lewat stats_time_in_system(bus.current.origin, ...): hook statistik (f), dikelompokkan menurut lokasi asal. Lalu urutannya: turunkan yang berikutnya; kalau tak ada, mulai muat; kalau tak ada juga, bus menjadi BUS_IDLE dan dicek try_depart.

Urutan “turun dulu baru naik” terjamin karena try_start_load baru dipanggil setelah try_start_unload mengembalikan 0.

4.4 Bus tiba, batas 5 menit, berangkat (baris 470 sampai 524)

src/bus-sim.c baris 470-524

/* Event: bus arrival, 5-minute rule and departure. */
 
void event_bus_arrival(void)
{
    bus.status           = BUS_IDLE;
    bus.arrival_time     = sim_time;
    bus.min_stop_reached = 0;
 
    /* The bus stays at least 5 minutes. */
 
    event_schedule(sim_time + MIN_STOP_TIME, EVENT_MIN_STOP_REACHED);
 
    /* Unload first, then load. */
 
    if (!try_start_unload())
        try_start_load();
}
 
void event_min_stop_reached(void)
{
    bus.min_stop_reached = 1;
    try_depart();
}
 
/* Leave once 5 minutes have passed and no loading/unloading is in process.
   Called when the 5 minutes run out and whenever the bus becomes idle. */
void try_depart(void)
{
    if (bus.min_stop_reached && bus.status == BUS_IDLE)
        bus_depart();
}
 
void bus_depart(void)
{
    int here;
 
    here = bus.location;
 
    stats_bus_stop(here, sim_time - bus.arrival_time);
 
    /* A loop runs from one car-rental departure to the next. */
 
    if (here == LOC_CAR_RENTAL) {
        stats_bus_loop(sim_time - bus.last_rental_departure);
        bus.last_rental_departure = sim_time;
    }
 
    /* Drive to the next stop. */
 
    event_schedule(sim_time + locations[here].travel_time_to_next, EVENT_BUS_ARRIVAL);
    bus.location         = locations[here].next_stop;
    bus.status           = BUS_MOVING;
    bus.min_stop_reached = 0;
}
 
FungsiTugas
event_bus_arrivalbus tiba: catat arrival_time, min_stop_reached = 0, jadwalkan event batas 5 menit, lalu “turun dulu, kalau tak ada yang turun baru naik”
event_min_stop_reached5 menit sudah lewat: min_stop_reached = 1, lalu try_depart()
try_departberangkat hanya jika min_stop_reached dan status BUS_IDLE
bus_departcatat statistik (d) dan (e), jadwalkan bus tiba di lokasi berikutnya, ubah status menjadi BUS_MOVING

Di bus_depart:

stats_bus_stop(here, sim_time - bus.arrival_time);        /* hook (d): lama berhenti */
if (here == LOC_CAR_RENTAL) {
    stats_bus_loop(sim_time - bus.last_rental_departure); /* hook (e): lama satu putaran */
    bus.last_rental_departure = sim_time;
}

Kenapa batas 5 menit memakai event, bukan perbandingan waktu: membandingkan sim_time - arrival_time >= 5 rawan salah di batas karena pembulatan angka desimal (itu bug yang kita alami di versi lama). Event EVENT_MIN_STOP_REACHED mengisi flag secara pasti.

Kenapa keberangkatan bukan event: soal menyuruh bus berangkat “segera”, jadi cukup dicek setiap kali bus menjadi idle dan saat 5 menit habis (try_depart).

4.5 Daftar hook yang dipanggil model

HookDipanggil dariSaat
stats_queue_delaytry_start_loadorang diambil dari antrian
stats_bus_count_changedbus_board, bus_alightjumlah di bus berubah
stats_bus_stopbus_departbus berangkat dari sebuah lokasi
stats_bus_loopbus_departbus berangkat dari rental mobil
stats_time_in_systemevent_end_unloadorang selesai turun

Berkas 6 menjelaskan apa yang terjadi di dalam tiap hook.