目錄
一、課程設計題目與要求
1、題目內容
模擬某校九層教學樓的電梯系統。該樓有一個自動電梯,能在每層停留,其中第一層是大樓的進出層,即是電梯的“本壘層”,電梯“空閒”時,將來到該層候命。
電梯一共有七個狀態,即正在開門(Opening)、已開門(Opened)、正在關門(Closing)、
已關門(Closed)、等待(Waiting)、移動(Moving)、減速(Decelerate)。
乘客可隨機地進出於任何層。對每個人來說,他有一個能容忍的最長等待時間,一旦等候電梯時間過長,他將放棄。
模擬時鐘從 0 開始,時間單位爲 0.1 秒。人和電梯的各種動作均要消耗一定的時間單位(簡記爲 t),比如:
有人進出時,電梯每隔 40t 測試一次,若無人進出,則關門;
關門和開門各需要 20t;
每個人進出電梯均需要 25t;
電梯加速需要 15t;
上升時,每一層需要 51t,減速需要 14t;
下降時,每一層需要 61t,減速需要 23t;
如果電梯在某層靜止時間超過 300t,則駛回 1 層候命。
2、題目要求
基本:按時序顯示系統狀態的變化過程,即發生的全部人和電梯的動作序列。
擴展:實現電梯模擬的可視化界面。
二、需求分析
1、總體功能需求
該作品將實現一個模擬電梯,在界面上顯示出電梯系統狀態的變化過程,即發生的全部人和電梯的動作序列,對於乘客而言,動作包括等待電梯到來、進出電梯以及由於等待時間過長而離開電梯,對於電梯而言,動作包括電梯的加減速、上升與下降、開門與關門以及由於在某一層停留時間過長而駛回1層。其中要求乘客的產生是隨機的,電梯有一定的載荷量,當超過載荷量的時候,電梯外乘客無法進入。
2、功能需求
能夠模擬出正常電梯的行爲以及狀態,能夠計算並打印出當前時間電梯的行爲是必要的
3、軟件開發平臺需求
開發者開發的軟件要求能夠正常運行在Windows平臺,且具有一定的平臺兼容性.。爲保證軟件的上下兼容性,開發者應選擇通用的開發工具的來進行開發,目前的開發軟件平臺爲Visual Studio 2016。
4、輸入輸出要求
該系統無需對其進行輸入,用戶只需運行,乘客的產生以及各信息由程序隨機產生;
系統的輸出爲電梯系統狀態的變化時間過程以及各乘客的動作序列過程,並打印出當前時間,如:
乘客1想做電梯,來自第9樓,去第2樓 現在時間:483
電梯準備去9樓 電梯經過3樓 現在時間:287
三、設計
1、設計思想
1、數據結構設計
主要存儲結構爲類和鏈表;
電梯內的乘客和電梯外等待的乘客分別由兩個鏈表存儲,使用鏈表存儲能夠更容易地對乘客進行出入操作,並且可以幫助,當電梯在未超載的前提下,到達某樓層,滿足可進電梯條件之後,將該乘客從等待鏈表中刪去,又加入到電梯內乘客的鏈表中。
乘客和電梯封裝成類,其中乘客類的屬性包括了乘客的基本信息,包括等待時間、所來和所去樓層以及唯一編號;
電梯類的屬性包括電梯從開始運行的時間、存儲電梯內外的乘客的兩個列表、電梯當前狀態以及處於當前狀態的時間、上次和當前以及目的樓層、現在電梯運行方向等。其方法包括自定義構造函數、隨機產生乘客、主時間函數、乘客進出電梯函數、乘客離開電梯函數、用戶因爲等待時間過長離開、電梯判斷可關門函數、電梯返回一樓函數等;
值得注意的是這裏無論是電梯的狀態還是乘客的狀態都是枚舉類型,好處是方便直觀。
2、算法設計
該系統可分爲產生乘客、乘客進出電梯模塊、電梯關門及運動、回到一樓、目的樓層計算五個模塊,模擬時鐘的運行依次照着這五個模塊的次序而運轉,接下來會詳細介紹這五個模塊:
- 產生乘客模塊:
本模塊使用隨機函數產生乘客,經過測試可得到,平均每100t產生一名乘客較合理,因此,我們對於每1t產生一個隨機數,並對結果模上100,若爲0則產生乘客,反之不成立。當確定產生乘客時,隨機產生乘客的其他信息,譬如乘客當前所在樓層和目的樓層,對其狀態設爲等待電梯狀態並開始計時。
- 乘客進出電梯模塊:
調用此模塊之前,需要對當前是否能夠開門進行判斷,判斷可行之後,延時20t來表示開門的20t,此後,再對人進入電梯以及離開電梯進行操作,並將當前樓層修改爲之前的目的樓層。
- 電梯的關門模塊:
調用此模塊之前,需要對當前是否能夠關門進行判斷,與上一模塊不同的是確定關門之後,每40t判斷一次,而不是每時每刻都在判斷,關門之後,將當前狀態設爲waiting或上行、下行,而當前狀態時間設爲0。
- 回到一樓模塊
該模塊相對而言較簡單,如果電梯在某層靜止時間超過 300t,則駛回 1 層候命,判斷可行時,將目的樓層設爲1。
- 目的樓層計算與到達模塊
該模塊首先會根據當前電梯運轉方向、電梯外乘客所在樓層、電梯內乘客的目的樓層來判斷是否繼續該方向的運轉,如果滿足,選擇最靠近當前樓層的樓層爲目的樓層,若不滿足,比如,之前電梯方向爲上,但是電梯沒有想去更高樓層的乘客,電梯外也沒有更高樓層的乘客在等侯,這時候,電梯應該考慮向下運轉,之後,我們再根據計算結果到達目的樓層。
下圖爲主程序的流程圖:
2、設計表示:
1、函數調用關係圖
2、函數接口規格說明
Elevator()
說 明:重寫電梯類的構造函數,初始化電梯的所有參數。
參 數:無
返 回 值:無
void Random_Man();
說 明:平均每100t隨機產生一名乘客,並在該函數中實現乘客的信息隨機產生,除此外,還有判斷電梯外乘客是否離開,以及通過乘客決定目的樓層。
參 數:無
返 回 值:無
void Time_();
說 明:電梯的模擬時鐘函數,作爲主函數中循環調用的函數,並在其內部一次調用其它模塊。
參 數:無
返 回 值:無
void entryElevator();
說 明:乘客進入電梯函數 : 在電梯開門後,將滿足進入電梯的乘客信息從等待鏈表中刪除,並添加到電梯內部乘客鏈表中。
參 數:無
返 回 值:無
void leaveElevator();
說 明:乘客離開電梯函數 : 在電梯開門後判斷電梯內的乘客是否滿足離開電梯的條件,將其信息刪去。
參 數:無
返 回 值:無
int canBackToOne();
說 明:判斷電梯是否滿足因爲等待時間過長而回到一樓的條件。
參 數:無
值 |
意義 |
0 |
不滿足自動返回一樓的條件 |
1 |
滿足自動返回一樓的條件 |
void manLeave();
說 明:對電梯外的乘客依次進行判斷是否由於等待時間過長而停止等待
參 數:無
返 回 值:無
int isPause();
說 明:通過計算來判斷當前電梯是否已到達目的樓層。
參 數:無
返 回 值:
值 |
意義 |
0 |
電梯未到目的樓層 |
1 |
電梯已到目的樓層 |
void Pause();
說 明:當電梯到達到目的樓層之後進行的一系列操作,包括開門、乘客的進入與離開。
參 數:無
返 回 值:無
int pre_Moving();
說 明:計算電梯當前的目的樓層。
參 數:無
返 回 值:返回當前電梯的目的樓層。
int canClose();
說 明:判斷電梯當前是否可以進行關門操作。
參 數:無
返 回 值:
值 |
意義 |
0 |
電梯不可關門 |
1 |
電梯可關門 |
void Close();
說 明:當電梯準備關門之後進行的一系列操作,包括關門,以及電梯的方向決定。
參 數:無
返 回 值:無
int UpStairs();
說 明:計算得到電梯內乘客所想去的最高樓層。
參 數:無
返 回 值:返回電梯內乘客所想去的最高樓層。
int DownStairs();
說 明:計算得到電梯內乘客所想去的最低樓層。
參 數:無
返 回 值:計算得到電梯內乘客所想去的最低樓層。
3、詳細設計
1、電梯狀態
/**
* 將電梯的狀態設爲枚舉類型,簡單直觀
*/
enum Status {
Opening = 1, Opend, Closing, Closed, Waiting, Down_Moving,Up_Moving, Decelerate, Accelerate
};
2、乘客類:
/**
* 定義一個乘客的所有信息
*/
class Man {
public:
int num;
int from;
int to; //去哪裏
int Time; //等待時間
Status_Man Status; //只有等待和不等待
};
3、電梯類:
/**
* 自定義的一個模擬電梯類
*/
數據成員:
public:
list<Man> Pass; //電梯外等待的乘客
list<Man> now_Pass;//電梯內的乘客
int time; //電梯已經運行的時間
int now_Floor; //上次電梯開門的樓層
int next_Floor; //電梯的目的樓層
Status now_Status; //電梯當前狀態
int before; // 1:之前是向上的 0:之前向下
int parm_Floor; //當前所處樓層
int now_Status_Time; //當前狀態已花時間
四、調試分析
1、遇到主要的問題:
1) 在使用STL對鏈表裏面的元素進行刪除時,總是造成不可預期的錯誤,因爲erase之後,鏈表更新,迭代器可能發生錯誤,之前在這個問題上停留了很久,最終通過閱讀《STL源碼剖析》找到了解決方案,先判斷迭代器是否爲最後一個元素,若不是,則使用erase(),若是,則使用pop_back()。
2) 對該模擬電梯進行設計的時候,因爲電梯需要邏輯判斷和行爲實施同時進行,比如需要在電梯上下運行的同時,不能影響乘客的產生,同時還需要判斷是否到達目的樓層,一開始所想的是使用多線程編程,但是C++對多線程很不友好,需要對windows底層進行編程,比較麻煩,因此,最後通過sleep()暫時休眠進程來對每一個t進行實際操作。
3) 在計算下一個目的樓層的時候,通過pre_Moving()計算之後的結果總是與實際結果不一樣,具體表現在當電梯某一樓層時,突然顯示到了第14層,通過單步斷點,最後發現是計算當前樓層發生了邏輯錯誤,修改代碼之後,將當前樓層加入到類的屬性之中,防止接下來的代碼編寫出現類似問題。
4) 顯示電梯和用戶的動作序列時,之前是將其直接輸出,最後顯示的太過混亂,電梯行爲和用戶的行爲序列互相混淆,因爲沒有使用圖形化界面,最後解決辦法是將電梯的行爲輸出到屏幕左邊,乘客的行爲輸出到右邊,中間顯示電梯的時間,同時在關於給每一個人一個標識的時候,爲了達到標識與記錄兩種功能,最後選擇使用全局數字序號,因爲它簡潔,能夠產生的標識多而且相比較而言更容易實現。
2、程序的時空複雜度分析:
該系統的運行時間是無窮的,當開始啓動之後,電梯的每時每刻都在運行或者等待操作,因此在討論複雜度的時候是對每一個時間t所要完成的任務作複雜度分析,對於本程序,使用最多的算法是對鏈表的操作,主要是插入和刪除操作,因爲插入操作是直接在鏈表的頭指針直接插入的時空複雜度均爲O(1),因此最高的時間複雜度就是對鏈表的刪除操作,其時間複雜度爲O(n),空間複雜度爲O(1),其中n爲鏈表的長度。對於其他操作,如計算目的樓層時,需要對鏈表中的元素進行遍歷,通過分析,時間複雜度爲O(n),空間複雜度爲O(1),實際情況下,由於每t都有可能隨機產生1名乘客,但是乘客的最大等待時間是300t,因此電梯外乘客的鏈表不會超過300,而電梯內乘客鏈表由於電梯負載限制最多容納13人。
3、經驗體會:
1) 我認爲使用多線程編程能使得該系統更簡單: 用虛擬時鐘,自己設置一個相對時間,每進行一個動作就將這個動作所用的時間加到一個全局變量中,並且每進行一個動作就將該動作的執行內容輸出,通過並行操作之後,實際中的t和編碼之後的t是一樣的,但是在該系統之中,題目所要求得t和該系統的t是有所不同的,但是C++多線程的處理過程很讓人費解,因爲對於本道題我更傾向於使用Java,因爲對Java的多線程有比較深的理解,可以將本道題沒有使用多線程的缺陷運用上去。
2) 理解分析問題的能力得到提高。設計一個應用程序關鍵是對要求做最準確的把握,也就是說弄清楚需求分析是很重要的。這道題剛開始看,很難理解這道題要達到的目的是什麼,如何通過模擬時鐘來告知各模塊應執行什麼操作,同時又要對乘客的進入進行操作,這之間的過程很讓人費解,經過仔細的多次讀題後,畫了個草圖,才理清其中的思路。所以,通過這道題,大大提高了我的理解分析問題能力。
五、用戶手冊
1.打開文件
windows環境下雙擊執行文件 zzz_eleva.exe。
2.運行:
(1)界面左邊顯示的是電梯的動作序列,比如當前樓層、目標樓層和當前方向,或者是電梯的開門、關門、加速和減速等等;
(2)界面右邊顯示的是乘客的動作序列,包括乘客的隨機產生成功、乘客因等待時間過長而離去、乘客進入或離開電梯;
(3)界面中間顯示的是當前電梯運行的時間,單位是t。
3.退出:
在任意時刻,操作者都可點擊右上角的“X”來退出電梯模擬。
六、測試數據及測試結果
測試輸入:
數據是程序隨機生成,包括產生乘客的時間和樓層信息:
電梯時間 |
乘客編號 |
來自樓層 |
目的樓層 |
4t |
乘客1 |
9 |
2 |
155t |
乘客2 |
3 |
7 |
183t |
乘客3 |
9 |
2 |
199t |
乘客4 |
3 |
5 |
267t |
乘客5 |
9 |
7 |
452t |
乘客6 |
7 |
2 |
504t |
乘客7 |
2 |
5 |
… |
… |
… |
… |
測試目的:
1)電梯是否能夠正常運行;
2)電梯時間是否正常。
3)乘客是否能夠在等待時間內乘坐電梯,並在目的樓層離開電梯;
正確輸出:
現在時間:4 乘客1想做電梯,來自第9樓,去第2樓
電梯準備去9樓 電梯經過1樓 ↑ 現在時間:19
電梯準備去9樓 電梯經過2樓 ↑ 現在時間:70
電梯準備去9樓 電梯經過3樓 ↑ 現在時間:121
現在時間:155 乘客2想做電梯,來自第3樓,去第7樓
電梯準備去9樓 電梯經過4樓 ↑ 現在時間:172
現在時間:183 乘客3想做電梯,來自第9樓,去第2樓
現在時間:199 乘客4想做電梯,來自第3樓,去第5樓
電梯準備去9樓 電梯經過5樓 ↑ 現在時間:223
現在時間:267 乘客5想做電梯,來自第9樓,去第7樓
電梯準備去9樓 電梯經過6樓 ↑ 現在時間:274
現在時間:300 1號乘客等待時間過長離開
電梯準備去9樓 電梯經過7樓 ↑ 現在時間:325
電梯準備去9樓 電梯經過8樓 ↑ 現在時間:376
電梯準備去9樓 電梯經過9樓 ↑ 現在時間:427
現在時間:441
電梯已到9樓
1電梯在開門 現在時間:441
現在時間:452 乘客6想做電梯,來自第7樓,去第2樓
現在樓層在9 現在時間:461
現在時間:470
電梯已到9樓
1電梯在開門 現在時間:470
現在時間:490 5號乘客從9樓進入電梯,想去7樓
電梯進人ing
現在時間:504 乘客7想做電梯,來自第2樓,去第5樓
現在時間:504 2號乘客等待時間過長離開
現在時間:509 乘客8想做電梯,來自第4樓,去第5樓
現在時間:515 3號乘客從9樓進入電梯,想去2樓
電梯進人ing
現在樓層在9 現在時間:540
電梯準備去7樓 電梯經過8樓 ↓ 現在時間:546
現在時間:585 4號乘客等待時間過長離開
電梯準備去7樓 電梯經過7樓 ↓ 現在時間:607
實際輸出: