// -------------------------------------------------- // --- 159.341 Assignment 2 - Lift Simulator --- // -------------------------------------------------- // Written by M. J. Johnson // Updated by D. P. Playne - 2019 #include #include #include #include "lift.h" // -------------------------------------------------- // Define Problem Size // -------------------------------------------------- #define NLIFTS 4 // The number of lifts in the building #define NFLOORS 20 // The number of floors in the building #define NPEOPLE 3 // The number of people in the building #define MAXNOINLIFT 10 // Maximum number of people in a lift // -------------------------------------------------- // Define delay times (in milliseconds) // -------------------------------------------------- //#define SUPERSLOW //#define SLOW //#define FAST #define SUPERFAST #if defined(SUPERSLOW) #define LIFTSPEED 500 // The time it takes for the lift to move one floor #define GETINSPEED 250 // The time it takes to get into the lift #define GETOUTSPEED 250 // The time it takes to get out of the lift #define PEOPLESPEED 10000 // The maximum time a person spends on a floor #elif defined(SLOW) #define LIFTSPEED 250 // The time it takes for the lift to move one floor #define GETINSPEED 100 // The time it takes to get into the lift #define GETOUTSPEED 100 // The time it takes to get out of the lift #define PEOPLESPEED 100 // The maximum time a person spends on a floor #elif defined(FAST) #define LIFTSPEED 25 // The time it takes for the lift to move one floor #define GETINSPEED 10 // The time it takes to get into the lift #define GETOUTSPEED 10 // The time it takes to get out of the lift #define PEOPLESPEED 10 // The maximum time a person spends on a floor #elif defined(SUPERFAST) #define LIFTSPEED 0 // The time it takes for the lift to move one floor #define GETINSPEED 0 // The time it takes to get into the lift #define GETOUTSPEED 0 // The time it takes to get out of the lift #define PEOPLESPEED 1 // The maximum time a person spends on a floor #endif // -------------------------------------------------- // Define lift directions (UP/DOWN) // -------------------------------------------------- #define UP 1 #define DOWN -1 // -------------------------------------------------- // Information about a floor in the building // -------------------------------------------------- typedef struct { int waitingtogoup; // The number of people waiting to go up int waitingtogodown; // The number of people waiting to go down semaphore up_arrow; // People going up wait on this semaphore down_arrow; // People going down wait on this semaphore queueInteraction; // People can't all press a button at the same time // Add a tracker to include how many people are currently moving into/out of a lift. // These people are still considered to be waiting, but a thread is in the process of loading them int inprogress_up; int inprogress_down; } floor_info; // -------------------------------------------------- // Information about a lift // -------------------------------------------------- typedef struct { int no; // The lift number (id) int position; // The floor it is on int direction; // Which direction it is going (UP/DOWN) int peopleinlift; // The number of people in the lift int stops[NFLOORS]; // How many people are going to each floor semaphore stopsem[NFLOORS]; // People in the lift wait on one of these // Semaphore stating if a thread is currently loading/unloading someone from the lift //semaphore loading; // If the target floor is being selected (only one can press at a time) //semaphore buttonPress; } lift_info; // -------------------------------------------------- // Some global variables // -------------------------------------------------- floor_info floors[NFLOORS]; lift_info* targetLift[NFLOORS]; // Bound all printing to this semaphore semaphore printSemaphore; // -------------------------------------------------- // Print a string on the screen at position (x,y) // -------------------------------------------------- void print_at_xy(int x, int y, const char *s) { // Lock our print to just one thread (stops x,y from changing) semaphore_wait(&printSemaphore); // Move cursor to (x,y) gotoxy(x,y); // Slow things down Sleep(0); // Print the string printf("%s", s); // Move cursor out of the way gotoxy(42, NFLOORS+2); // flush the stream and release the semaphore fflush(stdout); semaphore_signal(&printSemaphore); } // -------------------------------------------------- // Function for a lift to pick people waiting on a // floor going in a certain direction // -------------------------------------------------- void get_into_lift(lift_info *lift, int direction) { // Local variables int *waiting; int *inprogress; semaphore *s; // Check lift direction if(direction==UP) { // Use up_arrow semaphore s = &floors[lift->position].up_arrow; // Number of people waiting to go up waiting = &floors[lift->position].waitingtogoup; inprogress = &floors[lift->position].inprogress_up; } else { // Use down_arrow semaphore s = &floors[lift->position].down_arrow; // Number of people waiting to go down waiting = &floors[lift->position].waitingtogodown; inprogress = &floors[lift->position].inprogress_down; } // Problems here are primarily state consistency. Between the start of the conditional, the number of waiting people can change, resulting // in the number of waiting people dropping below zero. // For all the people waiting while(*waiting) { // Check if lift is empty if(lift->peopleinlift == 0) { // Set the direction of the lift lift->direction = direction; } // Check there are people waiting and lift isn't full. Ignore in-progress (moving into lift) people // must check here semaphore_wait(&floors[lift->position].queueInteraction); if(lift->peopleinlift < MAXNOINLIFT && *waiting - *inprogress) { //volatile int pos = floors[lift->position].waitingtogodown + floors[lift->position].waitingtogoup - (*inprogress); (*inprogress)++; //semaphore_signal(&floors[lift->position].queueInteraction); // Add person to the lift lift->peopleinlift++; print_at_xy(NLIFTS*4+floors[lift->position].waitingtogodown + floors[lift->position].waitingtogoup, NFLOORS-lift->position, " "); // Capture the person's printed position while we have the //semaphore_signal(&floors[lift->position].queueInteraction); // Do the print, no need to hold the semaphore while this is going on // One less person waiting // Note that while loading, the queue cannot change //semaphore_wait(&floors[lift->position].queueInteraction); (*waiting)--; (*inprogress)--; semaphore_signal(&floors[lift->position].queueInteraction); // Wait for person to get into lift Sleep(GETINSPEED); // Need a sem here targetLift[lift->position] = lift; semaphore_signal(s); } else { semaphore_signal(&floors[lift->position].queueInteraction); break; } } } // -------------------------------------------------- // Function for the Lift Threads // -------------------------------------------------- void* lift_thread(void *p) { // Local variables lift_info lift; int no = (long long)p; int i; // Set up Lift lift.no = no; // Set lift number lift.position = 0; // Lift starts on ground floor lift.direction = UP; // Lift starts going up lift.peopleinlift = 0; // Lift starts empty // lift is ok with loading a single person //semaphore_create(&lift.loading, 1); //semaphore_create(&lift.buttonPress, 1); for(i = 0; i < NFLOORS; i++) { lift.stops[i]=0; // No passengers waiting semaphore_create(&lift.stopsem[i], 0); // Initialise semaphores } // Randomise lift randomise(); // Wait for random start up time (up to a second) Sleep(rnd(1000)); // Loop forever while(true) { // Print current position of the lift print_at_xy(no*4+1, NFLOORS-lift.position, lf); // Wait for a while Sleep(LIFTSPEED); // Drop off passengers on this floor while (lift.stops[lift.position] != 0) { // One less passenger in lift //semaphore_wait(&lift.loading); lift.peopleinlift--; //semaphore_signal(&lift.loading); // One less waiting to get off at this floor // Don't need a semaphore as nothing should read this value now //semaphore_wait(&lift.buttonPress); lift.stops[lift.position]--; //semaphore_signal(&lift.buttonPress); // Wait for exit lift delay Sleep(GETOUTSPEED); semaphore_signal(&lift.stopsem[lift.position]); // Signal passenger to leave lift // Check if that was the last passenger waiting for this floor if(!lift.stops[lift.position]) { // Clear the "-" print_at_xy(no*4+1+2, NFLOORS-lift.position, " "); } } // Check if lift is going up or is empty if(lift.direction==UP || !lift.peopleinlift) { // Pick up passengers waiting to go up get_into_lift(&lift, UP); } // Check if lift is going down or is empty if(lift.direction==DOWN || !lift.peopleinlift) { // Pick up passengers waiting to go down get_into_lift(&lift, DOWN); } // Erase lift from screen print_at_xy(no*4+1, NFLOORS-lift.position, (lift.direction + 1 ? " " : lc)); // Move lift lift.position += lift.direction; // Check if lift is at top or bottom if(lift.position == 0 || lift.position == NFLOORS-1) { // Change lift direction lift.direction = -lift.direction; } } return NULL; } // -------------------------------------------------- // Function for the Person Threads // -------------------------------------------------- void* person_thread(void *p) { // Local variables int from = 0, to; // Start on the ground floor lift_info *lift; // Randomise randomise(); // Stay in the building forever while(true) { // Work for a while Sleep(rnd(PEOPLESPEED)); do { // Randomly pick another floor to go to to = rnd(NFLOORS); } while(to == from); semaphore* s; // Wait for our turn to press a button // Check which direction the person is going (UP/DOWN) if(to > from) { // One more person waiting to go up semaphore_wait(&floors[from].queueInteraction); floors[from].waitingtogoup++; //volatile int pos = floors[from].waitingtogoup +floors[from].waitingtogodown - floors[from].inprogress_down - floors[from].inprogress_up; print_at_xy(NLIFTS*4+ floors[from].waitingtogoup +floors[from].waitingtogodown,NFLOORS-from, pr); semaphore_signal(&floors[from].queueInteraction); // Print person waiting s = &floors[from].up_arrow; // Wait for a lift to arrive (going up) } else { semaphore_wait(&floors[from].queueInteraction); // One more person waiting to go down floors[from].waitingtogodown++; //volatile int pos = floors[from].waitingtogoup +floors[from].waitingtogodown - floors[from].inprogress_down - floors[from].inprogress_up; print_at_xy(NLIFTS*4+floors[from].waitingtogoup +floors[from].waitingtogodown,NFLOORS-from, pr); semaphore_signal(&floors[from].queueInteraction); // Print person waiting // We want this to happen after the semaphore is released, so the person is added after any deletions s = &floors[from].down_arrow; // Wait for a lift to arrive (going down) } // We've pressed our button semaphore_wait(s); // Which lift we are getting into lift = targetLift[from]; // Add one to passengers waiting for floor // Only one person enters at a time //semaphore_wait(&lift->buttonPress); lift->stops[to]++; //semaphore_signal(&lift->buttonPress); // Press button if we are the first if(lift->stops[to]==1) { // Print light for destination print_at_xy(lift->no*4+1+2, NFLOORS-to, "-"); } semaphore_wait(&lift->stopsem[to]); // Wait until we are at the right floor // Exit the lift from = to; } return NULL; } // -------------------------------------------------- // Print the building on the screen // -------------------------------------------------- void printbuilding(void) { // Local variables int l,f; // Clear Screen system(clear_screen); // Print the whole building before anything else // Print Roof printf("%s", tl); for(l = 0; l < NLIFTS-1; l++) { printf("%s%s%s%s", hl, td, hl, td); } printf("%s%s%s%s\n", hl, td, hl, tr); // Print Floors and Lifts for(f = NFLOORS-1; f >= 0; f--) { for(l = 0; l < NLIFTS; l++) { printf("%s%s%s ", vl, lc, vl); if(l == NLIFTS-1) { printf("%s\n", vl); } } } // Print Ground printf("%s", bl); for(l = 0; l < NLIFTS-1; l++) { printf("%s%s%s%s", hl, tu, hl, tu); } printf("%s%s%s%s\n", hl, tu, hl, br); // Print Message printf("Lift Simulation - Press CTRL-Break to exit\n"); // Ensure buffer is written fflush(stdout); // Once the building has been printed, then we can those waiting to print that they may do so semaphore_signal(&printSemaphore); } // -------------------------------------------------- // Iterate through the floors and update the queue size // -------------------------------------------------- void* update_floor_counts(void *p){ while(true){ int i; for (i = 0; i < NFLOORS; i++){ char str[12]; sprintf(str, "%d", floors[i].waitingtogodown + floors[i].waitingtogoup); print_at_xy(4*NLIFTS+NPEOPLE, NFLOORS-i, " "); print_at_xy(4*NLIFTS+NPEOPLE, NFLOORS-i, str); } } Sleep(0); } // -------------------------------------------------- // Main starts the threads and then waits. // -------------------------------------------------- int main() { // Local variables unsigned long i; semaphore_create(&printSemaphore, 0); // Initialise Building for(i = 0; i < NFLOORS; i++) { // Initialise Floor floors[i].inprogress_down = 0; floors[i].inprogress_up = 0; floors[i].waitingtogoup = 0; floors[i].waitingtogodown = 0; semaphore_create(&floors[i].up_arrow, 0); semaphore_create(&floors[i].down_arrow, 0); semaphore_create(&floors[i].queueInteraction, 1); // This initially has one available button press } // --- Initialise any other semaphores --- // Print Building printbuilding(); // Create Lifts for(i = 0; i < NLIFTS; i++) { // Create Lift Thread create_thread(lift_thread, (void*)i); } // Create People for(i = 0; i < NPEOPLE; i++) { // Create Person Thread create_thread(person_thread, (void*)i); } //create_thread(update_floor_counts, (void*)i); // Go to sleep for 86400 seconds (one day) Sleep(86400000ULL); }