结对编程组员:
马辰 11061178
柴泽华 11061153
1)改进电梯调度的interface 设计, 让它更好地反映现实, 更能让学生练习算法, 更好地实现信息隐藏和信息共享。
改进1:
在Elevator类中增设属性isfull.
public class SenElevator: IElevator { public bool isfull {get;set;} }
public class SigmaPassenger : IPassenger { private void EnterElevator(IScheduler scheduler, int tick, IElevator elev) { SenElevator senElev = elev as SenElevator; if (senElev.FreeCapability >= _weight) { //省略此部分代码 } else if( senElev.CurrentStatus.DoorCloseOpenTicks < 4 ) //wait for one tick to try enter again. { //省略此部分代码 elev.isfull = true; } } private void LeaveElevator(int tick, IElevator elev) { //省略此部分代码 elev.isfull = false; } }
初始为false.当电梯出现过一次满载拒绝后,isfull为true;当电梯中有乘客走下电梯时,isfull为false.
增设原因:
1)在算法设计过程中,如果在关门的瞬间有乘客要乘坐电梯,那么最好的方法就是在有请求时停靠一次;但是在人满时,即使有请求也不必要再次停靠。
因此,要通过判断电梯的余量来进行满载检测。这种满载检测是不准确的,虽然有时候余量大于最小的乘客体重,但是不一定刚好有一个乘客的体重小于余量可以进去,因为电梯是不知道乘客的体重的。那么,最好的方法不是检测电梯的余量,而是在电梯出现过一次满载后,就判定电梯不能再接收乘客了,直到有乘客从电梯中走下去,才可以视为可以再次接受乘客。
2)如果通过余量来检测电梯是否满载是非常麻烦的,需要根据不同情况修改限值,降低了Scheduler的使用范围。
改进2:
在Elevator类中增设属性afterhistory.
public class SenElevator: IElevator { public Direction afterhistory { get; set; } }
public class SenElevator: IElevator { public void StatusUpdateForEveryTick(int ticks) { if (_Status.DoorCloseOpenTicks == -1) // not closing or opening { //省略部分代码 } _Status.CurrentFloor = _Status.CurrentHeight / _FloorHeight; if (_Status.CurrentHeight == _target * this._FloorHeight || _Status.DoorCloseOpenTicks >= 0) { ResetDoorCloseOpenTickCountOnce(); HistoryDirection = afterhistory; EventArgs e = new ElevStoppedEventArgs(_Status.CurrentFloor, ticks, Scheduler); } } }
Scheduler通过afterhistory,设置当Scheduler刚刚提出的请求在完成之后才会相应的修改电梯的HistoryDirection,保证Scheduler停止命令的原子性。
增设原因:
如果在发出命令的同时修改电梯的HistoryDirection,使得Scheduler只有在电梯停止的时候才能进行调度控制,这样既不符合实际真实的时间连续性,又使电梯的相应速度变慢,不能适用于快速变化的环境,从而影响效率。其根本原因在于,直接设置HistoryDirection的算法破坏了电梯停靠的原子操作,与真实情况有较大出入。引入afterhistory使得停靠操作与HistoryDirection同时进行操作,实现实时控制与保持停靠的原子性。
2)目前的这个测试程序只有命令行界面, 请给它设计UI界面, 显示乘客/电梯的运动, 并实现之,show your code and UI.
初始界面
运动界面
实现思想,以World作为连接算法框架和UI的controller。可实现多次重复重新开始~
这里只列出与UI相关的代码。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Elevator; using Passenger; using Scheduler; using Loader; using commons; using System.Threading; using System.Windows; namespace World { public partial class Form1 : Form { public Form1() { InitializeComponent(); maskedTextBox1.Text = "50"; maskedTextBox3.Text = "C:\Users\T\Desktop\v6tt\Loader\elevators.xml"; maskedTextBox4.Text = "C:\Users\T\Desktop\v6tt\Loader\passenger1.xml"; //this.SetStyle(ControlStyles.ResizeRedraw, true); } static List<SenElevator> AllElevators; static List<SigmaPassenger> AllPassengers; //sorted by DirectionReqTime static IScheduler NaiveScheduler; public Thread TickGoes; public Thread t1; bool complete = false; bool finished = false; PassengerLoader _passengerLoader; ElevatorLoader _elevatorLoader; List<Label> ll = new List<Label>(); List<Label> up = new List<Label>(); List<Label> down = new List<Label>(); int ticks = 0; int delaytime = 50; bool AreElevatorsIdle() { bool ret = true; foreach (SenElevator e in AllElevators) ret = ret && e.IsIdle; return ret; } void WorldInit(string xmlElv, string xmlPassengers) { ll.Add(label1); ll.Add(label2); ll.Add(label3); ll.Add(label4); ll.Add(label5); ll.Add(label6); ll.Add(label7); ll.Add(label8); ll.Add(label9); ll.Add(label10); ll.Add(label11); ll.Add(label12); ll.Add(label13); ll.Add(label14); ll.Add(label15); ll.Add(label16); ll.Add(label17); ll.Add(label18); ll.Add(label19); ll.Add(label20); ll.Add(label21); ll.Add(label22); ll.Add(label23); ll.Add(label24); ll.Add(label25); ll.Add(label26); ll.Add(label27); ll.Add(label28); ll.Add(label29); ll.Add(label30); ll.Add(label31); ll.Add(label32); ll.Add(label33); ll.Add(label34); ll.Add(label35); ll.Add(label36); ll.Add(label37); ll.Add(label38); ll.Add(label39); ll.Add(label40); ll.Add(label41); ll.Add(label42); ll.Add(label43); ll.Add(label44); ll.Add(label45); ll.Add(label46); ll.Add(label47); ll.Add(label48); ll.Add(label49); ll.Add(label50); ll.Add(label51); ll.Add(label52); ll.Add(label53); ll.Add(label54); ll.Add(label55); ll.Add(label56); ll.Add(label57); ll.Add(label58); ll.Add(label59); ll.Add(label60); ll.Add(label61); ll.Add(label62); ll.Add(label63); ll.Add(label64); ll.Add(label65); ll.Add(label66); ll.Add(label67); ll.Add(label68); ll.Add(label69); ll.Add(label70); ll.Add(label71); ll.Add(label72); ll.Add(label73); ll.Add(label74); ll.Add(label75); ll.Add(label76); ll.Add(label77); ll.Add(label78); ll.Add(label79); ll.Add(label80); ll.Add(label81); ll.Add(label82); ll.Add(label83); ll.Add(label84); up.Add(label85); up.Add(label86); up.Add(label87); up.Add(label88); up.Add(label89); up.Add(label90); up.Add(label91); up.Add(label92); up.Add(label93); up.Add(label94); up.Add(label95); up.Add(label96); up.Add(label97); up.Add(label98); up.Add(label99); up.Add(label100); up.Add(label101); up.Add(label102); up.Add(label103); up.Add(label104); up.Add(label105); down.Add(label106); down.Add(label107); down.Add(label108); down.Add(label109); down.Add(label110); down.Add(label111); down.Add(label112); down.Add(label113); down.Add(label114); down.Add(label115); down.Add(label116); down.Add(label117); down.Add(label118); down.Add(label119); down.Add(label120); down.Add(label121); down.Add(label122); down.Add(label123); down.Add(label124); down.Add(label125); down.Add(label126); complete = false; ticks = 0; //delaytime = 50; finished = false; for (int i = 0; i <= 83; i++) ll[i].BackColor = Color.White; for (int i = 0; i <= 20; i++) { up[i].BackColor = Color.White; down[i].BackColor = Color.White; } _passengerLoader = new PassengerLoader(xmlPassengers); _elevatorLoader = new ElevatorLoader(xmlElv); AllPassengers = _passengerLoader.Load(); AllElevators = _elevatorLoader.Load(); NaiveScheduler = Scheduler.SchedulerFactory.CreateScheduler(); foreach (SenElevator elev in AllElevators) { elev.Scheduler = NaiveScheduler; } List<IElevator> elevs = new List<IElevator>(); foreach (IElevator e in AllElevators) elevs.Add(e); NaiveScheduler.Initialize(elevs); //SenElevator.SenClock.Start(AllElevators); TickGoes = new Thread(new ThreadStart(this.WorldEngine)); t1 = new Thread(new ThreadStart(this.t2start)); t1.Start(); TickGoes.Start(); } void TickUpdate(int nextActiveTick) { if (AreElevatorsIdle() && nextActiveTick > ticks) { ticks = nextActiveTick; } else { ticks++; } } void t2start() { while (!finished) { for (int i = 0; i <= 20; i++) { if (NaiveScheduler.upwait.Contains(i)) up[20 - i].BackColor = Color.Black; else up[20 - i].BackColor = Color.White; } for (int i = 0; i <= 20; i++) { if (NaiveScheduler.downwait.Contains(i)) down[20 - i].BackColor = Color.Black; else down[20 - i].BackColor = Color.White; } int j = AllElevators[0].CurrentStatus.CurrentFloor; ll[20 - j].BackColor = Color.Red; for (int k = 0; k <= 20; k++) { if (k != j) ll[20 - k].BackColor = Color.White; } j = AllElevators[1].CurrentStatus.CurrentFloor; ll[41 - j].BackColor = Color.Yellow; for (int k = 0; k <= 20; k++) { if (k != j) ll[41 - k].BackColor = Color.White; } j = AllElevators[2].CurrentStatus.CurrentFloor; ll[62 - j].BackColor = Color.Green; for (int k = 0; k <= 20; k++) { if (k != j) ll[62 - k].BackColor = Color.White; } j = AllElevators[3].CurrentStatus.CurrentFloor; ll[83 - j].BackColor = Color.Blue; for (int k = 0; k <= 20; k++) { if (k != j) ll[83 - k].BackColor = Color.White; } label131.Text = AllPassengers.Count().ToString().Trim(); j = 0; for (int i = 0; i < AllPassengers.Count(); i++) { if (AllPassengers[i].IsArrived) j++; } label132.Text = j.ToString().Trim(); /*int tmp; for (int i = 0; i < NaiveScheduler.upwait.Count(); i++) { tmp = NaiveScheduler.upwait[i]; up[20 - tmp].BackColor = Color.LightCyan; } for (int i = 0; i < NaiveScheduler.downwait.Count(); i++) { tmp = NaiveScheduler.downwait[i]; down[20 - tmp].BackColor = Color.LightCyan; } int j = AllElevators[0].CurrentStatus.CurrentFloor; ll[20 - j].BackColor = Color.Red; //if (last != j) //ll[20 - last].BackColor = Color.White; //last = j; for (int k = 0; k < 20; k++) { if (k != j) ll[k].BackColor = Color.White; } */ this.Refresh(); } } void WorldEngine() { while (true) { //TickGoes. Console.WriteLine("Tick #{0}-------------------------------", ticks); //TickGoes.Join(); int NextActiveTick = -1; complete = DrivePassenger(ref NextActiveTick); if (complete) break; DriveElevator(); NaiveScheduler.Run(); TickUpdate(NextActiveTick); Console.WriteLine(""); Thread.Sleep(delaytime); //t1.Join(); } } bool DrivePassenger(ref int NextActiveTick) { bool complete = true; bool active = false; int nextTick = 0x7fffffff; foreach (SigmaPassenger p in AllPassengers) { if (!p.IsArrived) { complete = false; } IRequest req = p.GenerateReq(ticks, AllElevators.ConvertAll<IElevator>(o => o)); if (req != null) { NaiveScheduler.QueueReq(req); active = true; } if (p.IsInside) active = true; else { if (!p.IsArrived) { nextTick = Math.Min(nextTick, p.DirectionReqTime); } } } if (!active) NextActiveTick = nextTick; return complete; } void DriveElevator() { foreach (SenElevator elev in AllElevators) { elev.StatusUpdateForEveryTick(ticks); } } static void RealTimeTest( List<SenElevator> Elevs, List<SigmaPassenger> Passengers, IScheduler Scheduler ) { //int i = 0; //while (i < Passengers.Count) //{ // IPassenger p = Passengers[i]; // SenElevator.SenClock.Wait(p.DirectionReqTime); // lock (SenElevator.SenClock) //stop the clock // { // do // { // Scheduler.QueueDirection(p); // i++; // } while (i < Passengers.Count && Passengers[i].DirectionReqTime == p.DirectionReqTime); // } //} } static void SimulateTimeTest( List<SenElevator> Elevs, List<SigmaPassenger> Passengers, IScheduler Scheduler) { //foreach (IPassenger p in Passengers) //{ // Scheduler.QueueDirection(p); //} //Scheduler.Run(); } private void button1_Click(object sender, EventArgs e) { } private void Form1_Load(object sender, EventArgs e) { } private void label131_Click(object sender, EventArgs e) { } private void label135_Click(object sender, EventArgs e) { } private void label143_Click(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { if (delaytime - 1 < 0) delaytime = 0; else delaytime--; } private void button3_Click(object sender, EventArgs e) { delaytime++; } private void button1_Click_1(object sender, EventArgs e) { label135.Text = "Running"; label133.Text = ""; string elefile, passfile; elefile = maskedTextBox3.Text.Trim(); passfile = maskedTextBox4.Text.Trim(); string detime = maskedTextBox1.Text.Trim(); if (detime != "") { delaytime = 2500 / Convert.ToInt32(maskedTextBox1.Text.Trim()); } else { delaytime = 50; } WorldInit(elefile, passfile); //WorldInit("C:\Users\T\Desktop\v6\Loader\elevators.xml", "C:\Users\T\Desktop\v6\Loader\passenger2.xml"); TickGoes.Join(); int num = 0; int cost = 0; foreach (SigmaPassenger p in AllPassengers) { p.PrintStatistics(); cost += p.ReqCompleteTime - p.DirectionReqTime; num++; } label133.Text = ((float)cost / num).ToString().Trim(); label135.Text = "Finished!"; finished = true; int j = 0; for (int i = 0; i < AllPassengers.Count(); i++) { if (AllPassengers[i].IsArrived) j++; } label132.Text = j.ToString().Trim(); this.Refresh(); //label135.BackColor = Color.White; } } }
3)阅读有关 MVC 和 MVVM 设计模式的文章。
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用于组织代码用一种业务逻辑和数据显示分离的方法,这个方法的假设前提是如果业务逻辑被聚集到一个部件里面,而且界面和用户围绕数据的交互能被改进和个性化定制而不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
这次工程中的应用
Model(逻辑模型 -->实际模型代码)
首先在建立工程的初期,应该只考虑被工程需要什么类,各个类之间的关系,以及每个类的接口。这一步,由于电梯框架已经搭建好,所以就只要阅读代码,绘制UML类图,理解各个类的关系,以及每个类的作用。
在写代码的过程中,如果算法需要,再根据实际需求相应地修改模型,接口,将逻辑模型向实际模型过渡。
主要是将算法利用框架实现,有时需要小幅度修改框架。
UI
设计UI,绘制完成UI界面,为UI添加事件响应。这里主要运用label来表示电梯的状态。
Controllor(连接Model和UI)
书写相应的Controller部分,将UI的事件响应以及视图效果,Model的数据流,通过Controller连接,
使得UI和Model既可以通过Controller相互协作,又可以一定程度上相互独立地工作,使开发可以达到并行,且修改Model,UI时,在一定范围内只需要修改Controller即可,使开发简单高效。
主要将原来代码框架中的World的代码修改,添加UI部分作为主框架。
4)我们现在的题目是假设所有电梯到达所有的楼层。 在现实生活中, 多部电梯到达的楼层都不一样。如果是这样 (例如3号电梯能到达10 – 20 层, 4 号电梯能到达5-15 层),整个程序框架和你的电梯调度模块要做什么改变?
电梯:应该设置不能停靠楼层的限制;在电梯初始化时,将不能停靠的部分初始化。
乘客:首先应该在生成请求(generatereq)时检测是否合法,并且将请求中添加所请求电梯的电梯号属性,也就是每个电梯的请求是不同的。
调度模块:在电梯调度时,要考虑将要调度的楼层是否处于合法楼层。在请求处理中,应该分别处理不同种类的请求,设置每个电梯单独的上升列表,下降列表,完成调度。
也可以将每个电梯的请求上升列表,下降列表加入到电梯的属性,但这样框架的松耦合会不是很好,不建议。
建议:如果可以单双层运行,效率可能会更好些,但此时乘客的设计会比较复杂了。