4 Star 5 Fork 1

wangjiezhe / plane_management

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Report.md 10.51 KB
一键复制 编辑 原始数据 按行查看 历史
wangjiezhe 提交于 2015-08-02 12:15 . Remove Report.md from .gitignore

关于机场调度系统的上机报告

Author: 王介哲 1200010730

问题的需求分析

问题

假设某机场停机坪共可停放n架飞机,若停机坪上已停满n架飞机, 则后来的飞机只能在空中盘旋等候, 调度等候飞机降落的顺序是由飞机的当前现有燃油量程度来决定。 机场按照所有飞机到达的时间顺序给飞机编号ID,依次为1,2,3,…,m; 并且根据飞机的燃油量设置Priority值,燃油量越少,Priority的值越小。 一旦停机坪上的飞机起飞,系统将根据等候的飞机的Priority值, 由小到大依次安排飞机降落。 航空公司必须按照每架飞机在停机坪停留的时间长短交纳费用。

目标程序

开发一个机场对飞机起落进行管理的程序。

简化假设

  • 机场有一条起飞跑道和一条降落跑道.
  • 飞机型号基本相同,即起飞、降落、整休时间都相同,分别为t_u, t_d, t_r.
  • 最小起飞时间间隔相同,为t_ub.
  • 最小降落时间间隔相同,为t_db.
  • 一天最大的飞机降落流量不超过MAXNUMPERDAY(MPD)架次.
  • 飞机到达密度不超过机场承受能力(即不存在燃油不够仍无法降落的问题), 设为MAXNUMPERHOUR(MPH).
  • 天气情况始终不影响起飞和降落.
  • 飞机停留在停机坪的时间尽量少。
  • 初始状态停机坪上飞机数量为0.
  • 飞机盘旋等候时单位时间耗油量(L)为OILPERHOUR(OPH)。
  • 时间精确到秒。
  • 飞机起飞和降落互不影响。

这一部分的信息储存在src/data.h

目标功能

  • 记录飞机信息
  • 对飞机燃油剩余量排序
  • 记录飞机的信息
  • 发送降落和起飞命令

模块功能的划分

总体来说,整个程序分为两个层次:界面和内部数据处理。

界面部分

由于采用的是GTK+界面编程,主要涉及到的是GTK+的C API函数的调用和实现。

这一部分的具体实现见后面。

内部数据处理

这一层又分为四个部分:飞机进入等待队列,飞机降落,飞机起飞和飞机修整。

飞机进入等待队列

由于飞机的降落顺序是按照其剩余油量来确定的,剩余油量较少的飞机优先降落, 遵循最小元素先出的原则。因此,采用优先队列来实现飞机的等待队列waiting

为了便于比较以及后面不需要做过多的转化,在存储飞机信息是并不是直接存储其剩余油量, 而是将其换算成允许降落的最后期限。这样得到的排序与直接按剩余油量得到的排序结果相同。

具体的实现方式是采用小根堆,与二叉树的定义类似。

struct PriorityQueue {		/* 优先队列 */
	int MAXNUM;
	int n;
	DataType_pq * pq;	/* 优先队列中元素的顺序表示 */
};
typedef struct PriorityQueue * PPriorityQueue;

PPriorityQueue waiting;

其中,DataType_pq为等待降落的飞机的信息的定义。 由于需要同时储存飞机的航班号和允许降落的最后期限,故用一个结构来存储飞机信息:

struct flight {
	char flight_name[7];	/* 航班号,国内航班为六位,国际航班为七位 */
	time_t landing_deadline;	/* 允许降落的最后期限 */
	};
typedef struct flight DataType_pq;

这样,每次有飞机到来,点击New plane arrived按钮,并输入航班号和剩余油量, 即可将其加入到等待队列中,剩余油量和最后降落期限的转化由系统自动完成。

飞机降落

为了便于与其他信号函数进行信息交互,定义一个runway_down表示降落跑道的状态:

gint runway_down;

runway_down的值为0(即初始值),表示跑道空闲,可以降落飞机; runway_down的值为1,表示跑道已被占用或正在准备,不能降落飞机。

根据前面的假设,各架飞机降落和修整的时间均相同, 故先降落的飞机先修整完毕,进入等待起飞状态。 这符合先进先出的原则,故可以用以个队列来实现停机坪上的飞机队列reseting

具体的实现方式可以通过环形队列的顺序表示来实现:

struct SeqQueue{
	int MAXNUM;
	int f,r;
	DataType_q *q;
};
typedef struct SeqQueue * PSeqQueue;

PSeqQueue resting;

其中,DataType_q为停在停机坪上的飞机的信息的定义, 由于需要储存飞机的航班号和其降落时间,故采用一个结构来实现:

struct SeqQueue{
	int MAXNUM;
	int f,r;
	DataType_q *q;
};
typedef struct SeqQueue * PSeqQueue;

PSeqQueue reseting;

需要注意的是由于采用环形队列牺牲了一个节点, 故resting->MAXNUM应该比停机坪的最大允许停机数目大1.

这样,当New plane to land按钮可用时, 点击即可将等待队列中的最前端的一架飞机由等待队列进入停机队列。 如果等待队列为空,将给出提示信息,无其他任何影响。

飞机起飞

遇上面类似,定义一个runway_up来表示起飞跑道的状态:

gint runway_up;

runway_up的值为0(即初始值),表示跑道空闲,可以起飞飞机; runway_up的值为1,表示跑道已被占用或正在准备,不能起飞飞机。

New plane to take off按钮可用时,点击即可发送飞机起飞指令, 处在停机队列最前端的飞机随即进入起飞过程。

飞机修整

事实上,飞机修整部分与降落和起飞部分并不是相互独立的, 主要涉及到的就是飞机由停机队列到起飞的过程转化。 这一过程主要利用alarm函数来实现。 当停机队列最前端的飞机达到休息时间时,向自己发送SIFALRM信号, 程序接受到该信号后使New plane to take off按钮变为可用, 即修整完毕可以进入起飞过程。

程序的具体实现

先调用gtk_window_new(GTK_WINDOW_TOPLEVEL)建立主窗口, 并调用g_signal_connect绑定基本信号。

然后建立菜单栏和状态栏,这一部分内容较少。

最后是界面的主要控制和输出部分,分为三个部分: 空中等待,飞机降落和飞机起飞。

空中等待

左边部分。

具体分为三部分。 最上面为New plane arrived按钮,为控制其大小建立在一个表格中。 中间是一个标签,现实等待队列中的剩余飞机数。 下面是信息输出框,当数据增加时,会自动显示滚动条并跟踪输出数据滚动。

这一部分的重点在于New plane arrived的回调函数。

当用户点击该按钮时,程序会调用gtk_dialog_new构建一个新的对话框。 当用户输入正确的数据后(这里未设置检查函数,要求用户对自己的输入负责), 点击Ok后程序将调用dialog_ok_clicked用户的输入导入到等待队列中, 并在下方的输出框中显示存储的信息; 点击Cancal取消输入。

飞机降落

中间部分。

最上面为New plane to land按钮,构建同上。 中间是一个进度条,用来显示飞机降落的进度。 下面是信息输出框,现实降落的飞机的信息。

这一部分的重点在于New plane to land的回调函数。

当用户点击该按钮时,程序会调用new_plane_to_land函数。 该函数首先检查等待队列是否为空,若为空则在下方输出提示信息并返回。 若不为空,则将等待队列中的第一个取出并从队列中删除, 然后在下方输出提示信息,并将启动进度条控制函数progress_timeout2, 同时将New plane to land按钮变为不可用。

progress_timeout2按照计时器pdata2->timer不断更新进度条pdata2->pbar, 直到进度条满后,停止计时器,将飞机的航班号和降落时间存入停机队列, 并输出提示信息,根据停机队列的情况将New plane to land按钮变为可用 或启动alarm函数。

飞机起飞

右边部分。

结构与中间类似,按钮为New plane to take off。 与中间不同的是,该按钮在初始状态下不可用,必须当有飞机可以起飞时, 被alarm函数唤醒后才可使用。

这一部分的重点在于New plane to take off的回调函数。

当用户点击该按钮是,程序会调用new_plane_to_take_off函数。 改善数将停机队列中的第一个取出并从队列中删除, 然后在下方输出提示信息,并启动进度条控制函数progress_timeout3, 同时将New plane to take off按钮变为不可用。

progress_timeout3按照计时器pdata3->timer不断更新进度条pdata3->pbar, 直到进度条满后,停止计时器,输出提示信息, 并根据停机队列的情况启动alarm函数。

算法分析

时间代价

时间代价主要是两个队列的操作。

飞机等待队列waiting的插入和删除,由于采用的是优先队列,故其时间代价都是O(log2n)。

飞机停机队列reseting采用的是顺序表示的队列,时间代价为O(1)。

空间代价

空间代价主要是两个队列和程序控件的存储。

上机实习总结

有待改进的问题

  • 界面的部分地方仍有预留的可优化的地方,如菜单栏比较简陋,About菜单仍可以完善, 状态栏创建了未正式使用等。
  • 多线程部分仅初始化而未具体使用。
  • 跑道被占用和跑道准备时间未做区分。
  • 由于假设过于简化,有许多地方与实际并不相符,如不同飞机的型号不一定相同等。
  • 缺少意外情况的处理,一些情况如飞机同时到来的数量过多等无法正确处理。

收获和体会

  • 一开始,重心被放在了整个程序的核心算法和数据结构上,但实际操作中发现, 花费了更多精力去处理的是其他周边配合的工作,如界面的设计、界面的库函数的调用、 各个部分之间的通信等。因此,才真正的软件设计中,并不是仅仅有一个好的算法就可以的, 还需要大量准备工作的配合。
  • 在整个编程过程中,除了应用课程中所学知识外,还通过其它资料的学习解决实际困难。 界面设计帮助我们提高了 GTK+ 的使用能力;操作过程中遇到诸多意想不到的错误, 处理这些错误使我们更了解编程过程中容易出现的问题,并使解决问题的能力得到加强。

参考资料

C
1
https://gitee.com/wangjiezhe/plane_management.git
git@gitee.com:wangjiezhe/plane_management.git
wangjiezhe
plane_management
plane_management
master

搜索帮助