软件设计开发笔记5:QT开发三参数温室气体数据记录软件

  最近有一个为三参数温室气体分析仪及其多通道换向阀箱编写数据记录和控制的需求。所以在这一篇中我们就来分析一下如何使用QT实现这一需求。

1、需求分析

  虽然说传递过来的需求只有“实现一个三参数温室气体分析仪及其多通道换向阀箱的数据记录和控制”这样一句话,但所有人都清楚实际需求并非表达的那么简单。所以,我们首先还是来分析一下实现这样一个软件的具体需求。
  一方面,这个软件需要获取三参数温室气体分析仪的数据并将其记录下来。那么我们就需要知道究竟需要哪些数据?这些数据需要记录成什么样的格式?这些数据是否需要处理?这些数据是否需要展现?这些数据如果展现需要以什么样的形式展现?这些问题其实都包含在需求当中,只是没有直接表达出来,或者说提出需求的人他也不知道该怎么表达出来。但是我们必须考虑这些问题。
  另一方面,这个软件需要获取多通道换向阀箱的数据并对其进行控制。同样存在上述的哪些问题需要解决。此外对于多通道切换阀的控制也要考虑具体的方式。是采用自动控制方式还是手动控制方式。如果手动控制那还要考虑具体怎么操作。如果采用自动控制方式具体的运行逻辑有什么要求。这些问题其实也是需要开率并实现的。
  至于其它如稳定性和可靠性等方面的要求基本就得按工业标准来考虑,再次不做具体讨论了。

2、设计与实现

  根据上面的分析我们来考虑如何设计这一软件。我们考虑需要获取分析仪和阀箱的数据,但从物理角度来说这是2个独立的设备,所以我们通过独立的模块来实现它。数据记录下来后,他们可能还需要对数据进行查看,所以我们在增加一个数据查询的模块。
  从外观实现上来说,我们也是在主体框架下采用3个操作页面来对应上述的3个模块。这3个界面分别为:“实时数据”、“数据查询”和“阀箱交互”,具体如下图:

  上述的界面能够满足数据显示、查询接操作。但对于与两台设备通讯我们还需要配置串口的相关参数,所以我们增加一个参数配置界面,具体如下所示:

  完成界面设计后,我们考虑功能的实现。首先是分析仪数据的获取,分析仪是RS232接口,通讯协议为厂商自定义。所以我们只需要按照厂商的协议解析数据包就可以实现数据的获取。关键是数据报文不能保证每一帧都是完整无误的,所以我们解析式需要做一些条件判断。场上的具体协议不方便发布,这里给出主要代码:

void AnalyserForm::DataParsing()
{
    QString context;

    if(rxDatas.indexOf(0x23)>=0)
    {
        rxDatas.remove(0,rxDatas.indexOf(0x23));
    }
    else
    {
        qDebug()<<rxDatas.size();
        qDebug()<<rxDatas;
        rxDatas.clear();
        return;
    }

    if((rxDatas.indexOf(0x0D)<0)||(rxDatas.indexOf(0x0A)<0))
    {
        return;
    }

    if((rxDatas.indexOf(0x0D)<433)||(rxDatas.indexOf(0x0A)<434))
    {
        rxDatas.remove(0,rxDatas.indexOf(0x0A));
        return;
    }

    QByteArrayList list=rxDatas.split(';');

    datetime=list[6].trimmed().mid(14,-1);
    co_d_ppb=DataConversion(list[7].trimmed());
    co2_d_ppm=DataConversion(list[8].trimmed());
    ch4_d_ppm=DataConversion(list[9].trimmed());
    co_ppb=DataConversion(list[10].trimmed());
    co2_ppm=DataConversion(list[11].trimmed());
    ch4_ppm=DataConversion(list[12].trimmed());
    h2o_pct=DataConversion(list[13].trimmed());
    temp_cav=DataConversion(list[14].trimmed());
    pres_cav=DataConversion(list[15].trimmed());
    temp_box=DataConversion(list[16].trimmed());

    amValues[0]=co_ppb;
    amValues[1]=co_d_ppb;
    amValues[2]=co2_ppm;
    amValues[3]=co2_d_ppm;
    amValues[4]=ch4_ppm;
    amValues[5]=ch4_d_ppm;
    amValues[6]=h2o_pct;

    ui->lineEditCO->setText(QString::number(co_ppb));
    ui->lineEditCOD->setText(QString::number(co_d_ppb));
    ui->lineEditCC->setText(QString::number(co2_ppm));
    ui->lineEditCCD->setText(QString::number(co2_d_ppm));
    ui->lineEditCH->setText(QString::number(ch4_ppm));
    ui->lineEditCHD->setText(QString::number(ch4_d_ppm));
    ui->lineEditHO->setText(QString::number(h2o_pct));

    if(rxDatas.indexOf(0x23,434)>0)
    {
        rxDatas.remove(0,rxDatas.indexOf(0x23,434));
    }
    else
    {
        rxDatas.clear();
    }
}

  在分析仪的数据展示中,我们实际上还是用了曲线的显示,所以我们需要更新数据显示,就是在数据到来时,我们更新曲线图。具体如下:

void AnalyserForm::UpdateCurveShow()
{
    bool flagAM[8]={false,false,false,false,false,false,false,false};
    QString amName[8]={"CO","CO_d","CO2","CO2_d","CH4","CH4_d","H2O","unkown"};

    if(delayNum>0)
    {
        delayNum--;
        return;
    }

    flagAM[3]=true;
    ChartHelper::ChartDisplay(amChart1,amLineSeries1,amValues,flagAM,amName);
    ui->graphicsViewCC->setChart(amChart1);

    flagAM[3]=false;
    flagAM[5]=true;
    ChartHelper::ChartDisplay(amChart2,amLineSeries2,amValues,flagAM,amName);
    ui->graphicsViewCH->setChart(amChart2);

    flagAM[1]=true;
    flagAM[5]=false;
    ChartHelper::ChartDisplay(amChart3,amLineSeries3,amValues,flagAM,amName);
    ui->graphicsViewCO->setChart(amChart3);

    flagAM[1]=false;
    flagAM[6]=true;
    ChartHelper::ChartDisplay(amChart4,amLineSeries4,amValues,flagAM,amName);
    ui->graphicsViewHO->setChart(amChart4);
}

  同时,由于存在历史数据查询的需求,所以我们还需要向数据库中写入实时数据。
  阀箱的控制及数据获取也是一样的,厂商定义有专用的通讯协议只需要按照协议发送和解析报文就可以。不过有一个自定义逻辑过程的需求,就是定义一个逻辑序列,这个序列可以设定指定的通道及持续时间,软件根据这个逻辑序列自动执行。

  我们使用一个计时器来计算持续的时间,每当计时到则判断是否进入下一步,陆续进入下一步则启动下一步,同时间计时器的时间跨度修改为该步骤需要持续的时长。

void ValveBoxForm::CycleSendData()
{
    if(!ui->checkBoxEnable->isChecked())
    {
        if(currentChannel==0)
        {
            ReadCurrentPosition();
        }

        ui->lineEditCurrentStep->setText(QString::number(0));

        return;
    }

    for (int i=0;i<72;i++)
    {
        stepIndex=stepIndex>=72?0:stepIndex;

        if(((0<preChannel[stepIndex])&&(preChannel[stepIndex]<9))&&((0<preTime[stepIndex])&&(preTime[stepIndex]<1666)))
        {
            WriteSerialData(stepIndex);
            ui->lineEditCurrentStep->setText(QString::number(stepIndex+1));
            break;
        }

        stepIndex++;
    }

    stepIndex++;

    ReadCurrentPosition();
}

  上述逻辑中最多可以执行72步的预设过程。每次从头开始执行直至最后,然后再循环执行。

3、测试与改进

  软件的实现并不复杂,我们都只需要实现厂商定义的通讯协议就可以实现操作和数据获取。接下来我们测试一下这个软件。我们需要先配置好通讯的参数,然后点击“分析仪连接”和“阀箱连接”按钮,如果串口成功打开则会显示蓝色的“分析仪已连接”和“阀箱已连接”。
  接收到的分析仪数据解析后会显示到数据框和数据曲线。这里我们连接分析仪一段时间后其结果如下:

  界面从上到下是二氧化碳的曲线和数值、甲烷的曲线和数值、一氧化碳的曲线和数字值以及水含量的曲线和数值。可以看出软件实现了获取分析仪数据的需求,并且运行良好。
  运行一段时间后我们就可以查看历史数据,选择起始时间和结束时间,然后点击“数据查询”按钮即可检索历史数据,结果如下所示:

  数据查询的时间跨度限定在一个小时以内,如果设置的时间超过1小时则只会显示从起始时间开始的1小时的数据,余下的数据不会显示在数据表中。
  阀箱在建立连接后,软件会获取阀箱的数据,并显示在界面当中。我们在“目标通道”输入框中输入1~8的任意数字,然后点击“手动切换”按钮可实现阀门通道切换。如我们在目标通道”输入框中输入1,然后点击“手动切换”按钮将阀门切换到通道1,具体如下图显示:

  上图的表格中显示的是预置的运行逻辑,用于自动状态时操作。选中“自动循环”复选框会启动自动操作,按照表格中设定的逻辑运行。我们在这里选中“自动循环”复选框,软件次序按照表中的排序进行。

4、开发总结

  这一篇中,我们设计了一个可以实现三参数温室气体分析仪数据获取和阀箱操作及数据获取的软件。该软件涉及到串口通讯、文件操作、数据库操作、曲线显示及定时器操作等主要功能。
  我们在开发之后对软件进行的实际应用,功能上基本以及实现了用户提出的所有需求,但性能上还是可以提高的。比如对异常数据的处理,对曲线显示的处理等还是可以进一步细化的。

欢迎关注:

posted @ 2023-09-23 14:20  Moonan  阅读(43)  评论(0编辑  收藏  举报