C语言电梯模拟程序

C语言电梯模拟程序

一直以来我对电梯很感兴趣,起初认为用C语言不可能实现电梯模拟,需要多线程的支持,因此一直以来也没有想着做。最近数据结构习题书的这道题引起了我的注意,通过几天的努力终于实现了,先将程序的实现与大家分享出来。

在这个程序关键是处理好电梯运行状态转换与乘客进出的同步进行。好在题目要求每次输入时要输入下一个乘客到来的时间,使得程序变简单了。通过一个模拟时钟,根据模拟时钟判断该运行哪个函数。以下是模拟时钟的代码。

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++;
	}
}
在这个先不管TestPeople()、Controler()是什么,其中activity是关键,它是一个链表在链表的头部是计时器时间最小的函数,根据模拟时钟判断是否调用这个函数以及删除这个节点。以下是Activity的具体定义。

typedef struct Activity{
	int time;
	void(*fn)(void);
	struct Activity* next;
}Activity;
以及全局变量activity

Activity activity={0,NULL,NULL};
下面的代码用于将一个函数加入activity链表,这是根据时钟值从小到大插入activity的。

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 Input(void){//输入人员信息,这个需要手动调用一次,之后就根据定时器调用了 
	Person* p = (Person*)malloc(sizeof(Person));
	int infloor,outfloor,giveuptime,intertime;
	while(1){
		printf("请输入用户的起始楼层:");
		scanf("%d",&infloor);
		printf("请输入用户的目标的楼层:");
		scanf("%d",&outfloor);
		printf("请输入用户的最长容忍时间:");
		scanf("%d",&giveuptime);
		printf("请输入下一个用户的到来时间:");
		scanf("%d",&intertime);
		if(!(infloor<0||infloor>MaxFloor-1||outfloor<0||outfloor>MaxFloor-1)&&(infloor!=outfloor))
			break;
		printf("错误的用户信息录入!\n");
	}
	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);
}
这使得main函数可以写的很简单。

int main(){
	Init();
	Input();
	DoTime();
	return 0;
}
这样整个电梯程序大体就搭建起来了。下面看Controler()这个函数,它就是判断电梯的运行状态,然后决定电梯的下一个运行状态。AddAct()很重要,这很好的实现了一个需要时间的过程,如电梯的开门,上升,减速。

void Controler(void){
	if(State==Idle||State==Stop){ 
		if(CallUp[Floor]||CallDown[Floor]||CallCar[Floor]){
			//当前层有请求,需要开门进出
			if(CallCar[BaseFloor]==2){
				CallCar[BaseFloor]=0;
				State=Idle;
				printf("现在在%d层,无人请求电梯!\n",BaseFloor);
				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=Idle;
				if(Floor!=BaseFloor)
					AddAct(OverTime,tofirst);
			}
		}
	}
	//否则电梯忙碌 
	return;
}
下面是一些用到宏定义以及全局变量。

#define GoingUp 1//匀速上升
#define GoingDown 2//匀速下降
#define SpeedUp 3//加速上升
#define SpeedDown 4//加速下降
#define SlowUp 5//减速上升准备停靠
#define SlowDown 6//减速下降准备停靠
#define Idle 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 5
#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;
	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 Time=0;
int CallUp[MaxFloor]={0,};
int CallDown[MaxFloor]={0,};
int CallCar[MaxFloor]={0,};
int Floor=BaseFloor;
int State=Idle;
int PersonId=0;
Activity activity={0,NULL,NULL};
Person_Ele Stack[5]={0,};
Person Queue[5]={0,};
下面是Controler()具体调用细节。

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("电梯门关了\n");
	State=Stop;
}

void doopendoor(void){//打开电梯门 
	printf("电梯门开了\n");
	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("用户%d走出电梯\n",p->Id);
		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("用户%d走入电梯\n",p->Id);
		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("在选择方向时发生错误!\n");
	return old;
}

void tofirst(void){//去第一层
	if(State!=Idle||Floor==BaseFloor)
		return;
	printf("长时间没人请求电梯!将进入%d层\n",BaseFloor);
	CallCar[BaseFloor]=2;//给电梯一个虚拟的去1层的请求,这并不会开门 
}

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

void doup(void){ 
	Floor++;
	printf("电梯正在上升!现已到了%d层!\n",Floor);
	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("电梯正在下降!现已到了%d层!\n",Floor);
	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("电梯已加速上升!\n");
		State=GoingUp;
		AddAct(UpTime,doup);
	}else{
		printf("电梯已加速下降!\n");
		State=GoingDown;
		AddAct(DownTime,dodown);
	}
}
他们之间尽管没有直接调用关系,但都是通过AddAct()联系起来的。下面是TestPeople()的实现,我知道DoTime()每次都调用它严重影响效率,但是这需要修改AddAct(),增加复杂性。

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>=Idle))
					break;
				q->next=p->next;
				printf("用户%d放弃了等待!\n",p->Id);
				free(p);
				p=q->next;
				continue;
			}
			q=p;
			p=p->next;
		}
	}
}
下面是所有的源代码:

Dianti.h

#ifndef _DIANTI_H_
#define _DIANTI_H_
#include <stdio.h>
#include <stdlib.h>

#define GoingUp 1//匀速上升
#define GoingDown 2//匀速下降
#define SpeedUp 3//加速上升
#define SpeedDown 4//加速下降
#define SlowUp 5//减速上升准备停靠
#define SlowDown 6//减速下降准备停靠
#define Idle 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 5
#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;
	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);
#endif
Dianti.c

#include "Dianti.h"

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

int main(){
	Init();
	Input();
	DoTime();
	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>=Idle))
					break;
				q->next=p->next;
				printf("用户%d放弃了等待!\n",p->Id);
				free(p);
				p=q->next;
				continue;
			}
			q=p;
			p=p->next;
		}
	}
}



void Input(void){//输入人员信息,这个需要手动调用一次,之后就根据定时器调用了 
	Person* p = (Person*)malloc(sizeof(Person));
	int infloor,outfloor,giveuptime,intertime;
	while(1){
		printf("请输入用户的起始楼层:");
		scanf("%d",&infloor);
		printf("请输入用户的目标的楼层:");
		scanf("%d",&outfloor);
		printf("请输入用户的最长容忍时间:");
		scanf("%d",&giveuptime);
		printf("请输入下一个用户的到来时间:");
		scanf("%d",&intertime);
		if(!(infloor<0||infloor>MaxFloor-1||outfloor<0||outfloor>MaxFloor-1)&&(infloor!=outfloor))
			break;
		printf("错误的用户信息录入!\n");
	}
	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("电梯门关了\n");
	State=Stop;
}

void doopendoor(void){//打开电梯门 
	printf("电梯门开了\n");
	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("用户%d走出电梯\n",p->Id);
		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("用户%d走入电梯\n",p->Id);
		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("在选择方向时发生错误!\n");
	return old;
}

void tofirst(void){//去第一层
	if(State!=Idle||Floor==BaseFloor)
		return;
	printf("长时间没人请求电梯!将进入%d层\n",BaseFloor);
	CallCar[BaseFloor]=2;//给电梯一个虚拟的去1层的请求,这并不会开门 
}

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

void doup(void){ 
	Floor++;
	printf("电梯正在上升!现已到了%d层!\n",Floor);
	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("电梯正在下降!现已到了%d层!\n",Floor);
	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("电梯已加速上升!\n");
		State=GoingUp;
		AddAct(UpTime,doup);
	}else{
		printf("电梯已加速下降!\n");
		State=GoingDown;
		AddAct(DownTime,dodown);
	}
}

void Controler(void){
	if(State==Idle||State==Stop){ 
		if(CallUp[Floor]||CallDown[Floor]||CallCar[Floor]){
			//当前层有请求,需要开门进出
			if(CallCar[BaseFloor]==2){
				CallCar[BaseFloor]=0;
				State=Idle;
				printf("现在在%d层,无人请求电梯!\n",BaseFloor);
				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=Idle;
				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++;
	}
}
附程序运行结果:









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章