IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 《数据结构、算法与应用 —— C++语言描述》学习笔记 — 队列 —— 应用 —— 工厂仿真(二) -> 正文阅读

[C++知识库]《数据结构、算法与应用 —— C++语言描述》学习笔记 — 队列 —— 应用 —— 工厂仿真(二)

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 队列 —— 应用 —— 工厂仿真(二)

一、数据统计与展示

1、声明

/*************************************************
Author: coding-hwz
Date:2021-08-23
Description: 数据统计类,用于数据统计和展示
**************************************************/

#pragma once
#include "../data/machine/CLS_Machine.h"
#include "../data/machine/CLS_MachineObserver.h"
#include <iostream>
#include <map>
#include <vector>

class CLS_Statistics : public CLS_MachineObserver
{
public:
    CLS_Statistics();

    /*
     * @brief 输出时间表
     * @param os 输出流对象
     */
    void outputTimeTable(std::ostream& os);

    /*
     * @brief 输出机器等待时间
     * @param os 输出流对象
     */
    void outputMachinesWaitingTime(std::ostream& os);

    /*
     * @brief 输出任务完成时间和等待时间
     * @param os 输出流对象
     */
    void outputJobsTime(std::ostream& os);

    /*
     * @brief 输出任务排队时间表
     * @param os 输出流对象
     */
    void outputJobQueueTimeTable(std::ostream& os);

    /*
     * @brief 机器状态更新回调
     * @param _pMachine 机器对象指针
     */
    virtual void updateStatus(CLS_Machine* _pMachine) override;

    /*
     * @brief 机器任务队列更新回调
     * @param _pMachine 机器对象指针
     * @param _pJob     任务对象指针
     */
    virtual void updateQueue(CLS_Machine* _pMachine, CLS_Job* _pJob) override;

private:
    std::map<int, std::vector<std::pair<CLS_Machine::EnumMachineStatus, int>>> m_mapExecuteTimeTable;   // 任务执行时间表映射(<机器编号,vector<机器状态,任务编号>>)
    std::map<int, int> m_mapMachinesWaitingTime;                                                        // 每个机器的等待时间映射(<机器编号,等待时间>)
    std::map<int, std::pair<int, int>> m_mapJobsTime;                                                   // 每个任务的时间映射(<任务编号,<任务长度,完成时间>>)
    std::map<int, std::vector<std::string>> m_mapJobQueueTimeTable;                                     // 每个机器上的任务队列时间表映射(机器编号,vector(队列字符串))
	std::map<int, std::vector<std::pair<CLS_Job*, int>>> m_mapAddingJobBeforeStatusChange;              // 在状态转换之前添加的任务映射(机器编号,vector<任务指针,任务加入机器时其当前状态持续时间>)

    /*
     * @brief 更新时间表
     * @param _pMachine 机器对象指针
     */
    void updateTimeTable(CLS_Machine* _pMachine);

    /*
     * @brief 更新机器等待时间
     * @param _pMachine 机器对象指针
     */
    void updateMachinesWaitingTime(CLS_Machine* _pMachine);

    /*
     * @brief 更新任务时间
     * @param _pMachine 机器对象指针
     */
    void updateJobsTime(CLS_Machine* _pMachine);

    /*
     * @brief 更新任务队列时间表
     * @param _pMachine 机器对象指针
     */
    void updateJobqueueTimeTable(CLS_Machine* _pMachine);
};

2、实现

#include "CLS_Statistics.h"
#include "../data/CLS_DataManager.h"
#include <algorithm>
#include <string>
using namespace std;

const int CONST_JOBID_NONE               = -1;
const string CONST_MACHINESTATUS_IDLE    = "I";
const string CONST_MACHINESTATUS_CONVERT = "C";
const string CONST_MACHINESTATUS_EXECUTE = "E";
const string CONST_MACHINENAME_PREFIX    = "M";
const string CONST_OUTPUTQUEUE_DELIMITER = ",";

CLS_Statistics::CLS_Statistics()
{
    // 初始化所有数据
    auto& machines = CLS_DataManager::getInstance().getMachines();
    auto initMachineFunc = [this](CLS_Machine& _machine)
    {
        m_mapExecuteTimeTable[_machine.getMachineId()] = vector<pair<CLS_Machine::EnumMachineStatus, int>>(); _machine.addWatch(this);
        m_mapMachinesWaitingTime[_machine.getMachineId()] = 0;
        m_mapAddingJobBeforeStatusChange[_machine.getMachineId()] = vector<std::pair<CLS_Job*, int>>();
        m_mapJobQueueTimeTable[_machine.getMachineId()] = vector<string>();
    };
    for_each(machines.begin(), machines.end(), initMachineFunc);

    auto& jobs = CLS_DataManager::getInstance().getJobs();
    for_each(jobs.begin(), jobs.end(), [this](CLS_Job& _job) { m_mapJobsTime[_job.getJobId()] = make_pair(0, 0); });
}

void CLS_Statistics::outputTimeTable(std::ostream& os)
{
    int iTotalTime = 0;
    for (auto& iterExecuteTimeTable : m_mapExecuteTimeTable)
    {
        if (iterExecuteTimeTable.second.size() > iTotalTime)
        {
            iTotalTime = iterExecuteTimeTable.second.size();
        }
    }

    os << "时间/机器编号\t";
    auto& machines = CLS_DataManager::getInstance().getMachines();
    vector<decltype(m_mapExecuteTimeTable.begin())> vecIterExecuteTimeTable; // 按机器编号顺序保存map中的迭代器,否则其访问顺序不定
    for (auto& machine : machines)
    {
        vecIterExecuteTimeTable.push_back(m_mapExecuteTimeTable.find(machine.getMachineId()));
        os << machine.getMachineId() << "\t";
    }
    os << endl;


    for (int time = 0; time < iTotalTime; ++time)
    {
        os << "\t" << time + 1 << "\t\t\t";
        for (auto& iterExecuteTimeTable : vecIterExecuteTimeTable)
        {
            string strStatus;
            string strJob;
            // 判断该机器是否在某个时间点之后已经没有再更新过状态
            if (iterExecuteTimeTable->second.size() <= time)
            {
                strStatus = CONST_MACHINESTATUS_IDLE;
                strJob = "";
            }
            else
            {
                switch (iterExecuteTimeTable->second.at(time).first)
                {
				case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Idle:
				{
					strStatus = CONST_MACHINESTATUS_IDLE;
					break;
				}
                case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Convert:
                {
                    strStatus = CONST_MACHINESTATUS_CONVERT;
                    break;
                }
				case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Execute:
				{
					strStatus = CONST_MACHINESTATUS_EXECUTE;
					break;
				}
                default:
                {
                    break;
                }
                }

                strJob = iterExecuteTimeTable->second.at(time).second != CONST_JOBID_NONE ? to_string(int(iterExecuteTimeTable->second.at(time).second)) : "";
            }

            os << strStatus << strJob << "\t";
        }

        os << endl;
    }
}

void CLS_Statistics::outputMachinesWaitingTime(std::ostream& os)
{
    os << "机器编号\t等待时间" << endl;
    auto outputFunc = [&os](pair<const int, int>& _pairMachinesWaitingTime) {os << "\t" << CONST_MACHINENAME_PREFIX << _pairMachinesWaitingTime.first << "\t\t\t" << _pairMachinesWaitingTime.second << endl; };
    for_each(m_mapMachinesWaitingTime.begin(), m_mapMachinesWaitingTime.end(), outputFunc);
}

void CLS_Statistics::outputJobsTime(std::ostream& os)
{
    os << "任务编号\t完成时间\t等待时间" << endl;
    auto outputFunc = [&os](pair<const int, pair<int, int>>& _pairJobsTime) 
    {
        os << "\t" << _pairJobsTime.first
           << "\t\t\t" << _pairJobsTime.second.second
           << "\t\t\t" << _pairJobsTime.second.second - _pairJobsTime.second.first
           << endl; };
    for_each(m_mapJobsTime.begin(), m_mapJobsTime.end(), outputFunc);
}

void CLS_Statistics::outputJobQueueTimeTable(std::ostream& os)
{
	int iTotalTime = 0;
	for (auto& iterMapExecuteTimeTable : m_mapExecuteTimeTable)
	{
		if (iterMapExecuteTimeTable.second.size() > iTotalTime)
		{
			iTotalTime = iterMapExecuteTimeTable.second.size();
		}
	}

    os << "时间/机器编号\t";
	auto& machines = CLS_DataManager::getInstance().getMachines();
	vector<decltype(m_mapJobQueueTimeTable.begin())> vecIterExecuteTimeTable;
	for (auto& machine : machines)
	{
		vecIterExecuteTimeTable.push_back(m_mapJobQueueTimeTable.find(machine.getMachineId()));
		os << machine.getMachineId() << "\t";
	}
    os << endl;

	for (int i = 0; i < iTotalTime; ++i)
	{
		os << "\t" << i + 1 << "\t\t\t";
		for (auto& iterExecuteTimeTable : vecIterExecuteTimeTable)
		{
            string strQueueOutput;
			if (iterExecuteTimeTable->second.size() > i)
			{
                strQueueOutput = iterExecuteTimeTable->second.at(i);
			}

            os << strQueueOutput << "\t";
		}

		os << endl;
	}
}

void CLS_Statistics::updateStatus(CLS_Machine* _pMachine)
{
    if (_pMachine == nullptr)
    {
        return;
    }
    
    // 首先更新时间表
    updateTimeTable(_pMachine);
    updateMachinesWaitingTime(_pMachine);
    updateJobsTime(_pMachine);
    updateJobqueueTimeTable(_pMachine);
}

void CLS_Statistics::updateQueue(CLS_Machine* _pMachine, CLS_Job* _pJob)
{
    int iDurationTime = _pMachine->getDurationTime();
    m_mapAddingJobBeforeStatusChange[_pMachine->getMachineId()].push_back(make_pair(_pJob, iDurationTime < 0 ? 0 : iDurationTime));
}

void CLS_Statistics::updateTimeTable(CLS_Machine* _pMachine)
{
    CLS_Machine::EnumMachineStatus iStatus;
    switch (_pMachine->getState())
    {
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Idled:
	{
		iStatus = CLS_Machine::EnumMachineStatus::enum_MachineStatus_Idle;
        break;
    }
    case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Converted:
    {
        iStatus = CLS_Machine::EnumMachineStatus::enum_MachineStatus_Convert;
        break;
    }
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Executed:
	{
		iStatus = CLS_Machine::EnumMachineStatus::enum_MachineStatus_Execute;	
        break;
    }
    default:
    {
        break;
    }
    }

    // 对于每个ed状态,表示在 _pMachine->getDurationTime() 对应的时间段内一直处于对应的非ed状态
    auto iterExecuteTimeTable = m_mapExecuteTimeTable.find(_pMachine->getMachineId());
    for (int i = 0; i < _pMachine->getDurationTime(); ++i)
    {
        iterExecuteTimeTable->second.push_back(make_pair(iStatus, _pMachine->getCurrentJob() ? _pMachine->getCurrentJob()->getJobId() : CONST_JOBID_NONE));
    }
}

void CLS_Statistics::updateMachinesWaitingTime(CLS_Machine* _pMachine)
{
    switch (_pMachine->getState())
    {
    case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Converted:
    case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Executed:
    { 
        int iTotalWaitTime = _pMachine->getJobQueue().size() * _pMachine->getDurationTime();
        auto iter = m_mapAddingJobBeforeStatusChange.find(_pMachine->getMachineId());
        for (auto& pairAddingJob : iter->second)
        {
            if (pairAddingJob.first != _pMachine->getCurrentJob())
            {
                // 去掉中间加入的任务未排队的时长
                iTotalWaitTime -= pairAddingJob.second;
            }
        }

        m_mapMachinesWaitingTime[_pMachine->getMachineId()] += iTotalWaitTime;
        //iter->second.clear(); 后面还需要使用这个迭代器指向的对象;此处实现的不好,需要外面保证 updateMachinesWaitingTime 和 updateJobqueueTimeTable 的调用流程
        break;
    }
    default:
    {
        break;
    }
    }
}

void CLS_Statistics::updateJobsTime(CLS_Machine* _pMachine)
{
	switch (_pMachine->getState())
	{
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Executed:
	{
		m_mapJobsTime[_pMachine->getCurrentJob()->getJobId()].first += _pMachine->getDurationTime();
		auto nextTask = _pMachine->getCurrentJob()->getNextTask();
		if (!nextTask.has_value())
		{
			m_mapJobsTime[_pMachine->getCurrentJob()->getJobId()].second = m_mapExecuteTimeTable.find(_pMachine->getMachineId())->second.size();
		}
        break;
	}
	default:
	{
		break;
	}
	}
}

void CLS_Statistics::updateJobqueueTimeTable(CLS_Machine* _pMachine)
{
	switch (_pMachine->getState())
	{
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Idle:
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Execute:
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Convert:
    {
        // 在非ed状态下保存队列字符串(临时保存,最后以“,”结尾)
		auto jobQueue = _pMachine->getJobQueue();
        string strQueue;
        while (!jobQueue.empty())
        {
            strQueue = strQueue + to_string(jobQueue.front()->getJobId()) + ",";
            jobQueue.pop();
        }

        m_mapJobQueueTimeTable[_pMachine->getMachineId()].push_back(strQueue);
        break;
    }
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Idled:
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Executed:
	case CLS_Machine::EnumMachineStatus::enum_MachineStatus_Converted:
	{
		// 取出当前机器对应排队的字符串
        auto iterJob = m_mapJobQueueTimeTable.find(_pMachine->getMachineId());
        string strLastQueue = ""; // 前一个加入队列的字符串
        if (!iterJob->second.empty())
        {
            strLastQueue = iterJob->second.back();
            m_mapJobQueueTimeTable[_pMachine->getMachineId()].pop_back();
        }

        auto iterAddingJobBeforeStatusChange = m_mapAddingJobBeforeStatusChange.find(_pMachine->getMachineId());
        int iLastAddJobTime = 0; // 前一个任务放入队列的时间(从前一个非ed状态开始计时)
        int iLastDurationTime = 0; // 前一个任务持续的时间
		for (auto& jobPtrAndAddTime : iterAddingJobBeforeStatusChange->second)
		{
            if (jobPtrAndAddTime.second == iLastAddJobTime)
            {
                // 同时加入的都放到相同字符串中后再根据放入时间决定放入vector的个数
                strLastQueue += to_string(jobPtrAndAddTime.first->getJobId()) + CONST_OUTPUTQUEUE_DELIMITER;
            }
            else
            {
				iLastAddJobTime = jobPtrAndAddTime.second;     
                for (; iLastDurationTime < jobPtrAndAddTime.second; ++iLastDurationTime)
                {
                    m_mapJobQueueTimeTable[_pMachine->getMachineId()].push_back(strLastQueue.substr(0, strLastQueue.size() - 1));
                }

                strLastQueue += to_string(jobPtrAndAddTime.first->getJobId()) + ",";
            }
		}

        // 处理最后放入的数据
		for (; iLastDurationTime < _pMachine->getDurationTime(); ++iLastDurationTime)
		{
			m_mapJobQueueTimeTable[_pMachine->getMachineId()].push_back(strLastQueue.substr(0, strLastQueue.size() - 1));
		}

		iterAddingJobBeforeStatusChange->second.clear();
        
		break;
	}
	default:
	{
		break;
	}
	}
}

二、测试代码

1、实现

#include ".\src\\service\CLS_DispatchManager.h"
#include ".\src\data\CLS_DataManager.h"
#include ".\src\service\CLS_Statistics.h"
#include <fstream>
#include <algorithm>
using namespace std;

void test()
{
    ifstream ifs("input.txt");
    ofstream ofs("output.txt");

    int iJobNum;
    int iMachineNum;
    ifs >> iMachineNum >> iJobNum;

    int iMachineId;
    int iChangeTime;
    for (int i = 0; i < iMachineNum; ++i)
    {
        ifs >> iMachineId;
        ifs >> iChangeTime;
        CLS_DataManager::getInstance().getMachines().push_back(CLS_Machine(iChangeTime, iMachineId++));
    }
    
    int iJobId;
    int iTaskNum;
    for (int i = 0; i < iJobNum; ++i)
    {
        ifs >> iJobId;
        ifs >> iTaskNum;
        
        CLS_Job job(iJobId);
        int iMachineId;
        int iTaskTime;
        for (int j = 0; j < iTaskNum; ++j)
        {
            ifs >> iMachineId;
            ifs >> iTaskTime;
            job.addTask(iMachineId, iTaskTime);
        }
        CLS_DataManager::getInstance().addJob(job);
    }

    CLS_Statistics statistics;
    CLS_DispatchManager dispatcher;
    dispatcher.executeLoop();


    statistics.outputTimeTable(ofs);
    statistics.outputMachinesWaitingTime(ofs);
    statistics.outputJobsTime(ofs);
    statistics.outputJobQueueTimeTable(ofs);
}

2、测试文件

3 6
1 2
2 0 
3 1
1 3
1 2 2 4 3 3
2 2
2 2 1 4
3 4
3 6 2 1 3 2 1 3
4 2
1 3 2 4
5 1
1 2
6 5
2 2 1 2 3 3 1 1 2 3

3、输出

下列输出没有打印各自展示的是哪部分数据(经过了手动对齐),从上到下依次为各机器状态时间表各机器等待时长各任务完成时间及等待时间各机器等待队列时间表

时间/机器编号	    1	2	3	
	1			I	I	I	
	2			E1	E2	E3	
	3			E1	E2	E3	
	4			C	E6	E3	
	5			C	E6	E3	
	6			E4	E1	E3	
	7			E4	E1	E3	
	8			E4	E1	C	
	9			C	E1	I	
	10			C	E3	E1	
	11			E5	E4	E1	
	12			E5	E4	E1	
	13			C	E4	C	
	14			C	E4	E3	
	15			E2	I	E3	
	16			E2	I	C	
	17			E2	I	I	
	18			E2	I	I	
	19			C	I	I	
	20			C	I	I	
	21			E6	I	I	
	22			E6	I	I	
	23			C	I	E6	
	24			C	I	E6	
	25			E3	I	E6	
	26			E3	I	C	
	27			E3	I	I	
	28			C	I	I	
	29			C	I	I	
	30			E6	I	I	
	31			C	E6	I	
	32			C	E6	I	
	33			I	E6	I	
机器编号	    等待时间
	M1			52
	M2			8
	M3			3
任务编号	    完成时间	    等待时间
	1			12			3
	2			18			12
	3			27			15
	4			14			7
	5			12			10
	6			33			22
时间/机器编号		1		2		3	
	1			1,4,5	2,6		3	
	2			4,5	6		
	3			4,5	6		
	4			4,5,2	1		
	5			4,5,2	1		
	6			5,2,6			
	7			5,2,6			
	8			5,2,6	3		
	9			5,2,6	3,4		
	10			5,2,6	4		
	11			2,6				3	
	12			2,6				3	
	13			2,6				3	
	14			2,6			
	15			6			
	16			6,3			
	17			6,3			
	18			6,3			
	19			6,3			
	20			6,3			
	21			3			
	22			3			
	23			3			
	24			3			
	25						
	26			6			
	27			6			
	28			6			
	29			6			
	30						
	31						
	32						
	33						

三、总结

这个设计的结构还是比较明确的,每个类都有各自的职责。但是,问题1 — 对于 CLS_Statistics 来说,它要统计的数据过于多,导致代码臃肿且相互耦合性过高。例如,我们这里要求 updateMachinesWaitingTimeupdateJobqueueTimeTable 两个函数的调用顺序是一定的,因为它们使用了相同的数据,而该数据在被它们使用完后才能清理(可以选择将该数据提取到外面进行删除)。除此之外,其实每个需要统计的数据都可以单独成一个类,一开始我并没想统计这么多数据,所以疏忽了这部分设计。

如果想把它们各自独立,还有一个很严重的问题:问题2,没有独立的计时模块。不难发现,我们这里所有的计时功能都是通过时间表来统计的。这样的即使很不完善,也不方便我们将各模块独立。计时功能应该单独成一个模块,需要事件的对象去该模块中取时间即可。一开始,我的设计是按照书中的事件驱动方式来的,完全在一个函数中模拟时间的流动。后来发现这完全没有利用C++面向对象和封装的特性,因此才重新设计了一版。但是,其中还是残留了一些面向过程编程的痕迹。

还有一个问题是:问题3,使用串行的模式模拟并行过程,业务逻辑较为复杂。这里最说的就是队列时间表的输出。我们需要监控所有的状态,而不是像其它功能一样监控需要的2-3个状态即可。而且我们要了然它们是如何相互关联的。幸运也是很重要的一点是,我们这里确定了状态转换过程中机器对象内部数据的处理原则。这在一定程度上简化了设计。

最后一个小问题是:问题4,输入模块也可以独立。这里,我们手动在外面实现了输入和数据初始化功能。这部分功能可以独立成为工程中的一个模块。其要求用户传入按照固定的模式(json等)写成的配置文件进行数据初始化。这样可以减少用户的输入和需要支持的功能。

总的来说,这个工厂仿真应用确实让我们练习了观察者模式。但是在设计和模块化的道路上还有很远需要走。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-24 15:22:34  更:2021-08-24 15:23:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 6:17:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计