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++;
	}
}
附程序運行結果:









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