第二次结对编程_电梯调度的可视化
第二次结对编程_电梯调度的可视化
10061130 殷鹏程
10061204 姚 铭
运行时截图
主要功能:
- 完整反映了电梯调度的整个过程
- 动态展示要求的与未要求的运行时数据
a) 当前运行时长
b) 当前活跃乘客数目
c) 所有乘客平均旅行时间
d) 当前乘客平均旅行时间(所有活跃乘客的平均旅行时间)
e) 乘客最长旅行时间(当前活跃乘客+已到达目的地乘客)
f) 已到达目的地乘客的最短旅行时间
g) 每层乘客等待人数
h) 每层的电梯指示灯
- UI自适应,动画展示电梯运行过程
- 支持点击电梯显示当前选中电梯的相关数据
- 运行\暂停\停止 控制功能
源程序架构
程序包括两个线程,UI线程与电梯调度算法所在的线程.
程序采用消息队列的形式,类MessageQueue以静态工厂化方式提供给每一个子项目(Passenger,Elevator,etc),对于电梯,乘客的每一次状态改变(电梯运行,乘客按键,乘客进入电梯),均将相应状态提交至消息队列,并由主类中的相关逻辑在同一线程,同一Ticks下完成消息的处理,并驱动UI线程的相关显示功能.
考虑到编码方便,电梯位置消息并未封装至消息队列中,而是直接使用窗体提供的委托化方法,已实现UI中电梯的运动.
代码行数(仅UI部分以及UI相关逻辑)
大于800行
设计心得
这次的结对编程,难点不在算法,而在于如何实现跨线程的数据传送与控件的控制,UI线程需要及时的显示调度线程的状态变化,调度线程在必要时需要直接驱动UI线程的显示.
在设计中,我们使用了消息队列的模式实现线程间的消息传送,使用代理(delegate)实现调度线程对UI线程的驱动.这样的设计编码成本低,且维护性较高.
在Console模式下的调度程序里,所有的状态都是直接打印到控制台的,只需要将控制台输出的语句替换为提交消息的语句:
/* Console.WriteLine("[Passenger] {0} is pressing, from {1} to {2}, ", _name, _SourceFloor, _TargetFloor);*/ MsgQueue.GetInstance().EnQueue( new Message(msg,this,MessageType.PassengerCome) );
其中的MessageQueue类使用典型的静态工厂方法
public class MsgQueue { Queue<Message> msgQueue; static MsgQueue instance = null; static object lockObj = new object(); private MsgQueue() { msgQueue = new Queue<Message>(); } public static MsgQueue GetInstance() { lock (lockObj) { if (instance == null) { instance = new MsgQueue(); } } return instance; } public int Count { get { return msgQueue.Count; } } public void EnQueue(Message msg) { msgQueue.Enqueue(msg); } public Message DeQueue() { return msgQueue.Dequeue(); } }
关于跨线程的UI驱动:
跨线程驱动使用了代理(delegate)的模式,在UI窗体类MainForm中,提供了公有方法:
public void SetWaitLb(int i, int num) { this.Invoke(new SetWaitLbDel(setWaitLb), new object[] { i, num }); }
其中SetWaitLbDel为delegate,setWaitLb()为私有方法(实现UI控制逻辑)
此外,为了显示电梯运行时数据,需要进行一些数学运算,运算虽不复杂,但几乎都有涉及循环编译,为了简化编码,清一色采用Linq实现:
double actTotalTime = (from p in status.ActivePassengers select (ticks - p.DirectionReqTime)).Sum(); double inactTotalTime = (from p in status.InActivePassengers select p.ReqCompleteTime - p.DirectionReqTime).Sum(); status.AvgTravelTime = (actTotalTime+inactTotalTime)/status.PassengerNum; status.CurrentActTravelTime = actTotalTime / status.ActivePassengerNum; double maxActTime = 0; double minActTime = 0; double maxInActTime = 0; double minInActTime = 0; try{ maxActTime = (from p in status.ActivePassengers select (ticks - p.DirectionReqTime)).Max(); minActTime = (from p in status.ActivePassengers select (ticks - p.DirectionReqTime)).Min(); maxInActTime = (from p in status.InActivePassengers select p.ReqCompleteTime - p.DirectionReqTime).Max(); minInActTime = (from p in status.InActivePassengers select p.ReqCompleteTime - p.DirectionReqTime).Min(); MainForm.SetMaxTime(Math.Max(maxActTime, maxInActTime)); MainForm.SetMinTime(minInActTime); } catch { }
使用说明
1.点击“选择乘客xml”,选择一个乘客数据文件。
2.点击“选择电梯”,选择一个电梯数据文件。
3.在调度算法栏中,选择一个调度算法。
4.点击“运行”,程序开始启动。
左则显示了各个电梯所在楼层,箭头为绿色表示当前乘客的请求。
运行过程中,可以动态显示基本运行数据。
任意点击一个电梯,可以显示该电梯的具体数据。
同时,运行过程中可以随时暂停或停止。