软件工程项目:电梯调度

软件工程结对项目:电梯调度
需要相关资源的:qq 1678896503
第一阶段:接口定义

阶段目标:

设计一组可用于电梯调度的接口和类定义。主要考虑:
简单性
如何提供足够的信息给调度器,以便于能顺利完成调度?
实际驱动电梯的组件是什么?
对何规定乘客的行为?例如当乘客需要从3层到20层时,但是当前电梯不能直达,乘客应如何行动?

问题分析:

问题假设:
一幢21层的大厦,有4部电梯,乘客的体重:平均70kg,最大120kg,最小40g)。
其他的常量包括:电梯的速度,电梯门开关时间,乘客进出电梯的时间。
大厦的楼层为-1,0,…,20,-1层是地下停车场,1层是大厅。以下是4部电梯的参数:

电梯编号服务楼层乘客人数限制重量限制
1所有楼层10800公斤
21-10层10800公斤
3-1,1-10层201600公斤
4-1,1,11-20层202000公斤

以上参数信息是可以修改,即电梯调度程序可以在初始化时读取配置信息来设置上述参数值。

阶段分析:

电梯调度算法的种类:

先来先服务算法(FCFS)
先来先服务(FCFS-First Come First Serve)算法,是一种随即服务算法,它不仅仅没有对寻找楼层进行优化,也没有实时性的特征,它是一种最简单的电梯调度算法。
它根据乘客请求乘坐电梯的先后次序进行调度。此算法的优点是公平、简单,且每个乘客的请求都能依次地得到处理,不会出现某一乘客的请求长期得不到满足的情况。
这种方法在载荷较轻松的环境下,性能尚可接受,但是在载荷较大的情况下,这种算法的性能就会严重下降,甚至恶化。
人们之所以研究这种在载荷较大的情况下几乎不可用的算法,有两个原因:
任何调度算法在请求队列长度为1时,请求速率极低或相邻请求的间隔为无穷大时使用先来先服务算法既对调度效率不会产生影响,而且实现这种算法极其简单。
先来先服务算法可以作为衡量其他算法的标准。
最短寻找楼层时间优先算法(SSTF)
最短寻找楼层时间优先(SSTF-Shortest Seek Time First)算法,它注重电梯寻找楼层的优化。
最短寻找楼层时间优先算法选择下一个服务对象的原则是最短寻找楼层的时间。
这样请求队列中距当前能够最先到达的楼层的请求信号就是下一个服务对象。
在重载荷的情况下,最短寻找楼层时间优先算法的平均响应时间较短,但响应时间的方差较大,原因是队列中的某些请求可能长时间得不到响应,出现所谓的“饿死”现象。
扫描算法(SCAN)
扫描算法(SCAN) 是一种按照楼层顺序依次服务请求,它让电梯在最底层和最顶层之间连续往返运行,在运行过程中响应处在于电梯运行方向相同的各楼层上的请求。
它进行寻找楼层的优化,效率比较高,但它是一个非实时算法。扫描算法较好地解决了电梯移动的问题,在这个算法中,每个电梯响应乘客请求使乘客获得服务的次序是由其发出请求的乘客的位置与当前电梯位置之间的距离来决定的。
所有的与电梯运行方向相同的乘客的请求在一次电向上运行或向下运行的过程中完成,免去了电梯频繁的来回移动。
扫描算法的平均响应时间比最短寻找楼层时间优先算法长,但是响应时间方差比最短寻找楼层时间优先算法小,从统计学角度来讲,扫描算法要比最短寻找楼层时间优先算法稳定。
LOOK 算法
LOOK 算法是扫描算法(SCAN)的一种改进。对LOOK算法而言,电梯同样在最底层和最顶层之间运行。但当 LOOK 算法发现电梯所移动的方向上不再有请求时立即改变运行方向,而扫描算法则需要移动到最底层或者最顶层时才改变运行方向。
SATF 算法
SATF(Shortest Access Time First)算法与 SSTF 算法的思想类似,唯一的区别就是 SATF 算法将 SSTF 算法中的寻找楼层时间改成了访问时间。这是因为电梯技术发展到今天,寻找楼层的时间已经有了很大地改进,但是电梯的运行当中等待乘客上梯时间却不是人为可以控制。SATF 算法考虑到了电梯运行过程中乘客上梯时间的影响。

类和接口的定义:

乘客:
属性: 乘客编号、体重、当前所在楼层、当前要去的楼层、最终目的楼层
方法: 1获得体重
2获得当前所在楼层
3获得当前要去的楼层
4获得最终目的楼层
5判断当前该去哪个楼层-根据乘客情况选出该去楼层(当前所在楼层,最终目的楼层)
6叫电梯 Call(&Class 调度系统) //调用电梯系统的接受处理函数,所有电梯都来
7等电梯 Wait() //循环等待直到分配的最佳电梯到达当前所在楼层,返回该电梯
8上电梯 Getin(&Class 电梯) //先调用电梯的超重超人数方法,判断是否能进入该电梯,不能则返回0,能则互斥修改电梯的当前重量和当前人数并返回1
9按楼层按钮 Press(&Class 电梯) //调用该电梯的改变工作链表函数插入要去 的楼层(互斥)
10 下电梯 Getoff(&Class 电梯) //循环等待电梯到站,到站后互斥修改电梯当前重量、人数,修改当前所在楼层
11 判断是否到达最终楼层

电梯
属性: 服务范围、限制人数、限制重量、mutex1(互斥修改当前人数和重量)、mutex2(互斥修改工作链表)、当前所在楼层、目标楼层、当前重量、当前人数、电梯状态(运行中、闲置、到站)、电梯当前运行方向(上、下、0)、工作列表
方法: 各种获得……
1 判断给定的楼层是否在自己服务范围内
2 判断是否超重
3 判断是否超人数
4 插入工作链表,插入之前判断是否在服务范围
5 设置目标楼层 //按c-look算法选出目标楼层并设置目标楼层(若工作链表为空则设为一个特殊值)
6 运行 //首先调用选出目标楼层函数,(判断若工作链表为空直接置电梯状态为闲置后return),然后与当前所在楼层比较设置相应运行方向,设置电梯状态为运行中,每隔n秒修改一次当前楼层,直至到达目标楼层,调用到站函数。
7 到站 //设置电梯状态为到站状态,等待k秒,调用运行函数

调度系统:
属性:四个电梯,电梯表(21*4)
方法:1 接收处理(楼层) //先筛选在服务范围的电梯,插入工作链表并设置目标楼层,如果该电梯闲置还要额外调用电梯的运行函数
2 根据乘客情况选出该去楼层()(返回楼层值)

调度器信息和电梯驱动

调度器主要接受来自乘客的请求信息,由楼层信息和行动方向组成,在同时接收多个请求时,处理并为每个请求分配电梯。电梯接受来自调度器的调度信息之后,按照电梯运行的C-LOOK算法,对所有请求进行满足。

乘客换乘问题

由于电梯服务楼层的限制,有些乘客的请求并不能由一部电梯满足,必须采取换乘措施。换乘楼层必须有多部电梯工作,到达换乘楼层后判断是否到达最终楼层,若否则继续换乘,直到到达最终楼层。

UML类图

在这里插入图片描述
第二阶段:乘客,电梯,调度系统中算法的实现

编写设计的规范和对于异常的处理

设计方面,乘客和电梯都是独立活动的个体,在程序中体现为线程,互不干扰,独立自主的执行相应的流程。对乘客和电梯的类定义时,设计相应接口时,对数据的需求相对独立,防止产生线程之间资源的相互影响。
代码实现和编写规范:代码规范化基本上有七大原则,体现在空行、空格、成对书写、缩进、对齐、代码行、注释七方面的书写规范上。在编写过程中对于数据的处理,主要有数据类型的一致性,控制指针的产生和消失,避免产生野指针,所有数据的初始化,数据传递的独立性。对于代码的注释,在各个接口的函数实现部分都有相应的注释,说明功能和注意事项。
对于异常的处理:对于程序运行过程中出现的异常,首先通过CPPCHECK进行分析,找出问题的定位处,针对性解决。遇到程序运行过程中出现的崩溃,采用断点调试的方法,定位导致崩溃的代码位置,进行分析和改正。

算法设计的重点

电梯的C-Look算法
传统的C-LOOK算法在电梯上表现为,循环从顶楼跑到底楼,这样优点是可行性和代码编写较为简易,但是效率上存在改进的空间。对传统的C-LOOK算法进行改进,将电梯的活动范围改为当前工作列表的最高楼层和最低楼层,这样电梯会实时根据工作列表中的数据进行实时的范围变化,在效率上有很大的改善。具体的实现方式,在设置电梯的下一个运行目的地时,首先对电梯的工作列表进行排序,对方向进行判断之后,选择C-LOOK的算法下的最佳抵达楼层。

void elevator::SetTargetFloor()//读取工作链表时互斥,改进的C-LOOK算法
{
    sort(worklist.begin(), worklist.end());
	if (Direction == up)
	{
		int flag = 0;
        for (size_t i = 0; i < size_t(worklist.size()); i++)
		{
			if (worklist[i] >= Currentfloor)
			{
                Targetfloor = worklist[i];
				flag = 1;
                break;
			}	
		}
		if (flag == 0)
			Targetfloor = worklist.back();
	}
	if (Direction == down)
	{
		int flag = 0;
        for (int i = int(worklist.size()-1); i >=0 ; i--)
		{
            if (worklist[size_t(i)] <= Currentfloor)
			{
                Targetfloor = int(worklist[size_t(i)]);
				flag = 1;
                break;
			}
		}
		if (flag == 0)
			Targetfloor = worklist.front();
	}

}

电梯的运行(Run())函数
电梯的状态分为 rest,running, arrive, start 四种,rest表示电梯目前没有任务,处于闲置状态,running为运行状态,arrive为到站状态,start为rest之后的启动状态,用于启动处于闲置的电梯。电梯的运行,分为两个部分,调用SetTargetFloor(),即电梯的C-LOOK算法得出下一个目标楼层,然后根据目标楼层对电梯的方向进行更新,保证下一步正确运行。运行过程中会等待到站,在到站(Arrive())函数中也会重新调用运行(Run())函数重新启动电梯。

if (worklist.empty())
	{
		State = rest;
        QString s = "NowState:Rest";
        emit(ChangeNowState(i,s));
        emit(ChangeColor(i ,Currentfloor ,0));
		Direction = none;
		return;
	}
	SetTargetFloor();
	int change = 0;
	if (Targetfloor <= Currentfloor)
	{
		 Direction = down;
         QString s = "NowState:Down";
         emit(ChangeNowState(i,s));
		 change = -1;
	}
	else
	{
		Direction = up;
        QString s = "NowState:Up";
        emit(ChangeNowState(i,s));
		change = 1;
	}
    State = running;
	while (1)//电梯运行,改变楼层
	{
        Sleep(1500);
		if (Targetfloor == Currentfloor)
			break;
        emit(ChangeColor(i ,Currentfloor ,1));
		Currentfloor += change;
        emit(ChangeColor(i ,Currentfloor ,2));
	}
    Arrive(i);

电梯的到站(Arrive())函数
电梯到站,会存在乘客上下的问题,所以乘客上下站的时候,电梯会休眠一段时间再继续运行。继续运行调用运行(Run())函数,运行函数又会重复相同的步骤驱动电梯。

void elevator::Arrive(int i)
{

    listmutex.lock();
    for (int j = 0; j < int(worklist.size()); j++)
	{
		
        if (worklist[size_t(j)] == Currentfloor)
            worklist.erase(worklist.begin()+j);//互斥修改

	}
    listmutex.unlock();
    State = arrive;
    Sleep(3000);
    Run(i);
}

涉及换乘时,乘客的行为问题
乘客的换乘问题是电梯系统模拟的难点之一。在乘客的换乘问题中,考虑到四部电梯的调度,乘客的行为存在四种,分别是直达,换乘一次,换乘两次,和换乘三次。在对调度系统的初始化过程中,四部电梯也被初始化,同时我们用一个21*4的数组记录了四部电梯服务范围在21个楼层的分布情况,便于后期对电梯服务范围的判断。换乘的具体思想是,选出服务乘客当前楼层的电梯,和服务乘客最终楼层的电梯,每次从两组中各选出一个,遍历其余电梯,以存在公共服务楼层为条件,找出一条路径,能够从当前楼层到达最终楼层的换乘线路,返回当前楼层电梯和路径上下一个电梯的公共楼层。这样,乘客就会在返回的公共楼层下电梯,这时候,判断是否到达最终楼层,如果没到,则在此调用换乘函数,直到抵达。此换乘算法的优点是,在第一次执行换乘函数之后,不用人为继续跟进乘客,乘客会自主选择,实现随乘客行为的模拟,为之后整个系统的模拟带来了巨大的便利。
乘客换乘函数的实现:

int passenger::ChooseNextfloor(Scheduler* s)//5判断当前该去哪个楼层 - 根据乘客情况选出该去楼层(当前所在楼层,最终目的楼层)
{
    for (int i = 0; i < 4; i++)
    {//直达
        if (s->elevatorlist[i].IsInServiceRange(Currentfloor) && s->elevatorlist[i].IsInServiceRange(Finalfloor))
        {
            Targetfloor = Finalfloor;
            return Finalfloor;
        }
    }

    for (int i = 0; i < 4; i++) {//换乘一次的
        if (s->ElevatorForm[Currentfloor][i] == 1)
        {
            //换乘一次的

            int high;
            int low;
            if (Currentfloor > Finalfloor)
            {
                high = Currentfloor;
                low = Finalfloor;
            }
            else
            {
                high = Finalfloor;
                low = Currentfloor;
            }
            for (int j = low; j <high; j++)
            {
                for (int k = 0; k < 4 ; k++)
                {
                    if (k == i)
                        continue;
                    if (s->ElevatorForm[j][i] == 1 && s->ElevatorForm[Finalfloor][k] == 1 && s->ElevatorForm[j][k] == 1)
                    {
                        Targetfloor = j;
                        return j;
                    }
                }
            }
            for (int j = 0; j < low; j++) {
                for (int k = 0; k < 4; k++) {
                    if (k == i)
                        continue;
                    if (s->ElevatorForm[j][i] == 1 && s->ElevatorForm[Finalfloor][k] == 1 && s->ElevatorForm[j][k] == 1)
                    {
                        Targetfloor = j;
                        return j;
                    }
                }
            }
            for (int j =21; j>high; j--)
            {
                for (int k = 0; k < 4; k++)
                {
                    if (k == i)
                        continue;
                    if (s->ElevatorForm[j][i] == 1 && s->ElevatorForm[Finalfloor][k] == 1 && s->ElevatorForm[j][k] == 1)
                    {
                        Targetfloor = j;
                        return j;
                    }
                }
            }
        }
    }
    //换乘两次的
    for (int i = 0; i < 4; i++)
    {
        if (s->ElevatorForm[Currentfloor][i] == 1)
        {
            for (int j = 0; j < 4; j++)
            {
                if (j == i)
                    continue;
                if (s->ElevatorForm[Finalfloor][j] == 1)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        if (k == i||k==j)
                            continue;
                        if (CompareEleRange(s, k, i) && CompareEleRange(s, k, j))
                        {//找到路径
                            //找一个公共楼层
                            Targetfloor = FindBestCommonFloor(s, k, i);
                            return Targetfloor;
                        }
                    }
                }
            }
        }
    }
    //换乘三次的
    for (int i = 0; i < 4; i++)
    {
        if (s->ElevatorForm[Currentfloor][i] == 1)
        {
            for (int j = 0; j < 4; j++)
            {
                if (j == i)
                    continue;
                if (s->ElevatorForm[Finalfloor][j] == 1)
                {
                    int sss[2];
                    int ss = 0;
                    for (int k = 0; k < 4; k++)
                    {
                        if (k != i && k != j)
                            sss[ss++]=k;
                    }
                    if (CompareEleRange(s, i, sss[0])==1&&CompareEleRange(s, sss[0], sss[1])==1&&CompareEleRange(s, sss[1], j)==1)
                    {

                        Targetfloor = FindBestCommonFloor(s, i, sss[0]);
                        return Targetfloor;
                    }
                    if (CompareEleRange(s, i, sss[1]) == 1 && CompareEleRange(s, sss[0], sss[1]) == 1 && CompareEleRange(s, sss[0], j) == 1)
                    {
                        Targetfloor = FindBestCommonFloor(s, i, sss[0]);
                        return Targetfloor;
                    }
                }
            }
        }
    }
    return 0;
}

其中,在换乘函数中,为了提高换乘的效率,我们对乘客换乘的楼层进行的优化,优先选择当前楼层和目标楼层之间的楼层,如下:

int passenger::FindBestCommonFloor(Scheduler* s, int one, int two) {
    if (Currentfloor>Finalfloor) {
        for (int j = Currentfloor; j >= 0; j--) {
            if (s->ElevatorForm[j][one] == 1 && s->ElevatorForm[j][two] == 1)
                return j;
        }
        for (int j = Currentfloor; j<=21; j++) {
            if (s->ElevatorForm[j][one] == 1 && s->ElevatorForm[j][two] == 1)
                return j;
        }
        return 0;
    }
    else {
        for (int j = Currentfloor; j <= 21; j++) {
            if (s->ElevatorForm[j][one] == 1 && s->ElevatorForm[j][two] == 1)
                return j;
        }
        for (int j = Currentfloor; j >= 0; j--) {
            if (s->ElevatorForm[j][one] == 1 && s->ElevatorForm[j][two] == 1)
                return j;
        }
        return 0;
    }
}

电梯调度系统类的初始化问题
在调度系统初始化过程中,涉及到从界面UI到主函数的数据传递,涉及到不同CPP之间的数据传递。实现不同CPP之间的数据传递,采用将全局变量定义成一个类的静态变量,通过使用类名::变量名进行调用的方法。设置一个Message类,将需要传递的信息放在Message类中,并且设置为Static,这样在其他CPP中,只要包含Message类的头文件,都可以修改和访问对应的信息。
电梯调度系统类中包含电梯类的对象数组,C++没有给对象数组提供专门的初始化方法。为了解决这个问题,首先电梯类要有一个无参的构造函数,再额外编写一个Init函数,以便于在电梯调度系统的构造函数中,循环调用Init函数来初始化电梯类数组。

void elevator::Init(int range[21], int maxnum, int maxweight)//Init函数
{
	for (int i = 0; i < 21; i++)
	{
		ServiceRange[i] = range[i];
	}
	MaxNumber = maxnum;
	MaxWeight = maxweight;
	Currentfloor = 1;
	Targetfloor = 0;
	Currentweight = Currentnumber = 0;

	State = rest;
	Direction = none;
	worklist.clear();
}
Scheduler::Scheduler(int range[4][21], int maxnum[4], int maxweight[4])//初始化电梯类数组
{
	; for (int i = 0; i < 4; i++)
	{
		elevatorlist[i].Init(range[i], maxnum[i], maxweight[i]);
	}
	for (int i = 0; i < 21; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			ElevatorForm[i][j] = range[j][i];
		}
	}
}

主函数的编写
主函数主要包含两个部分,电梯线程的生成和乘客线程的生成。首先,编写电梯和乘客的线程启动函数,在线程创建时自动调用。
电梯的启动函数,只要循环判断电梯的状态是否被置为Start,如果电梯被置为Start,则说明电梯的服务列表变为非空,电梯启动;

void StartElevator(Scheduler *s,int i) {

    while (1)
    {
        if (s->elevatorlist[i].GetState() == elevator::start)
        {
            s->elevatorlist[i].Run(i);
        }
        Sleep(100);
    }
}

乘客的启动函数,则是创建一个乘客和模拟这个乘客的行为。首先,乘客呼叫电梯,等待电梯,等待电梯到站后,等待下电梯的乘客全部下电梯,判断自己是否能够进入电梯,存在超重和超人数的限制。如果未能进入电梯,等待当前电梯走之后,再次呼叫电梯。如果进入电梯,则在电梯按下自己通过换乘函数返回的对应楼层,等待电梯到站后,判断自己是否到达最终楼层,没到则再次执行该过程。

void PassengerBehaviour(int Num,int Weight,int CurrentFloor,int FinalFloor,Scheduler *System,int time) {
    Sleep(DWORD(time));//乘客生成时间
    passenger person(Num,Weight,CurrentFloor,FinalFloor);
    while (1)
    {
        person.ChooseNextfloor(System);
        while(1)
        {
            person.Call(System);

            int Ele=person.Wait(System);

            while(flooroff[Ele][person.GetCurrentfloor()]!=0);
            if (person.Getin(System,Ele) == 1)
            {
                while (testmutex3(offmutex));
                flooroff[Ele][person.GetTargetFloor()]++;
                offmutex=1;
                person.Press(System,Ele);
                person.Getoff(System,Ele);
                while (testmutex3(offmutex));
                flooroff[Ele][person.GetCurrentfloor()]--;
                offmutex=1;
                break;
            }
            else
            {
                while(System->elevatorlist[Ele].GetCurrentFloor()==person.GetCurrentfloor());//等当前电梯走
            }
        }
        if (person.ArriveFinalfloor())
        {
            //输出乘客已到站
            break;
        }
    }
}

第三阶段:Ui界面的设计以及与线程之间的交互
电梯设置界面
电梯的设置界面,最重要的一点是支持多选的实现。刚刚开始,准备采用84个按钮来模拟四部电梯,但是工程量过于庞大,在以后的代码里面也不好对其进行操作。我们采用放置四个QTableWidget的方法,循环21次,创建Item并设置好信息插入QTableWidget,代表四个电梯的21层。在界面中选择服务楼层时候,右键选择,会出发信号,调用写好的onContextMenu函数,此函数调用PrintAllSelect函数,将所选信息放入Message中,实现传递,并且在当前界面显示选择的楼层信息。

for(int i = 0; i < 21; i++){//生成楼层
        if(i==0){
            QTableWidgetItem *item = new QTableWidgetItem(QString::number(-1));
            ui->Range1->setItem(i, 0, item);
        }
        else{
            QTableWidgetItem *item = new QTableWidgetItem(QString::number(i));
            ui->Range1->setItem(i, 0, item);
        }
    }
    void Widget::onContextMenu1(const QPoint &pt)//信号的槽函数
{
    QMenu menu;
    menu.addAction("Choose",this, SLOT(printAllSelect1()));
    menu.exec(ui->Range1->mapToGlobal(pt));
}
void Widget::printAllSelect1()//信息展示和传递函数
{
    QString str;
    QList<QTableWidgetItem*> tableItemList = ui->Range1->selectedItems();
    for(int i = 0; i < tableItemList.size(); i++){

        str.append(tableItemList[i]->text() + ",");
        int k=tableItemList[i]->text().toInt();
        if(k==-1)
            k=0;
        Message::EleForm[0][k]=1;
    }
    str = str.left(str.size() - 1);
    QMessageBox::information(this, "tip", str);
}

设置界面:
在这里插入图片描述

电梯运行界面
电梯的运行界面,具有4*21个button来表示电梯的每一层,同时具有展示电梯基本信息的label和textbroswer。基本数据结构如下:

QPushButton *floor[4][21];
    QLabel *MaxNum[4];
    QLabel *MaxWeight[4];
    QLabel *NowNum[4];
    QLabel *NowWeight[4];
    QLabel *NowState[4];
    QStringList PassengerList[4];

在Elevator和Passenger函数实现中,在特定的地方会发出信号来改变电梯运行界面的内容。

signals://人的信号和连接
    void AppendList(int,QString);
    void RemoveList(int,QString);
    void ChangeEvent(QString);
  connect(this,SIGNAL(ChangeEvent(QString)),mainui,SLOT(ChangeEventText(QString)));
    connect(this,SIGNAL(AppendList(int,QString)),mainui,SLOT(AddPeople(int,QString)));
    connect(this,SIGNAL(RemoveList(int,QString)),mainui,SLOT(RemovePeople(int,QString)));
    signals://电梯的信号和连接
    void ChangeColor(int ,int ,int);
    //void ChangeMaxNum(int,QString);
    //void ChangeMaxWeight(int,QString);
    void ChangeNowNum(int,QString);
    void ChangeNowWeight(int,QString);
    void ChangeNowState(int,QString);
    void ChangeEvent(QString);
     connect(this,SIGNAL(ChangeColor(int,int,int)),mainui,SLOT(ChangeButtonColor(int,int,int)));
    connect(this,SIGNAL(ChangeNowNum(int,QString)),mainui,SLOT(ChangeNowNumText(int,QString)));
    connect(this,SIGNAL(ChangeNowWeight(int,QString)),mainui,SLOT(ChangeNowWeiText(int,QString)));
    connect(this,SIGNAL(ChangeNowState(int,QString)),mainui,SLOT(ChangeNowStateText(int,QString)));
    connect(this,SIGNAL(ChangeEvent(QString)),mainui,SLOT(ChangeEventText(QString)));
public slots://电梯运行界面的响应槽函数
    void ChangeButtonColor(int,int ,int);
    void ChangeNowNumText(int,QString);
    void ChangeNowWeiText(int,QString);
    void ChangeNowStateText(int,QString);
    void ChangeEventText(QString);
    void AddPeople(int,QString);
    void RemovePeople(int,QString);

电梯运行效果:
在这里插入图片描述

第四阶段:代码测试以及效果展示
代码测试主要集中于电梯的功能实现和效率问题。功能测试集中在乘客从各个楼层出发,到达各个楼层目的地的完成情况,包含直达和换乘。效率测试主要对比与BUS算法和普通的C-LOOK算法。
功能测试:由于代码实现乘客的行为方式非常适合大量数据放入程序中运行,乘客的行为由乘客“自己决定”,我们的功能测试采用随机生成大量乘客,规定不同电梯服务范围的方式,对代码各个功能的分支进行覆盖。

vector<thread> PassengerThread;//生成多个乘客线程
    for(int k=0;k<100;k++)
    {
        int Weight=QRandomGenerator::global()->bounded(40, 150);
        int FinalFloor;
        int CurrentFloor=QRandomGenerator::global()->bounded(0, 20);
        while(1)
        {
            FinalFloor=QRandomGenerator::global()->bounded(0, 20);
            if(FinalFloor!=CurrentFloor)
                break;
        }
        int time=QRandomGenerator::global()->bounded(1000, 30000);
        PassengerThread.push_back(thread(PassengerBehaviour,k,Weight,CurrentFloor,FinalFloor,sys,time));
    }
    for(auto& i:PassengerThread){
        i.detach();
    }

性能测试:
对比BUS算法和普通的C-LOOK算法,从时间上 改进的C-LOOK<C-LOOK<BUS。
对于三十个乘客:改进的C-LOOK 36.5S,C-LOOK 50S,BUS 96S。

结对项目的体会(附讨论照片):
结对编程的优点和缺点:
优点是能够集思广益,及时有效的解决遇到的问题。明确的分工使每个人的长处得到发挥,在设计方案的过程中,能够更加全面的考虑问题;编写代码时能够互相监督,及时发现代码中的错误和遗漏;在项目完成的辛苦过程中,能够相互帮助,遇到难缠的问题能够互相鼓励和支持。在结对工作的过程中,双方的默契程度在不断上升,能够及时沟通,使得代码编写过程较为流畅。
缺点是本次项目在线上,恰逢春节期间,走亲访友耽搁了一些时间,且项目的三个阶段并不紧凑,导致项目完成的时间周期较长。
成员一(陈天问):优点, 态度积极,乐于探索,对待问题具有耐心,缺点:偶尔迟到
成员二(陈彦达):优点,考虑问题非常的全面,对待错误一丝不苟,乐于探索,缺点:偶尔迟到
代码编写中解决问题带来的收获
技术方面:
1.CPP之间传递信息的解决
2.设置电梯服务范围,多选问题的解决
3.采用事件循环实现主线程阻塞直到界面关闭
4.向线程启动函数传递多个参数
精神层面:
在代码编写和测试的过程中,遇到了很多意想不到的情况。比如,野指针导致主线程的崩溃,却怎么也找不到,一天都卡在一个问题上,长时间解决不了一个问题,对心理和生理都是巨大的。在代码的算法设计上,如何找到一个效率和可行性的算法也对我们的耐心和探索能力有着很高的要求,我们也在这个过程中不断提升自己,适应高强度的的工作压力。无疑对于我们来说是一种宝贵的经历。

附录:PSP2.1表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划54
· Estimate· 估计这个任务需要多少时间54
Development开发34502515
· Analysis· 需求分析 (包括学习新技术)240190
· Design Spec· 生成设计文档12060
· Design Review· 设计复审 (和同事审核设计文档)3030
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)3015
· Design· 具体设计120120
· Coding· 具体编码19201470
· Code Review· 代码复审3030
· Test· 测试(自我测试,修改代码,提交修改)960600
Reporting报告330230
· Test Report· 测试报告120100
· Size Measurement· 计算工作量3010
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划180120
合计37852749
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值