电梯模拟系统

模拟九层电梯系统。有七个状态:正在开门(Opening)、已开门(opened)、正在关门(closing)、已关门(closed)、等待(waiting)、移动(moving)、减速(Decelerate)。

对于每个乘客有一个最长容忍时间,超过这个候电梯时间,他就会放弃。

模拟始终从0开始,时间单位为0.1s。人和电梯各种动作均要消耗一定时间单位(简记为t),比如:

有人进出时,电梯每隔40t测试一次,若无人进出,则关门。

关门开门各需要20t

每隔人进出电梯均需要25t

电梯加速需要15t

上升时,每一层需要51t,减速需要14t

下降时,每一层需要61t,减速需要23t

如果电梯在某层超过300t,则回一层待命

要求:按时序显示系统状态变化过程,即发生的全部人和电梯动作序列

扩展要求:实现电梯模拟可视化界面

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#define GoingUp 1//匀速上升
#define GoingDown 2//匀速下降
#define SpeedUp 3//加速上升
#define SpeedDown 4//加速下降
#define SlowUp 5//减速上升准备停靠
#define SlowDown 6//减速下降准备停靠
#define Free 7//空闲
#define Stop 8//停止且已关门
#define DoorOpen 9//停止且门已打开
#define DoorOpening 10
#define DoorCloseing 11

#define	CloseTest 40	//电梯关门测试时间
#define OverTime  300	//电梯停候超时时间
#define Accelerate 15	//加速时间
#define UpTime	51	//上升时间
#define DownTime 61	//下降时间
#define UpDecelerate 14  	//上升减速
#define DownDecelerate 23	//下降减速
#define DoorTime	20	//开门关门时间
#define InOutTime	25	//进出电梯时间

#define MaxTime 10000  //最大容忍时间
#define MaxFloor 10   //最大层数9
#define BaseFloor 1

//初始每层电梯等待队列和栈 
/*#define Init() ({int i;\
	for(i=0;i<MaxFloor;i++){\
		Stack[i].next=NULL;\
		Queue[i].next=NULL;\
	}\
	activity.next=NULL;\
})*/
typedef struct Person {
	int Id;           //number
	int OutFloor;     //目标楼层
	int GiveupTime;   //容忍时间
	struct Person* next;
}Person;

typedef struct Activity {
	int time;
	void(*fn)(void);
	struct Activity* next;
}Activity;

typedef struct Person_Ele {
	int Id;
	struct Person_Ele* next;
}Person_Ele;

int AddQueue(int floor, struct Person* p); //加入相应层的客户等待队列 
void AddAct(int time, void(*fn)(void));
void TestPeople();  //检查是否有客户放弃等待
void DoTime();
void Input(void);

//电梯状态
void testinout(void);  //检测有无人进出 
void doclosedoor(void);  //电梯关门
void doopendoor(void);  //电梯开门
void doout(void); //出人
void doin(void); //进人
void doup(void); //上升
void dodown(void); //下降
void domove(void); //加速
void doslow(void); //停止
void tofirst();  //回一层
int GetWhere(void); //电梯行走方向



int Time = 0;
int CallUp[MaxFloor] = { 0, };
int CallDown[MaxFloor] = { 0, };
int CallCar[MaxFloor] = { 0, };
int Floor = BaseFloor;
int State = Free;
int PersonId = 0;
Activity activity = { 0,NULL,NULL };
Person_Ele Stack[5] = { 0, };
Person Queue[5] = { 0, };

int main() {
   //  Init();
    Input(); //输入用户信息
	DoTime();
	system("pause");
	return 0;
}

int AddQueue(int floor, Person* p) {//加入相应层的客户等待队列 
	Person* tmp = &Queue[floor];//这始终加在链表的最后一位, 
	while (tmp->next != NULL) {
		tmp = tmp->next;
	}
	tmp->next = p;
	return 0;
}

void AddAct(int time, void(*fn)(void)) {//将一个活动加入定时器,时间到了调用这个函数 
	time = Time + time;					//这个函数参数必须是void,返回值也必须是void
	struct Activity* act;
	act = (struct Activity*)malloc(sizeof(struct Activity));
	act->next = NULL;
	act->fn = fn;
	act->time = time;
	struct Activity* p = &activity;
	while (p->next != NULL) {
		if (p->next->time>time)
			break;
		p = p->next;
	}
	act->next = p->next;
	p->next = act;
}

void TestPeople() {//这是检测每层队列是否有人放弃,有人放弃就将他踢出队列 
	int i;      //每个时间都会被调用,效率相对较低 
	for (i = 0; i<MaxFloor; i++) {
		Person* p = Queue[i].next;
		Person* q = &Queue[i];
		if (p == NULL)
			continue;
		while (p != NULL) {
			if (p->GiveupTime <= Time) {
				if (Floor == i && (State >= Free))
					break;
				q->next = p->next;
				printf("User %d gave up and left   ——%d\n", p->Id,Time); //用户放弃等待,已离开
				free(p);
				p = q->next;
				continue;
			}
			q = p;
			p = p->next;
		}
	}
}

int eexit = 0;  //退出参数
int people = 0;
void Input(void) {//输入人员信息,这个需要手动调用一次,之后就根据定时器调用了 
	Person* p = (Person*)malloc(sizeof(Person));
	int infloor, outfloor, giveuptime, intertime;

	while (1) {
		printf("\n*********************************************\n");
		printf("      ▲ 请输入用户%d起始楼层:",people++);
		scanf("%d", &infloor);
		printf("      ▲ 请输入用户%d目标楼层:", --people);
		scanf("%d", &outfloor);
		printf("      ▲ 请输入用户%d最长容忍时间:", people++);
	    scanf("%d", &giveuptime);
		printf("      ▲ 请输入下一个用户的到来时间:");
		scanf("%d", &intertime);
		printf("      ▲ 退出--1,继续--2   ");
		scanf("%d", &eexit);
		if (eexit == 1)
			exit(0);  //退出函数
		printf("*********************************************\n\n");
		
		if (!(infloor<0 || infloor>MaxFloor - 1 || outfloor<0 || outfloor>MaxFloor - 1) && (infloor != outfloor))
			break;
		printf("录入错误请重输\n");
	}
	//giveuptime = OverTime;
	p->Id = PersonId++;
	p->GiveupTime = giveuptime + Time;
	p->next = NULL;
	p->OutFloor = outfloor;
	if (outfloor>infloor)
		CallUp[infloor] = 1;
	else
		CallDown[infloor] = 1;
	AddQueue(infloor, p);
	AddAct(intertime, Input);
}

void testinout(void) { //检测有无人进出 
	if (Queue[Floor].next || Stack[Floor].next)
		AddAct(CloseTest, testinout);
	else {
		State = DoorCloseing;
		CallUp[Floor] = 0;
		CallDown[Floor] = 0;
		CallCar[Floor] = 0;
		AddAct(DoorTime, doclosedoor);
	}
}

void doclosedoor(void) //关闭电梯门
{  
	printf("door's closed   ——%d\n\n",Time);
	State = Stop;
}

void doopendoor(void)  //打开电梯门 
{
	printf("\ndoor's opened   ——%d\n",Time);
	State = DoorOpen; //门打开了 
	AddAct(CloseTest, testinout);
	if (Stack[Floor].next)
		AddAct(InOutTime, doout);
	else 
	{ //没人出,就看有没有进的 
		if (Queue[Floor].next)
			AddAct(InOutTime, doin);
	}
}

void doout(void) {
	//根据栈出人,如果没有看是否有人进 
	if (Stack[Floor].next) {
		Person_Ele* p = Stack[Floor].next;
		Stack[Floor].next = p->next;
		//;//显示信息 
		printf("User %d ------- get out of the elevator   ——%d\n", p->Id, Time); //出电梯
		free(p);
	}
	if (Stack[Floor].next) {
		AddAct(InOutTime, doout);
	}
	else {
		if (Queue[Floor].next)
			AddAct(InOutTime, doin);
	}
}

void doin(void) {//人进入电梯,这里不用关电梯门它会定时关的 
	Person* p = Queue[Floor].next;
	if (p) {
		Queue[Floor].next = p->next;
		Person_Ele* pe = (Person_Ele*)malloc(sizeof(Person_Ele));
		int in = p->OutFloor;
		CallCar[in] = 1;//置位请求 
		pe->next = Stack[in].next;
		pe->Id = p->Id;
		Stack[in].next = pe;
		printf("User %d ------- get into the elevator   ——%d\n", p->Id,Time); //进电梯
		free(p);
	}
	if (Queue[Floor].next) {
		AddAct(InOutTime, doin);
	}
}

int GetWhere(void) {
	static int old = 0;//保存上一次电梯的方向,保证电梯尽可能在一个方向走 
	int isup = 0, isdown = 0;
	int i;
	for (i = Floor + 1; i<MaxFloor; i++) {
		if (CallDown[i] || CallUp[i] || CallCar[i])
			isup = 1;
	}
	for (i = Floor - 1; i >= 0; i--) {
		if (CallDown[i] || CallUp[i] || CallCar[i])
			isdown = 1;
	}
	if (isup == 0 && isdown == 0) {
		return 0;
	}
	if (old == 0) {
		if (isdown) old = GoingDown;
		if (isup) old = GoingUp;
		return old;
	}
	if (old == GoingUp&&isup)
		return old;
	else if (old == GoingDown&&isdown)
		return old;
	else if (isdown)
		old = GoingDown;
	else if (isup)
		old = GoingUp;
	else
		printf("Error!\n"); //在选择方向时发生错误
	return old;
}

void tofirst(void) {//回第一层
	if (State != Free || Floor == BaseFloor)
		return;
	printf("No one asked for a long time,ready to return to %d floor    ——%d\n", BaseFloor, --Time); Time--;//长时间没人请求电梯,将进入%d层
	CallCar[BaseFloor] = 2;//给电梯一个虚拟的去1层的请求,不会开门 
}

void doslow(void) {//电梯停了 
	printf("Stop ------%d    ——%d\n", Floor,Time--);  //电梯停了,当前层是%d
	State = Stop;
}

void doup(void) { //电梯上升
	Floor++;
	printf("going up --------%d    ——%d\n", Floor,Time);
	if (CallDown[Floor] || CallUp[Floor] || CallCar[Floor]) {
		State = SlowUp;
		AddAct(UpDecelerate, doslow);
	}
	else {
		if (Floor == MaxFloor - 1) {
			State = SlowUp;
			AddAct(UpDecelerate, doslow);
		}
		else {
			AddAct(UpTime, doup);
		}
	}
}

void dodown(void) {  //电梯下降
	Floor--;
	printf("going down ------%d    ——%d\n", Floor,--Time);
	if (CallUp[Floor] || CallDown[Floor] || CallCar[Floor]) {
		State = SlowDown;
		AddAct(DownDecelerate, doslow);
	}
	else {
		if (Floor == 0) {
			State = SlowDown;
			AddAct(DownDecelerate, doslow);
		}
		else {
			AddAct(DownTime, dodown);
		}
	}
}

void domove(void) {//加速完成,将进入正常速度 
	if (State == SpeedUp) {
		printf("Accelerating up    ——%d\n",--Time); //电梯加速上升
		State = GoingUp;
		AddAct(UpTime, doup);
	}
	else {
		printf("Accelerating Down    ——%d\n",--Time);  //电梯加速下降
		State = GoingDown;
		AddAct(DownTime, dodown);
	}
}

void Controler(void) {
	if (State == Free || State == Stop) {
		if (CallUp[Floor] || CallDown[Floor] || CallCar[Floor]) {
			//当前层有请求,需要开门进出
			if (CallCar[BaseFloor] == 2) //在一楼
			{
				CallCar[BaseFloor] = 0;
				State = Free;
				printf("Nobody asked ------%d    ——%d\n", BaseFloor,Time--);  //现在在%d层,无人请求电梯
				return;
			}
			State = DoorOpening;
			AddAct(DoorTime, doopendoor);
		}
		else {
			//当前层无请求,判断其他层请求
			int whitch = GetWhere();
			if (whitch == GoingUp) {
				State = SpeedUp;
				AddAct(Accelerate, domove);
			}
			else if (whitch == GoingDown) {
				State = SpeedDown;
				AddAct(Accelerate, domove);
			}
			else {
				State = Free;
				if (Floor != BaseFloor)
					AddAct(OverTime, tofirst);
			}
		}
	}
	//否则电梯忙碌 
	return;
}

void DoTime() {
	//此函数用于模拟时钟 
	while (1) {
		if (Time>MaxTime)
			return;
		TestPeople(); //两个始终都会被调用的函数 
		Controler();
		struct Activity* p = activity.next;
		if (p == NULL) {
			Time = MaxTime;
		}
		if (p&&Time >= p->time) {//取出活动队头的,检测定时是否到了 
			activity.next = p->next;
			p->fn();
			free(p);
		}
		Time++;
	}
}

八、 实验内容和要求: 要求根据下面的功能说明描述实现模拟电梯控制软件 (一)电梯配置 1. 共有1个电梯 2. 共有maxfloor层楼层。maxfloor=9。 3. 间层每层有上下两个按钮,最下层只有上行按钮,最上层只有上行按钮。每层都有相应的指示灯,灯亮表示该按钮已经被按下,如果该层的上行或者下行请求已经被响应,则指示灯灭 4. 电梯内共有maxfloor个目标按钮,表示有乘客在该层下电梯。有指示灯指示按钮是否被按下。乘客按按钮导致按钮指示灯亮,如果电梯已经在该层停靠则该按钮指示灯灭 5. 另有一启动按钮(GO)。当电梯停在某一楼层后,接受到GO信息就继续运行。如果得不到GO信息,等待一段时间也自动继续运行。 6. 电梯内设有方向指示灯表示当前电梯运行方向。 说明:由于本次实验不使用可视化框架,所以无法作到从图形界面上获取按钮请求。因此电梯按钮的设计,不在图形界面上体现,仅用来设计键盘的模拟输入。 (二)电梯的运行控制 1.电梯的初始状态是电梯位于第一层处,所有按钮都没有按下。 2.乘客可以在任意时刻按任何一个目标钮和呼叫钮。呼叫和目标对应的楼层可能不是电梯当前运行方向可达的楼层。 3. 如果电梯正在向I层驶来,并且位于I层与相邻层(向上运行时是I-1层或者向下运行时是I+1层)之间,则因为安全考虑不响应此时出现的I层目标或者请求。如果电梯正好经过了I楼层,运行在I楼层和下一楼层之间,则为了直接响应此时出现的I层目标或者请求,必须至少到达运行方向上的下一楼层然后才能掉头到达I楼层(假设掉头无须其它额外时间),如果I楼层不是刚刚经过的楼层则可以在任意位置掉头,此时掉头后经过的第一个楼层不可停。 4. 电梯系统依照某种预先定义好的策略对随机出现的呼叫和目标进行分析和响应。 5. 乘客数量等外界因素(可能导致停靠时间的长短变化)不予考虑。假设电梯正常运行一层的时间是5S,停靠目标楼层、上下乘客和电梯继续运行的时间是5S。 6. 当电梯停靠某层时,该层的乘客如果错误的按目标或呼叫按钮都不予响应。 7. 电梯停要某一层后,苦无目标和呼叫,则电梯处于无方向状态,方向指示灯全灭,否则电梯内某个方向的指示灯亮,表示电梯将向该方向运行。等接到“GO”信号后电梯立即继续运行。若无GO信号,则电梯在等了上下乘客和电梯继续运行时间后也将继续运行。 8. 当一个目标(呼叫)已经被服务后,应将对应的指示灯熄灭。 (三)电梯运行的控制策略 以下是几个候选策略: 1.先来先服务策略: 将所有呼叫和目标按到达时间排队,然后一一完成。这是相当简单的策略,只需要设计一个将呼叫和目标排队的数据结构。因为该策略效率也很低,所以没有实际的电梯采用这种策略。 2. 顺便服务策略: 顺便服务是一种最常见的简单策略。这种策略在运行控制所规定的安全前提下,一次将一个方向上的所有呼叫和目标全部完成。然后掉转运行方向完成另外一个方向上的所有呼叫和目标。 可以采用设定目标楼层的办法来实现这个策略,即电梯向一个目标楼层运行,但这个楼层可以修改。具体策略如下: 1) 修改目标楼层的策略: a.如果电梯运行方向向上,那么如果新到一个介于当前电梯所处楼层和目标楼层之间,又可以安全到达的向上呼叫或者目标,将目标楼层修改为这个新的楼层。 b.如果电梯运行方向向下,那么如果新到一个介于当前电梯所处楼层和目标楼层之间,又可以安全到达的向下呼叫或者目标,将目标楼层修改为这个新的楼层。 2)确定新的目标楼层: 如果电梯向上运行,当它到达某个目标楼层后,则依照以下顺序确定下一个目标楼层: a.如果比当前层高的楼层有向上呼叫或者目标,那么以最低的高于当前楼层的有向上呼叫或者目标的楼层为目标。 b.如果无法确定目标楼层,那么以最高的向下呼叫或者目标所在楼层为电梯当前目标楼层。 c.如果无法确定目标楼层,那么以最低的向上呼叫所在楼层为电梯当前的目标楼层。 d.如果仍然不能确定目标楼层(此时实际上没有任何呼叫和目标),那么电梯无目标,运行暂停。 如果电梯向下运行,依照以下顺序确定下一目标楼层: a.如果比当前层低的楼层有向下呼叫或者目标,那么以最高的低于当前楼层的有向下呼叫或者目标的楼层为目标。 b.如果无法确定目标楼层,那么以最低的向上呼叫或者目标所在楼层为电梯当前目标楼层。 c.如果无法确定目标楼层,那么以最高的向下呼叫楼层为目标楼层。 d.如果仍然不能确定目标楼层(此时实际上没有任何呼叫和目标),那么电梯无目标,运行暂停。 3)最快响应策略: 响应所有的现在存在的所有呼叫和目标所需时间(采用不同方案电梯停靠时间相同,所以不必考虑)最短的策略。 可选方案一是电梯先向上运行响应经过各层的目标和向上呼叫,再向下运行响应所有向下呼叫以及途经各层的目标,最后再向上响应剩余的向上呼叫。二是恰好相反,
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值