159.341_Assignment_2/assignment2_handout.c

454 lines
14 KiB
C
Raw Normal View History

2021-04-21 14:25:20 +12:00
// --------------------------------------------------
// --- 159.341 Assignment 2 - Lift Simulator ---
// --------------------------------------------------
// Written by M. J. Johnson
// Updated by D. P. Playne - 2019
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include "lift.h"
// --------------------------------------------------
// Define Problem Size
// --------------------------------------------------
2021-04-28 17:54:37 +12:00
#define NLIFTS 4 // The number of lifts in the building
2021-04-21 14:25:20 +12:00
#define NFLOORS 20 // The number of floors in the building
2021-04-28 17:54:37 +12:00
#define NPEOPLE 3 // The number of people in the building
2021-04-21 14:25:20 +12:00
#define MAXNOINLIFT 10 // Maximum number of people in a lift
// --------------------------------------------------
// Define delay times (in milliseconds)
// --------------------------------------------------
2021-04-27 20:34:24 +12:00
//#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
2021-04-21 14:25:20 +12:00
#define PEOPLESPEED 100 // The maximum time a person spends on a floor
#elif defined(FAST)
2021-04-27 20:34:24 +12:00
#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)
2021-04-21 14:25:20 +12:00
#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
2021-04-27 17:08:11 +12:00
semaphore queueInteraction; // People can't all press a button at the same time
2021-04-27 20:34:24 +12:00
// 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;
2021-04-21 14:25:20 +12:00
} 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
2021-04-27 20:34:24 +12:00
// Semaphore stating if a thread is currently loading/unloading someone from the lift
2021-04-28 17:54:37 +12:00
//semaphore loading;
2021-04-27 20:34:24 +12:00
// If the target floor is being selected (only one can press at a time)
2021-04-28 17:54:37 +12:00
//semaphore buttonPress;
2021-04-21 14:25:20 +12:00
} lift_info;
// --------------------------------------------------
// Some global variables
// --------------------------------------------------
floor_info floors[NFLOORS];
2021-04-27 17:08:11 +12:00
lift_info* targetLift[NFLOORS];
// Bound all printing to this semaphore
semaphore printSemaphore;
2021-04-21 14:25:20 +12:00
// --------------------------------------------------
// Print a string on the screen at position (x,y)
// --------------------------------------------------
void print_at_xy(int x, int y, const char *s) {
2021-04-27 20:34:24 +12:00
// Lock our print to just one thread (stops x,y from changing)
2021-04-27 17:08:11 +12:00
semaphore_wait(&printSemaphore);
2021-04-21 14:25:20 +12:00
// Move cursor to (x,y)
gotoxy(x,y);
// Slow things down
2021-04-28 17:54:37 +12:00
Sleep(0);
2021-04-21 14:25:20 +12:00
// Print the string
printf("%s", s);
// Move cursor out of the way
gotoxy(42, NFLOORS+2);
2021-04-27 20:34:24 +12:00
// flush the stream and release the semaphore
2021-04-27 17:08:11 +12:00
fflush(stdout);
semaphore_signal(&printSemaphore);
2021-04-21 14:25:20 +12:00
}
// --------------------------------------------------
// 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;
2021-04-21 14:25:20 +12:00
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;
2021-04-27 20:34:24 +12:00
inprogress = &floors[lift->position].inprogress_up;
2021-04-21 14:25:20 +12:00
} else {
// Use down_arrow semaphore
s = &floors[lift->position].down_arrow;
// Number of people waiting to go down
waiting = &floors[lift->position].waitingtogodown;
2021-04-27 20:34:24 +12:00
inprogress = &floors[lift->position].inprogress_down;
2021-04-21 14:25:20 +12:00
}
2021-04-27 20:34:24 +12:00
// 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.
2021-04-21 14:25:20 +12:00
// 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;
}
2021-04-27 20:34:24 +12:00
// 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);
2021-04-21 14:25:20 +12:00
// Add person to the lift
lift->peopleinlift++;
print_at_xy(NLIFTS*4+floors[lift->position].waitingtogodown + floors[lift->position].waitingtogoup, NFLOORS-lift->position, " ");
2021-04-27 20:34:24 +12:00
// 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
2021-04-21 14:25:20 +12:00
// One less person waiting
2021-04-27 20:34:24 +12:00
// Note that while loading, the queue cannot change
//semaphore_wait(&floors[lift->position].queueInteraction);
2021-04-21 14:25:20 +12:00
(*waiting)--;
2021-04-27 20:34:24 +12:00
(*inprogress)--;
semaphore_signal(&floors[lift->position].queueInteraction);
2021-04-21 14:25:20 +12:00
// Wait for person to get into lift
Sleep(GETINSPEED);
2021-04-28 17:54:37 +12:00
// Need a sem here
2021-04-27 17:08:11 +12:00
targetLift[lift->position] = lift;
semaphore_signal(s);
2021-04-21 14:25:20 +12:00
} else {
2021-04-27 20:34:24 +12:00
semaphore_signal(&floors[lift->position].queueInteraction);
2021-04-21 14:25:20 +12:00
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
2021-04-27 20:34:24 +12:00
// lift is ok with loading a single person
2021-04-28 17:54:37 +12:00
//semaphore_create(&lift.loading, 1);
//semaphore_create(&lift.buttonPress, 1);
2021-04-27 17:08:11 +12:00
2021-04-21 14:25:20 +12:00
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
2021-04-28 17:54:37 +12:00
while(true) {
2021-04-21 14:25:20 +12:00
// 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
2021-04-28 17:54:37 +12:00
//semaphore_wait(&lift.loading);
2021-04-21 14:25:20 +12:00
lift.peopleinlift--;
2021-04-28 17:54:37 +12:00
//semaphore_signal(&lift.loading);
2021-04-21 14:25:20 +12:00
// One less waiting to get off at this floor
2021-04-27 20:34:24 +12:00
// Don't need a semaphore as nothing should read this value now
2021-04-28 17:54:37 +12:00
//semaphore_wait(&lift.buttonPress);
2021-04-21 14:25:20 +12:00
lift.stops[lift.position]--;
2021-04-28 17:54:37 +12:00
//semaphore_signal(&lift.buttonPress);
2021-04-27 20:34:24 +12:00
2021-04-21 14:25:20 +12:00
// Wait for exit lift delay
Sleep(GETOUTSPEED);
2021-04-27 17:08:11 +12:00
semaphore_signal(&lift.stopsem[lift.position]);
2021-04-21 15:16:15 +12:00
// Signal passenger to leave lift
2021-04-21 14:25:20 +12:00
// 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, " ");
2021-04-27 20:34:24 +12:00
2021-04-21 14:25:20 +12:00
}
}
// 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
2021-04-28 17:54:37 +12:00
while(true) {
2021-04-21 14:25:20 +12:00
// Work for a while
Sleep(rnd(PEOPLESPEED));
do {
// Randomly pick another floor to go to
to = rnd(NFLOORS);
} while(to == from);
2021-04-27 17:08:11 +12:00
semaphore* s;
// Wait for our turn to press a button
2021-04-28 17:54:37 +12:00
2021-04-21 14:25:20 +12:00
// Check which direction the person is going (UP/DOWN)
if(to > from) {
// One more person waiting to go up
2021-04-28 17:54:37 +12:00
semaphore_wait(&floors[from].queueInteraction);
2021-04-21 14:25:20 +12:00
floors[from].waitingtogoup++;
2021-04-27 20:34:24 +12:00
//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);
2021-04-27 17:08:11 +12:00
semaphore_signal(&floors[from].queueInteraction);
2021-04-21 14:25:20 +12:00
// Print person waiting
2021-04-27 17:08:11 +12:00
s = &floors[from].up_arrow;
// Wait for a lift to arrive (going up)
2021-04-21 14:25:20 +12:00
} else {
2021-04-28 17:54:37 +12:00
semaphore_wait(&floors[from].queueInteraction);
2021-04-21 14:25:20 +12:00
// One more person waiting to go down
floors[from].waitingtogodown++;
2021-04-27 20:34:24 +12:00
//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);
2021-04-27 17:08:11 +12:00
semaphore_signal(&floors[from].queueInteraction);
2021-04-21 14:25:20 +12:00
// Print person waiting
2021-04-27 20:34:24 +12:00
// We want this to happen after the semaphore is released, so the person is added after any deletions
2021-04-27 17:08:11 +12:00
s = &floors[from].down_arrow;
// Wait for a lift to arrive (going down)
2021-04-21 14:25:20 +12:00
}
2021-04-27 17:08:11 +12:00
// We've pressed our button
semaphore_wait(s);
2021-04-21 14:25:20 +12:00
// Which lift we are getting into
2021-04-27 17:08:11 +12:00
lift = targetLift[from];
2021-04-21 14:25:20 +12:00
// Add one to passengers waiting for floor
2021-04-27 17:08:11 +12:00
// Only one person enters at a time
2021-04-28 17:54:37 +12:00
//semaphore_wait(&lift->buttonPress);
2021-04-21 14:25:20 +12:00
lift->stops[to]++;
2021-04-28 17:54:37 +12:00
//semaphore_signal(&lift->buttonPress);
2021-04-21 14:25:20 +12:00
// 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, "-");
}
2021-04-27 17:08:11 +12:00
semaphore_wait(&lift->stopsem[to]);
// Wait until we are at the right floor
2021-04-21 14:25:20 +12:00
// Exit the lift
from = to;
}
return NULL;
}
// --------------------------------------------------
// Print the building on the screen
// --------------------------------------------------
void printbuilding(void) {
2021-04-27 17:08:11 +12:00
2021-04-21 14:25:20 +12:00
// Local variables
int l,f;
// Clear Screen
system(clear_screen);
2021-04-27 17:08:11 +12:00
// Print the whole building before anything else
2021-04-21 14:25:20 +12:00
// 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);
2021-04-27 17:08:11 +12:00
2021-04-21 14:25:20 +12:00
// 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");
2021-04-27 17:08:11 +12:00
// Ensure buffer is written
fflush(stdout);
2021-04-27 20:34:24 +12:00
// Once the building has been printed, then we can those waiting to print that they may do so
2021-04-27 17:08:11 +12:00
semaphore_signal(&printSemaphore);
2021-04-21 14:25:20 +12:00
}
2021-04-27 20:34:24 +12:00
// --------------------------------------------------
// Iterate through the floors and update the queue size
// --------------------------------------------------
void* update_floor_counts(void *p){
2021-04-28 17:54:37 +12:00
while(true){
2021-04-27 20:34:24 +12:00
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);
}
}
2021-04-28 17:54:37 +12:00
Sleep(0);
2021-04-27 20:34:24 +12:00
}
2021-04-21 14:25:20 +12:00
// --------------------------------------------------
// Main starts the threads and then waits.
// --------------------------------------------------
int main() {
// Local variables
unsigned long i;
2021-04-27 20:34:24 +12:00
semaphore_create(&printSemaphore, 0);
2021-04-21 14:25:20 +12:00
// Initialise Building
for(i = 0; i < NFLOORS; i++) {
// Initialise Floor
2021-04-27 20:34:24 +12:00
floors[i].inprogress_down = 0;
floors[i].inprogress_up = 0;
2021-04-21 14:25:20 +12:00
floors[i].waitingtogoup = 0;
floors[i].waitingtogodown = 0;
semaphore_create(&floors[i].up_arrow, 0);
semaphore_create(&floors[i].down_arrow, 0);
2021-04-27 17:08:11 +12:00
semaphore_create(&floors[i].queueInteraction, 1); // This initially has one available button press
2021-04-21 14:25:20 +12:00
}
// --- 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);
}
2021-04-27 20:34:24 +12:00
//create_thread(update_floor_counts, (void*)i);
2021-04-21 14:25:20 +12:00
// Go to sleep for 86400 seconds (one day)
Sleep(86400000ULL);
}