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();
}
| Sintaks | Arti |
|---|---|
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();
}
| Sintaks | Arti |
|---|---|
list_size[LIST_QUEUE(bus.location)] == 0 | antrian 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;
}
| Fungsi | Tugas |
|---|---|
event_bus_arrival | bus 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_reached | 5 menit sudah lewat: min_stop_reached = 1, lalu try_depart() |
try_depart | berangkat hanya jika min_stop_reached dan status BUS_IDLE |
bus_depart | catat 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
| Hook | Dipanggil dari | Saat |
|---|---|---|
stats_queue_delay | try_start_load | orang diambil dari antrian |
stats_bus_count_changed | bus_board, bus_alight | jumlah di bus berubah |
stats_bus_stop | bus_depart | bus berangkat dari sebuah lokasi |
stats_bus_loop | bus_depart | bus berangkat dari rental mobil |
stats_time_in_system | event_end_unload | orang selesai turun |
Berkas 6 menjelaskan apa yang terjadi di dalam tiap hook.