first commit20240712

This commit is contained in:
wumin 2024-07-18 22:46:32 +08:00
commit 829412b834
22 changed files with 9915 additions and 0 deletions

665
camera.cpp Normal file
View File

@ -0,0 +1,665 @@
#include "camera.h"
#include "thread.h"
#include <vector>
// 使用std::vector存储所有帧的数据
//外部引用的标志位 save_flag 存图标志位 RGB 光谱是否需要保存图 (不能单独存一个 RGB 或 光谱 只能要存全存 除非再加标志位)
//spec_flag 没用到
extern int save_flag;
extern bool spec_flag;
tomato tomato;
//RGB相机的回调函数
void __stdcall onImageDataCallBackFunc(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser);
void __stdcall onOfflineCallBackFunc(unsigned int nMsgType, void* pUser);
void __stdcall onImageDataCallBackFunc1(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser);
void __stdcall onOfflineCallBackFunc1(unsigned int nMsgType, void* pUser);
void __stdcall onImageDataCallBackFunc2(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser);
void __stdcall onOfflineCallBackFunc2(unsigned int nMsgType, void* pUser);
//光谱相机的回调函数
extern void __stdcall RTRGBViewCallback(void* pContext, unsigned char* pRData, unsigned char* pGData, unsigned char* pBData, unsigned long dataLength);
extern void __stdcall RTSpecStreamingCallback(void *pContext, unsigned char *pData, unsigned long dataLength);
//三个相机是否连接上的标志位 在 initcamera 里面的 opencamera 函数有返回值 负责修改 ui界面的 "是否连接" 标志位
int camStatusret;
int camStatusret1;
int camStatusret2;
//果蔬标志位
extern int fruit_flag;
CameraL::CameraL(QObject *parent) : QObject(parent)
{
}
//初始化相机
bool CameraL::initCameraL()
{
/*
*
* */
int device_num = enum_device();
qDebug()<<"找到相机: "<<device_num;
if(device_num == -1)
{
qDebug()<<"enum device failed";
if(camera_handle != NULL)
{
destroy_handle();
}
return false;
}
if(device_num == 0)
{
qDebug()<<"no camera found";
if(camera_handle != NULL)
{
destroy_handle();
}
return false;
}
//print_device_info();
bool isOk = select_device();
if(!isOk)
{
qDebug()<<"create handle failed";
if(camera_handle != NULL)
{
destroy_handle();
}
if(camera_handle1 != NULL)
{
destroy_handle1();
}
if(camera_handle2 != NULL)
{
destroy_handle2();
}
return false;
}
/*
*
* */
isOk = open_camera();
if(!isOk)
{
qDebug()<<"open camera failed";
if(camera_handle != NULL)
{
destroy_handle();
}
return false;
}else{
qDebug()<<"打开相机: "<<"isOk: "<<isOk;
}
// set_ROI(0, 0, 2448, 2048);
return true;
}
//下面是所有 initcamera 函数内调用的函数的具体内容
uint32_t CameraL::enum_device()
{
int ret = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &device_list);
if(ret != MV_OK)
{
return -1;
}
return device_list.nDeviceNum;
}
//这里需要注意 因为是三个相机 且有位置顺序 所以需要根据相机的第四位 ip 来确定 所有相机都必须分配固定ip
//比如相机 ip 为 192.168.2.10 10 则为相机的第四位 ip 如果发现顺序不对使用MVS软件重新分配固定 ip
//200顶部相机 100右侧相机 150左侧相机
bool CameraL::select_device()
{
MV_CC_DEVICE_INFO* pstDeviceInfo;
for(i=0;i<=2;i++)
{
pstDeviceInfo = device_list.pDeviceInfo[i];
if (NULL == pstDeviceInfo)
{
printf("The Pointer of pstMVDevInfo is NULL!\n");
return false;
}
ip4 = pstDeviceInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff;
//根据第四位 ip 来创建 camera_handle
//camera_handle 相当于 一个指向相机的指针 这个指针就代表相机
switch (ip4)
{
case 100:
MV_CC_CreateHandle(&camera_handle1, device_list.pDeviceInfo[i]);
break;
case 150:
MV_CC_CreateHandle(&camera_handle, device_list.pDeviceInfo[i]);
break;
case 200:
MV_CC_CreateHandle(&camera_handle2, device_list.pDeviceInfo[i]);
break;
default:
break;
}
pstDeviceInfo++;
}
return true;
}
bool CameraL::open_camera()
{
int ret = MV_CC_OpenDevice(camera_handle);
int ret1 = MV_CC_OpenDevice(camera_handle1);
int ret2 = MV_CC_OpenDevice(camera_handle2);
camStatusret2=0;
camStatusret1=0;
camStatusret=0;
if(ret != MV_OK||ret1 != MV_OK||ret2 != MV_OK)
{
return false;
camStatusret2=0;
camStatusret1=0;
camStatusret=0;
}
return true;
}
bool CameraL::open_camera2()
{
/*
*
**/
bool isOk = import_config_file();
// set_ROI(0, 668, 1000, 2448);
if(!isOk)
{
qDebug()<<"import config file failed";
if(camera_handle != NULL)
{
destroy_handle();
}
if(camera_handle1 != NULL)
{
destroy_handle1();
}
if(camera_handle2 != NULL)
{
destroy_handle2();
}
return false;;
}
isOk = register_image_callback(onImageDataCallBackFunc,onImageDataCallBackFunc1,onImageDataCallBackFunc2);
qDebug()<<"注册回调成功: "<<"isOk: "<<isOk;
if(!isOk)
{
qDebug()<<"register image callback function failed";
if(camera_handle != NULL)
{
destroy_handle();
}
if(camera_handle1 != NULL)
{
destroy_handle1();
}
if(camera_handle2 != NULL)
{
destroy_handle2();
}
return false;
}
isOk = register_offline_callback(onOfflineCallBackFunc,onOfflineCallBackFunc1,onOfflineCallBackFunc2);
qDebug()<<"取消注册回调成功: "<<"isOk: "<<isOk;
if(!isOk)
{
qDebug()<<"register offline callback function failed";
if(camera_handle != NULL)
{
destroy_handle();
}
if(camera_handle1 != NULL)
{
destroy_handle1();
}
if(camera_handle2 != NULL)
{
destroy_handle2();
}
return false;
}
isOk = set_acquisition_mode();
qDebug()<<"设置触发成功: "<<"isOk: "<<isOk;
if(!isOk)
{
qDebug()<<"set acquistion mode failed";
if(camera_handle != NULL)
{
destroy_handle();
}
if(camera_handle1 != NULL)
{
destroy_handle1();
}
if(camera_handle2 != NULL)
{
destroy_handle2();
}
return false;
}
// MV_CC_SetBalanceWhiteAuto(camera_handle, 0);
// MV_CC_SetBalanceWhiteAuto(camera_handle1, 0);
// MV_CC_SetBalanceWhiteAuto(camera_handle2, 0);
// MV_CC_SetIntValue(camera_handle, "LineDebouncerTime", 100);
// MV_CC_SetIntValue(camera_handle1, "LineDebouncerTime", 100);
// MV_CC_SetIntValue(camera_handle2, "LineDebouncerTime", 100);
}
bool CameraL::start_capture()
{
int ret2 = MV_CC_StartGrabbing(camera_handle2);
int ret1 = MV_CC_StartGrabbing(camera_handle1);
int ret = MV_CC_StartGrabbing(camera_handle);
if (ret != MV_OK||ret1 != MV_OK||ret2 != MV_OK)
{
return false;
}
return true;
}
bool CameraL::register_image_callback(imageCallbackFunc onImageDataCallBackFunc,imageCallbackFunc onImageDataCallBackFunc1,imageCallbackFunc onImageDataCallBackFunc2)
{
int ret2 = MV_CC_RegisterImageCallBackForBGR(camera_handle1, onImageDataCallBackFunc1, NULL); //100 right
int ret1 = MV_CC_RegisterImageCallBackForBGR(camera_handle, onImageDataCallBackFunc2, NULL); //150 left
int ret = MV_CC_RegisterImageCallBackForBGR(camera_handle2, onImageDataCallBackFunc, NULL); //200 top
if (ret != MV_OK||ret1 != MV_OK||ret2 != MV_OK)
{
return false;
}
return true;
}
bool CameraL::register_offline_callback(exceptionCallbackFunc onOfflineCallBackFunc, exceptionCallbackFunc onOfflineCallBackFunc1, exceptionCallbackFunc onOfflineCallBackFunc2)
{
int nRet1 = MV_CC_RegisterExceptionCallBack(camera_handle, onOfflineCallBackFunc2, NULL);
int nRet = MV_CC_RegisterExceptionCallBack(camera_handle2, onOfflineCallBackFunc1, NULL);
int nRet2 = MV_CC_RegisterExceptionCallBack(camera_handle1, onOfflineCallBackFunc, NULL);
if (nRet != MV_OK||nRet1 != MV_OK||nRet2 != MV_OK)
{
return false;
}
return true;
}
bool CameraL::destroy_handle()
{
int ret = MV_CC_DestroyHandle(camera_handle);
if (MV_OK != ret)
{
return false;
}
return true;
}
bool CameraL::destroy_handle1()
{
int ret = MV_CC_DestroyHandle(camera_handle1);
if (MV_OK != ret)
{
return false;
}
return true;
}
bool CameraL::destroy_handle2()
{
int ret = MV_CC_DestroyHandle(camera_handle2);
if (MV_OK != ret)
{
return false;
}
return true;
}
//前面的两个标志位决定导入哪一个相机配置文件
bool CameraL::import_config_file()
{
if(fruit_flag) //百香果
{
qDebug()<<"import passion camera ini";
int ret = MV_CC_FeatureLoad(camera_handle, CAMERApassion_left_config);
if(ret != MV_OK)
{
return false;
}
int ret2 = MV_CC_FeatureLoad(camera_handle2, CAMERAtoppassion_top2_config);
if(ret2 != MV_OK)
{
return false;
}
int ret1 = MV_CC_FeatureLoad(camera_handle1, CAMERApassion_right_config);
if(ret1 != MV_OK)
{
return false;
}
}
else //西红柿
{
qDebug()<<"import tomato camera ini";
int ret = MV_CC_FeatureLoad(camera_handle, CAMERA_CONFIG_PATH);
if(ret != MV_OK)
{
return false;
}
int ret2 = MV_CC_FeatureLoad(camera_handle2, CAMERA2_CONFIG_PATH);
if(ret2 != MV_OK)
{
return false;
}
int ret1 = MV_CC_FeatureLoad(camera_handle1, CAMERAtop_CONFIG_PATH);
if(ret1 != MV_OK)
{
return false;
}
}
return true;
}
bool CameraL::save_config_file()
{
int ret = MV_CC_FeatureSave(camera_handle, CAMERA_CONFIG_PATH);
if(ret != MV_OK)
{
return false;
}
return true;
}
bool CameraL::set_param(Camera_param param_struct)
{
camera_param = param_struct;
int ret = MV_CC_SetIntValue(camera_handle, "BalanceRatio", camera_param.white_balance_ratio);
if (ret != MV_OK)
{
qDebug()<<"white balance ration set failed";
return false;
}
ret = MV_CC_SetFloatValue(camera_handle, "ExposureTime", camera_param.exposure_time);
if(ret != MV_OK)
{
qDebug()<<"exposure time set failed";
return false;
}
ret = MV_CC_SetFloatValue(camera_handle, "Gain", camera_param.gain);
if(ret != MV_OK)
{
qDebug()<<"gain set failed";
return false;
}
return true;
}
Camera_param CameraL::get_param()
{
MVCC_INTVALUE BalanceRatio = {0};
int ret = MV_CC_GetIntValue(camera_handle, "BalanceRatio", &BalanceRatio);
if (ret == MV_OK)
{
camera_param.white_balance_ratio = BalanceRatio.nCurValue;
}
else
{
qDebug()<<"get white balance ratio failed";
}
MVCC_FLOATVALUE ExposureTime = {0};
ret = MV_CC_GetFloatValue(camera_handle, "ExposureTime", &ExposureTime);
if(ret == MV_OK)
{
camera_param.exposure_time = ExposureTime.fCurValue;
}
else
{
qDebug()<<"get exposure time failed";
}
MVCC_FLOATVALUE Gain = {0};
ret = MV_CC_GetFloatValue(camera_handle, "Gain", &Gain);
if(ret == MV_OK)
{
camera_param.gain = Gain.fCurValue;
}
else
{
qDebug()<<"get gain failed";
}
#if 1
qDebug()<< camera_param.white_balance_ratio;
qDebug()<<camera_param.exposure_time;
qDebug()<<camera_param.gain;
#endif
return camera_param;
}
/*
* TriggerMode 0 OFF
* 1 ON
*
* TriggerSource Line0 0
* Line1 1
* Line2 2
* Line3 3
* Counter0 4
* Software 7
* FrequencyConverter 8
*TriggerActivation 0:RisingEdge
1:FallingEdge
2:LevelHigh
3:LevelLow
AcquisitionMode 0:SingleFrame
1:MultiFrame
2:Continuous
* */
bool CameraL::set_acquisition_mode()
{
int ret = MV_CC_SetEnumValue(camera_handle, "TriggerMode", 1);
int ret1 = MV_CC_SetEnumValue(camera_handle1, "TriggerMode",1);
int ret2 = MV_CC_SetEnumValue(camera_handle2, "TriggerMode", 1);
if(MV_OK != ret || MV_OK != ret1 ||MV_OK != ret2)
{
return false;
}
int ret3 = MV_CC_SetEnumValue(camera_handle, "TriggerSource",0);
int ret4 = MV_CC_SetEnumValue(camera_handle1, "TriggerSource", 0);
int ret5 = MV_CC_SetEnumValue(camera_handle2, "TriggerSource", 0);
if(MV_OK != ret3 || MV_OK != ret4 ||MV_OK != ret5)
{
return false;
}
int ret6 = MV_CC_SetEnumValue(camera_handle, "TriggerActivation", 0);
int ret7 = MV_CC_SetEnumValue(camera_handle1, "TriggerActivation", 0);
int ret8 = MV_CC_SetEnumValue(camera_handle2, "TriggerActivation", 0);
if(MV_OK != ret6 || MV_OK != ret7 ||MV_OK != ret8)
{
return false;
}
int ret9 = MV_CC_SetEnumValue(camera_handle, "AcquisitionMode", 2); //采集模式连续
int ret10 = MV_CC_SetEnumValue(camera_handle1, "AcquisitionMode", 2);
int ret11 = MV_CC_SetEnumValue(camera_handle2, "AcquisitionMode", 2);
if(MV_OK != ret9 || MV_OK != ret10 ||MV_OK != ret11)
{
return false;
}
return true;
}
bool CameraL::stop_capture()
{
int ret = MV_CC_StopGrabbing(camera_handle);
int ret1 = MV_CC_StopGrabbing(camera_handle1);
int ret2 = MV_CC_StopGrabbing(camera_handle2);
if(MV_OK!=ret){
destroy_handle();
}
if(MV_OK!=ret1){
destroy_handle1();
}
if(MV_OK!=ret2){
destroy_handle2();
}
return true;
}
//光谱相机的所有函数
SpecCamera::SpecCamera(QObject *parent)
{
}
//初始化相机
bool SpecCamera::init_SpecCamera()
{
//打开相机
int ires = RTOpenCamera();
qDebug()<<"ires: "<<ires;
if (ires != 1)
{
printf("Open Camera failed!");
return false;
}
//获取当前系统相机参数
m_uiDepth = RTGetCameraDepth(); //获取相机图像最大值256/4096/65536
RTGetImageProperty(m_iWidth, m_iHeight); //获取图像宽、高
m_pusSaveBuffer = new unsigned short[m_iHeight*((m_iWidth * 3 + 3) / 4 * 4)];
m_pRefreshBuffer = new unsigned char[m_iHeight*((m_iWidth * 3 + 3) / 4 * 4)];
RTGetCameraGain(m_fGain);
RTGetExposure(m_fExpTime);
RTGetCameraFrameRate(m_iFrameRate);
RTGetMinAndMaxWavelength(m_fWaveLengthMin, m_fWaveLengthMax);
RTGetRGBselect(m_cslRed, m_cslGreen, m_cslBlue);
RTSetExposure(m_fExpTime);
RTSetCameraGain(m_fGain);
RTSetCameraFrameRate(m_iFrameRate);
RTApplyCameraParam();
if (m_cslRed > m_iHeight || m_cslGreen > m_iHeight || m_cslBlue > m_iHeight
|| m_cslRed <= 0 || m_cslGreen <= 0 || m_cslBlue <= 0)
{
m_cslRed = m_iHeight * 3 / 4;
m_cslGreen = m_iHeight / 2;
m_cslBlue = m_iHeight / 4;
}
RTSetRGBselect(m_cslRed, m_cslGreen, m_cslBlue);
RTAddRGBCallback(RTRGBViewCallback, nullptr);
RTAddStreamingCallback(RTSpecStreamingCallback,nullptr);
return true;
}
//这个 cal 函数 是光谱相机的回调函数内触发的一个槽函数 可以在 thread 里面的光谱相机的回调找到
bool SpecCamera::cal(unsigned char *pData)
{
test_count++;
//假设筛选后的ROI 为 30 * 100 宽 * 谱段数
//按右上角为数据初始点 900像素点 每一行第 900 个像素点 第 73 个谱段
unsigned short* spec_total_data = new unsigned short[30 * 13]; //宽 * 谱段数
unsigned short* spec_short_data = (unsigned short *)pData;
int Band[13]={8,9,10,48,49,50,77,80,103,108,115,143,145};
for(int k=0;k<13;k++)
{
memcpy(spec_total_data + k * 30, spec_short_data + 900 + (Band[k] - 1) * 2048, 30 * sizeof(unsigned short));
}
if (test_count < 26)
{
SpecData_vector.push_back(spec_total_data);
}
else
{
delete[] spec_total_data; // 释放内存以防止内存泄漏
}
return true;
}
//光谱相机内置函数 不用管
bool SpecCamera::stop_capture()
{
if (RTIsCameraWorking())
{
RTStopSingleCapture();
}
return true;
}
bool SpecCamera::trigger_open()
{
bool ret = RT_SetBool(L"Camera.TriggerIn", true); //设置相机外触发
if(ret==false)
{
qDebug()<<"trigger is not open:"<<ret;
return false;
}
else
{
qDebug()<<"trigger is open:"<<ret;
}
return true;
}
bool SpecCamera::start_capture()
{
trigger_open();
bool flag = RTStartSingleCapture(false, false, "E:/QT/projects/tomato20240415/tomato04150955/image/newdata.raw");
if (!flag)
{
printf("erro to start capture");
return 1;
}
return true;
}

179
camera.h Normal file
View File

@ -0,0 +1,179 @@
#ifndef CAMERA_H
#define CAMERA_H
#include <QObject>
#include "MvCameraControl.h"
#include "SpectrolDll.h"
#include "QDebug"
#include "QImage"
#include <queue>
typedef void(*imageCallbackFunc)(unsigned char * , MV_FRAME_OUT_INFO_EX* , void* );
typedef void(*exceptionCallbackFunc)(unsigned int , void* );
//三个相机的配置文件位置 也是绝对路径
//三个相机用6个配置文件原因 百香果的相机参数是700曝光 番茄的相机参数是1500曝光 所以不同的果蔬对应不同的相机参数
#define CAMERApassion_left_config "./CameraConfig/PFleft.mfs"
#define CAMERApassion_right_config "./CameraConfig/PFright.mfs"
#define CAMERAtoppassion_top2_config "./CameraConfig/PFtop.mfs"
#define CAMERA_CONFIG_PATH "./CameraConfig/tomatoLeft.mfs"
#define CAMERA2_CONFIG_PATH "./CameraConfig/tomatoTop.mfs"
#define CAMERAtop_CONFIG_PATH "./CameraConfig/tomatoRight.mfs"
class tomato
{
public:
unsigned int count=0;
QImage img1;
QImage img2;
QImage img3;
QImage img4;
QImage img5;
};
//相机的参数
struct Camera_param //相机参数在此添加,在构造函数中给出默认值,设置参数需要刷新,左侧
{
float exposure_time;
float gain;
int white_balance_ratio;
//... other params
};
struct Camera_paramR //相机参数在此添加,在构造函数中给出默认值,设置参数需要刷新,右侧
{
float exposure_time;
float gain;
int white_balance_ratio;
//... other params
};
struct Camera_paramTop //相机参数在此添加,在构造函数中给出默认值,设置参数需要刷新,顶部
{
float exposure_time;
float gain;
int white_balance_ratio;
//... other params
};
//创建相机类 所有相机有关的函数、参数都在这里
class CameraL : public QObject
{
Q_OBJECT
private:
void* camera_handle; //设备句柄
void* camera_handle1;
void* camera_handle2;
MV_CC_DEVICE_INFO_LIST device_list; //设备列表
Camera_param camera_param;
unsigned int ip4; //用于保存ipv4地址的第4位即192.168.0.100中的100
int i;
public:
explicit CameraL(QObject *parent = nullptr);
bool initCameraL();
uint32_t enum_device();
bool print_device_info();
bool select_device();
bool destroy_handle();
bool destroy_handle1();
bool destroy_handle2();
bool open_camera(); //初始化中的打开
bool open_camera2(); //与开始采集关联
bool start_capture();
bool close_camera();
bool set_acquisition_mode();
bool set_test_acquisition_mode();
bool set_ROI(int offset_x, int offset_y, int width, int height);
bool stop_capture();
bool register_image_callback(imageCallbackFunc onImageDataCallBackFunc,imageCallbackFunc onImageDataCallBackFunc1,imageCallbackFunc onImageDataCallBackFunc2);
bool register_offline_callback(exceptionCallbackFunc onOfflineCallBackFunc,exceptionCallbackFunc onOfflineCallBackFunc1,exceptionCallbackFunc onOfflineCallBackFunc2);
bool import_config_file();
bool save_config_file();
bool set_param(Camera_param value);
Camera_param get_param();
std::queue<QImage> leftImgQueue;
std::queue<QImage> rightImgQueue;
std::queue<QImage> topImgQueue;
std::queue<QImage> topImgTQueue;
std::queue<QImage> topImgMQueue;
std::queue<QImage> topImgBQueue;
std::queue<tomato> tomatoQueue;
//信号 信号跟槽函数
signals:
// void send_RgbimageL(cv::Mat img,unsigned int count);
void send_Rgbimage(QImage& img);
void send_Rgbimage1(QImage& img);
void send_Rgbimage2(QImage& img,QImage& img1,QImage& img2);
void send_camerastatus(int ret,int ret1,int ret2);
public slots:
};
//光谱相机类
class SpecCamera : public QObject
{
Q_OBJECT
public:
explicit SpecCamera(QObject *parent = nullptr);
bool init_SpecCamera();
bool cal(unsigned char* pData);
bool stop_capture();
bool trigger_open();
int m_iWidth; //相机采集数据宽度
int m_iHeight; //相机采集数据高度
unsigned short* specfullImage;
const int totalFrames = 30; // 假设需要拼接30帧来形成一张图
// std::vector<unsigned short*> frameData;
int vector_flag;
// int nsize = 35*25*13;
std::vector<unsigned short*> SpecData_vector;//给容器开辟空间
int test_count = 0;
//CameraSetting
float m_fWaveLengthMin; //起点光谱波长
float m_fWaveLengthMax; //终点光谱波长
float m_fExpTime; //曝光时间
float m_fGain; //增益
float m_iFrameRate; //帧速
//GenralSetting
int m_cslRed; //红色波长序号
int m_cslGreen; //绿色波长序号
int m_cslBlue; //蓝色波长序号
unsigned int m_uiDepth;
unsigned short* m_pusSaveBuffer;
unsigned char* m_pRefreshBuffer; //RGB
unsigned char* m_specBuffer; //光谱
long m_iDrawCount = 0;
bool start_capture();
std::queue<unsigned short*> specImgQueue;
private:
signals:
void show_SpecImg(QImage);
};
#endif // CAMERA_H

BIN
debug/tomato1227.exe Normal file

Binary file not shown.

16
main.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
try {
Widget w;
w.show();
return a.exec();
} catch (const ExpiredException& e) {
QMessageBox::critical(nullptr, "程序过期", e.what());
return -1;
}
}

233
src/modbus-data.c Normal file
View File

@ -0,0 +1,233 @@
/*
* Copyright © 2010-2014 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include <stdlib.h>
#ifndef _MSC_VER
# include <stdint.h>
#else
# include "stdint.h"
#endif
#include <string.h>
#include <assert.h>
#if defined(_WIN32)
# include <winsock2.h>
#else
# include <arpa/inet.h>
#endif
//#include <config.h>
#include "modbus.h"
#if defined(HAVE_BYTESWAP_H)
# include <byteswap.h>
#endif
#if defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# define bswap_16 OSSwapInt16
# define bswap_32 OSSwapInt32
# define bswap_64 OSSwapInt64
#endif
#if defined(__GNUC__)
# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10)
# if GCC_VERSION >= 430
// Since GCC >= 4.30, GCC provides __builtin_bswapXX() alternatives so we switch to them
# undef bswap_32
# define bswap_32 __builtin_bswap32
# endif
# if GCC_VERSION >= 480
# undef bswap_16
# define bswap_16 __builtin_bswap16
# endif
#endif
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
# define bswap_32 _byteswap_ulong
# define bswap_16 _byteswap_ushort
#endif
#if !defined(bswap_16)
# warning "Fallback on C functions for bswap_16"
static inline uint16_t bswap_16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
#endif
#if !defined(bswap_32)
# warning "Fallback on C functions for bswap_32"
static inline uint32_t bswap_32(uint32_t x)
{
return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16));
}
#endif
/* Sets many bits from a single byte value (all 8 bits of the byte value are
set) */
void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value)
{
int i;
for (i=0; i < 8; i++) {
dest[idx+i] = (value & (1 << i)) ? 1 : 0;
}
}
/* Sets many bits from a table of bytes (only the bits between idx and
idx + nb_bits are set) */
void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits,
const uint8_t *tab_byte)
{
unsigned int i;
int shift = 0;
for (i = idx; i < idx + nb_bits; i++) {
dest[i] = tab_byte[(i - idx) / 8] & (1 << shift) ? 1 : 0;
/* gcc doesn't like: shift = (++shift) % 8; */
shift++;
shift %= 8;
}
}
/* Gets the byte value from many bits.
To obtain a full byte, set nb_bits to 8. */
uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx,
unsigned int nb_bits)
{
unsigned int i;
uint8_t value = 0;
if (nb_bits > 8) {
/* Assert is ignored if NDEBUG is set */
assert(nb_bits < 8);
nb_bits = 8;
}
for (i=0; i < nb_bits; i++) {
value |= (src[idx+i] << i);
}
return value;
}
/* Get a float from 4 bytes (Modbus) without any conversion (ABCD) */
float modbus_get_float_abcd(const uint16_t *src)
{
float f;
uint32_t i;
i = ntohl(((uint32_t)src[0] << 16) + src[1]);
memcpy(&f, &i, sizeof(float));
return f;
}
/* Get a float from 4 bytes (Modbus) in inversed format (DCBA) */
float modbus_get_float_dcba(const uint16_t *src)
{
float f;
uint32_t i;
i = ntohl(bswap_32((((uint32_t)src[0]) << 16) + src[1]));
memcpy(&f, &i, sizeof(float));
return f;
}
/* Get a float from 4 bytes (Modbus) with swapped bytes (BADC) */
float modbus_get_float_badc(const uint16_t *src)
{
float f;
uint32_t i;
i = ntohl((uint32_t)(bswap_16(src[0]) << 16) + bswap_16(src[1]));
memcpy(&f, &i, sizeof(float));
return f;
}
/* Get a float from 4 bytes (Modbus) with swapped words (CDAB) */
float modbus_get_float_cdab(const uint16_t *src)
{
float f;
uint32_t i;
i = ntohl((((uint32_t)src[1]) << 16) + src[0]);
memcpy(&f, &i, sizeof(float));
return f;
}
/* DEPRECATED - Get a float from 4 bytes in sort of Modbus format */
float modbus_get_float(const uint16_t *src)
{
float f;
uint32_t i;
i = (((uint32_t)src[1]) << 16) + src[0];
memcpy(&f, &i, sizeof(float));
return f;
}
/* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */
void modbus_set_float_abcd(float f, uint16_t *dest)
{
uint32_t i;
memcpy(&i, &f, sizeof(uint32_t));
i = htonl(i);
dest[0] = (uint16_t)(i >> 16);
dest[1] = (uint16_t)i;
}
/* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
void modbus_set_float_dcba(float f, uint16_t *dest)
{
uint32_t i;
memcpy(&i, &f, sizeof(uint32_t));
i = bswap_32(htonl(i));
dest[0] = (uint16_t)(i >> 16);
dest[1] = (uint16_t)i;
}
/* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
void modbus_set_float_badc(float f, uint16_t *dest)
{
uint32_t i;
memcpy(&i, &f, sizeof(uint32_t));
i = htonl(i);
dest[0] = (uint16_t)bswap_16(i >> 16);
dest[1] = (uint16_t)bswap_16(i & 0xFFFF);
}
/* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
void modbus_set_float_cdab(float f, uint16_t *dest)
{
uint32_t i;
memcpy(&i, &f, sizeof(uint32_t));
i = htonl(i);
dest[0] = (uint16_t)i;
dest[1] = (uint16_t)(i >> 16);
}
/* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */
void modbus_set_float(float f, uint16_t *dest)
{
uint32_t i;
memcpy(&i, &f, sizeof(uint32_t));
dest[0] = (uint16_t)i;
dest[1] = (uint16_t)(i >> 16);
}

116
src/modbus-private.h Normal file
View File

@ -0,0 +1,116 @@
/*
* Copyright © 2010-2012 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef MODBUS_PRIVATE_H
#define MODBUS_PRIVATE_H
#ifndef _MSC_VER
# include <stdint.h>
# include <sys/time.h>
#else
# include "stdint.h"
# include <time.h>
typedef int ssize_t;
#endif
#include <sys/types.h>
//#include <config.h>
#include "modbus.h"
MODBUS_BEGIN_DECLS
/* It's not really the minimal length (the real one is report slave ID
* in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
* communications to read many values or write a single one.
* Maximum between :
* - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
* - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
*/
#define _MIN_REQ_LENGTH 12
#define _REPORT_SLAVE_ID 180
#define _MODBUS_EXCEPTION_RSP_LENGTH 5
/* Timeouts in microsecond (0.5 s) */
#define _RESPONSE_TIMEOUT 500000
#define _BYTE_TIMEOUT 500000
typedef enum {
_MODBUS_BACKEND_TYPE_RTU=0,
_MODBUS_BACKEND_TYPE_TCP
} modbus_backend_type_t;
/*
* ---------- Request Indication ----------
* | Client | ---------------------->| Server |
* ---------- Confirmation Response ----------
*/
typedef enum {
/* Request message on the server side */
MSG_INDICATION,
/* Request message on the client side */
MSG_CONFIRMATION
} msg_type_t;
/* This structure reduces the number of params in functions and so
* optimizes the speed of execution (~ 37%). */
typedef struct _sft {
int slave;
int function;
int t_id;
} sft_t;
typedef struct _modbus_backend {
unsigned int backend_type;
unsigned int header_length;
unsigned int checksum_length;
unsigned int max_adu_length;
int (*set_slave) (modbus_t *ctx, int slave);
int (*build_request_basis) (modbus_t *ctx, int function, int addr,
int nb, uint8_t *req);
int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
int (*prepare_response_tid) (const uint8_t *req, int *req_length);
int (*send_msg_pre) (uint8_t *req, int req_length);
ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
int (*receive) (modbus_t *ctx, uint8_t *req);
ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
const int msg_length);
int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
const uint8_t *rsp, int rsp_length);
int (*connect) (modbus_t *ctx);
void (*close) (modbus_t *ctx);
int (*flush) (modbus_t *ctx);
int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
void (*free) (modbus_t *ctx);
} modbus_backend_t;
struct _modbus {
/* Slave address */
int slave;
/* Socket or file descriptor */
int s;
int debug;
int error_recovery;
struct timeval response_timeout;
struct timeval byte_timeout;
struct timeval indication_timeout;
const modbus_backend_t *backend;
void *backend_data;
};
void _modbus_init_common(modbus_t *ctx);
void _error_print(modbus_t *ctx, const char *context);
int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
#ifndef HAVE_STRLCPY
size_t strlcpy(char *dest, const char *src, size_t dest_size);
#endif
MODBUS_END_DECLS
#endif /* MODBUS_PRIVATE_H */

76
src/modbus-rtu-private.h Normal file
View File

@ -0,0 +1,76 @@
/*
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef MODBUS_RTU_PRIVATE_H
#define MODBUS_RTU_PRIVATE_H
#ifndef _MSC_VER
#include <stdint.h>
#else
#include "stdint.h"
#endif
#if defined(_WIN32)
#include <windows.h>
#else
#include <termios.h>
#endif
#define _MODBUS_RTU_HEADER_LENGTH 1
#define _MODBUS_RTU_PRESET_REQ_LENGTH 6
#define _MODBUS_RTU_PRESET_RSP_LENGTH 2
#define _MODBUS_RTU_CHECKSUM_LENGTH 2
#if defined(_WIN32)
#if !defined(ENOTSUP)
#define ENOTSUP WSAEOPNOTSUPP
#endif
/* WIN32: struct containing serial handle and a receive buffer */
#define PY_BUF_SIZE 512
struct win32_ser {
/* File handle */
HANDLE fd;
/* Receive buffer */
uint8_t buf[PY_BUF_SIZE];
/* Received chars */
DWORD n_bytes;
};
#endif /* _WIN32 */
typedef struct _modbus_rtu {
/* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */
char *device;
/* Bauds: 9600, 19200, 57600, 115200, etc */
int baud;
/* Data bit */
uint8_t data_bit;
/* Stop bit */
uint8_t stop_bit;
/* Parity: 'N', 'O', 'E' */
char parity;
#if defined(_WIN32)
struct win32_ser w_ser;
DCB old_dcb;
#else
/* Save old termios settings */
struct termios old_tios;
#endif
#if HAVE_DECL_TIOCSRS485
int serial_mode;
#endif
#if HAVE_DECL_TIOCM_RTS
int rts;
int rts_delay;
int onebyte_time;
void (*set_rts) (modbus_t *ctx, int on);
#endif
/* To handle many slaves on the same link */
int confirmation_to_ignore;
} modbus_rtu_t;
#endif /* MODBUS_RTU_PRIVATE_H */

1299
src/modbus-rtu.c Normal file

File diff suppressed because it is too large Load Diff

42
src/modbus-rtu.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef MODBUS_RTU_H
#define MODBUS_RTU_H
#include "modbus.h"
MODBUS_BEGIN_DECLS
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
* RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes
*/
#define MODBUS_RTU_MAX_ADU_LENGTH 256
MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity,
int data_bit, int stop_bit);
#define MODBUS_RTU_RS232 0
#define MODBUS_RTU_RS485 1
MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
#define MODBUS_RTU_RTS_NONE 0
#define MODBUS_RTU_RTS_UP 1
#define MODBUS_RTU_RTS_DOWN 2
MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);
MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on));
MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);
MODBUS_END_DECLS
#endif /* MODBUS_RTU_H */

44
src/modbus-tcp-private.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef MODBUS_TCP_PRIVATE_H
#define MODBUS_TCP_PRIVATE_H
#define _MODBUS_TCP_HEADER_LENGTH 7
#define _MODBUS_TCP_PRESET_REQ_LENGTH 12
#define _MODBUS_TCP_PRESET_RSP_LENGTH 8
#define _MODBUS_TCP_CHECKSUM_LENGTH 0
/* In both structures, the transaction ID must be placed on first position
to have a quick access not dependant of the TCP backend */
typedef struct _modbus_tcp {
/* Extract from MODBUS Messaging on TCP/IP Implementation Guide V1.0b
(page 23/46):
The transaction identifier is used to associate the future response
with the request. This identifier is unique on each TCP connection. */
uint16_t t_id;
/* TCP port */
int port;
/* IP address */
char ip[16];
} modbus_tcp_t;
#define _MODBUS_TCP_PI_NODE_LENGTH 1025
#define _MODBUS_TCP_PI_SERVICE_LENGTH 32
typedef struct _modbus_tcp_pi {
/* Transaction ID */
uint16_t t_id;
/* TCP port */
int port;
/* Node */
char node[_MODBUS_TCP_PI_NODE_LENGTH];
/* Service */
char service[_MODBUS_TCP_PI_SERVICE_LENGTH];
} modbus_tcp_pi_t;
#endif /* MODBUS_TCP_PRIVATE_H */

929
src/modbus-tcp.c Normal file
View File

@ -0,0 +1,929 @@
/*
* Copyright © 2001-2013 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#if defined(_WIN32)
# define OS_WIN32
/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
* minwg32 headers check WINVER before allowing the use of these */
# ifndef WINVER
# define WINVER 0x0501
# endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <signal.h>
#include <sys/types.h>
#if defined(_WIN32)
/* Already set in modbus-tcp.h but it seems order matters in VS2005 */
# include <winsock2.h>
# include <ws2tcpip.h>
# define SHUT_RDWR 2
# define close closesocket
#else
# include <sys/socket.h>
# include <sys/ioctl.h>
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
# define OS_BSD
# include <netinet/in_systm.h>
#endif
# include <netinet/in.h>
# include <netinet/ip.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
#if defined(_AIX) && !defined(MSG_DONTWAIT)
#define MSG_DONTWAIT MSG_NONBLOCK
#endif
#include "modbus-private.h"
#include "modbus-tcp.h"
#include "modbus-tcp-private.h"
#ifdef OS_WIN32
static int _modbus_tcp_init_win32(void)
{
/* Initialise Windows Socket API */
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
fprintf(stderr, "WSAStartup() returned error code %d\n",
(unsigned int)GetLastError());
errno = EIO;
return -1;
}
return 0;
}
#endif
static int _modbus_set_slave(modbus_t *ctx, int slave)
{
/* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
if (slave >= 0 && slave <= 247) {
ctx->slave = slave;
} else if (slave == MODBUS_TCP_SLAVE) {
/* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
* restore the default value. */
ctx->slave = slave;
} else {
errno = EINVAL;
return -1;
}
return 0;
}
/* Builds a TCP request header */
static int _modbus_tcp_build_request_basis(modbus_t *ctx, int function,
int addr, int nb,
uint8_t *req)
{
modbus_tcp_t *ctx_tcp = ctx->backend_data;
/* Increase transaction ID */
if (ctx_tcp->t_id < UINT16_MAX)
ctx_tcp->t_id++;
else
ctx_tcp->t_id = 0;
req[0] = ctx_tcp->t_id >> 8;
req[1] = ctx_tcp->t_id & 0x00ff;
/* Protocol Modbus */
req[2] = 0;
req[3] = 0;
/* Length will be defined later by set_req_length_tcp at offsets 4
and 5 */
req[6] = ctx->slave;
req[7] = function;
req[8] = addr >> 8;
req[9] = addr & 0x00ff;
req[10] = nb >> 8;
req[11] = nb & 0x00ff;
return _MODBUS_TCP_PRESET_REQ_LENGTH;
}
/* Builds a TCP response header */
static int _modbus_tcp_build_response_basis(sft_t *sft, uint8_t *rsp)
{
/* Extract from MODBUS Messaging on TCP/IP Implementation
Guide V1.0b (page 23/46):
The transaction identifier is used to associate the future
response with the request. */
rsp[0] = sft->t_id >> 8;
rsp[1] = sft->t_id & 0x00ff;
/* Protocol Modbus */
rsp[2] = 0;
rsp[3] = 0;
/* Length will be set later by send_msg (4 and 5) */
/* The slave ID is copied from the indication */
rsp[6] = sft->slave;
rsp[7] = sft->function;
return _MODBUS_TCP_PRESET_RSP_LENGTH;
}
static int _modbus_tcp_prepare_response_tid(const uint8_t *req, int *req_length)
{
return (req[0] << 8) + req[1];
}
static int _modbus_tcp_send_msg_pre(uint8_t *req, int req_length)
{
/* Substract the header length to the message length */
int mbap_length = req_length - 6;
req[4] = mbap_length >> 8;
req[5] = mbap_length & 0x00FF;
return req_length;
}
static ssize_t _modbus_tcp_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
/* MSG_NOSIGNAL
Requests not to send SIGPIPE on errors on stream oriented
sockets when the other end breaks the connection. The EPIPE
error is still returned. */
return send(ctx->s, (const char *)req, req_length, MSG_NOSIGNAL);
}
static int _modbus_tcp_receive(modbus_t *ctx, uint8_t *req) {
return _modbus_receive_msg(ctx, req, MSG_INDICATION);
}
static ssize_t _modbus_tcp_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length) {
return recv(ctx->s, (char *)rsp, rsp_length, 0);
}
static int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_length)
{
return msg_length;
}
static int _modbus_tcp_pre_check_confirmation(modbus_t *ctx, const uint8_t *req,
const uint8_t *rsp, int rsp_length)
{
/* Check transaction ID */
if (req[0] != rsp[0] || req[1] != rsp[1]) {
if (ctx->debug) {
fprintf(stderr, "Invalid transaction ID received 0x%X (not 0x%X)\n",
(rsp[0] << 8) + rsp[1], (req[0] << 8) + req[1]);
}
errno = EMBBADDATA;
return -1;
}
/* Check protocol ID */
if (rsp[2] != 0x0 && rsp[3] != 0x0) {
if (ctx->debug) {
fprintf(stderr, "Invalid protocol ID received 0x%X (not 0x0)\n",
(rsp[2] << 8) + rsp[3]);
}
errno = EMBBADDATA;
return -1;
}
return 0;
}
static int _modbus_tcp_set_ipv4_options(int s)
{
int rc;
int option;
/* Set the TCP no delay flag */
/* SOL_TCP = IPPROTO_TCP */
option = 1;
rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
(const void *)&option, sizeof(int));
if (rc == -1) {
return -1;
}
/* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
* make sockets non-blocking */
/* Do not care about the return value, this is optional */
#if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
#ifdef OS_WIN32
{
/* Setting FIONBIO expects an unsigned long according to MSDN */
u_long loption = 1;
ioctlsocket(s, FIONBIO, &loption);
}
#else
option = 1;
ioctl(s, FIONBIO, &option);
#endif
#endif
#ifndef OS_WIN32
/**
* Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
* necessary to workaround that problem.
**/
/* Set the IP low delay option */
option = IPTOS_LOWDELAY;
rc = setsockopt(s, IPPROTO_IP, IP_TOS,
(const void *)&option, sizeof(int));
if (rc == -1) {
return -1;
}
#endif
return 0;
}
static int _connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen,
const struct timeval *ro_tv)
{
int rc = connect(sockfd, addr, addrlen);
#ifdef OS_WIN32
int wsaError = 0;
if (rc == -1) {
wsaError = WSAGetLastError();
}
if (wsaError == WSAEWOULDBLOCK || wsaError == WSAEINPROGRESS) {
#else
if (rc == -1 && errno == EINPROGRESS) {
#endif
fd_set wset;
int optval;
socklen_t optlen = sizeof(optval);
struct timeval tv = *ro_tv;
/* Wait to be available in writing */
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
rc = select(sockfd + 1, NULL, &wset, NULL, &tv);
if (rc <= 0) {
/* Timeout or fail */
return -1;
}
/* The connection is established if SO_ERROR and optval are set to 0 */
rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen);
if (rc == 0 && optval == 0) {
return 0;
} else {
errno = ECONNREFUSED;
return -1;
}
}
return rc;
}
/* Establishes a modbus TCP connection with a Modbus server. */
static int _modbus_tcp_connect(modbus_t *ctx)
{
int rc;
/* Specialized version of sockaddr for Internet socket address (same size) */
struct sockaddr_in addr;
modbus_tcp_t *ctx_tcp = ctx->backend_data;
int flags = SOCK_STREAM;
#ifdef OS_WIN32
if (_modbus_tcp_init_win32() == -1) {
return -1;
}
#endif
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
#endif
#ifdef SOCK_NONBLOCK
flags |= SOCK_NONBLOCK;
#endif
ctx->s = socket(PF_INET, flags, 0);
if (ctx->s == -1) {
return -1;
}
rc = _modbus_tcp_set_ipv4_options(ctx->s);
if (rc == -1) {
close(ctx->s);
ctx->s = -1;
return -1;
}
if (ctx->debug) {
printf("Connecting to %s:%d\n", ctx_tcp->ip, ctx_tcp->port);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(ctx_tcp->port);
addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
rc = _connect(ctx->s, (struct sockaddr *)&addr, sizeof(addr), &ctx->response_timeout);
if (rc == -1) {
close(ctx->s);
ctx->s = -1;
return -1;
}
return 0;
}
/* Establishes a modbus TCP PI connection with a Modbus server. */
static int _modbus_tcp_pi_connect(modbus_t *ctx)
{
int rc;
struct addrinfo *ai_list;
struct addrinfo *ai_ptr;
struct addrinfo ai_hints;
modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data;
#ifdef OS_WIN32
if (_modbus_tcp_init_win32() == -1) {
return -1;
}
#endif
memset(&ai_hints, 0, sizeof(ai_hints));
#ifdef AI_ADDRCONFIG
ai_hints.ai_flags |= AI_ADDRCONFIG;
#endif
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_addr = NULL;
ai_hints.ai_canonname = NULL;
ai_hints.ai_next = NULL;
ai_list = NULL;
rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service,
&ai_hints, &ai_list);
if (rc != 0) {
if (ctx->debug) {
fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc));
}
errno = ECONNREFUSED;
return -1;
}
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
int flags = ai_ptr->ai_socktype;
int s;
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
#endif
#ifdef SOCK_NONBLOCK
flags |= SOCK_NONBLOCK;
#endif
s = socket(ai_ptr->ai_family, flags, ai_ptr->ai_protocol);
if (s < 0)
continue;
if (ai_ptr->ai_family == AF_INET)
_modbus_tcp_set_ipv4_options(s);
if (ctx->debug) {
printf("Connecting to [%s]:%s\n", ctx_tcp_pi->node, ctx_tcp_pi->service);
}
rc = _connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen, &ctx->response_timeout);
if (rc == -1) {
close(s);
continue;
}
ctx->s = s;
break;
}
freeaddrinfo(ai_list);
if (ctx->s < 0) {
return -1;
}
return 0;
}
/* Closes the network connection and socket in TCP mode */
static void _modbus_tcp_close(modbus_t *ctx)
{
if (ctx->s != -1) {
shutdown(ctx->s, SHUT_RDWR);
close(ctx->s);
ctx->s = -1;
}
}
static int _modbus_tcp_flush(modbus_t *ctx)
{
int rc;
int rc_sum = 0;
do {
/* Extract the garbage from the socket */
char devnull[MODBUS_TCP_MAX_ADU_LENGTH];
#ifndef OS_WIN32
rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT);
#else
/* On Win32, it's a bit more complicated to not wait */
fd_set rset;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rset);
FD_SET(ctx->s, &rset);
rc = select(ctx->s+1, &rset, NULL, NULL, &tv);
if (rc == -1) {
return -1;
}
if (rc == 1) {
/* There is data to flush */
rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, 0);
}
#endif
if (rc > 0) {
rc_sum += rc;
}
} while (rc == MODBUS_TCP_MAX_ADU_LENGTH);
return rc_sum;
}
/* Listens for any request from one or many modbus masters in TCP */
int modbus_tcp_listen(modbus_t *ctx, int nb_connection)
{
int new_s;
int enable;
int flags;
struct sockaddr_in addr;
modbus_tcp_t *ctx_tcp;
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
ctx_tcp = ctx->backend_data;
#ifdef OS_WIN32
if (_modbus_tcp_init_win32() == -1) {
return -1;
}
#endif
flags = SOCK_STREAM;
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
#endif
new_s = socket(PF_INET, flags, IPPROTO_TCP);
if (new_s == -1) {
return -1;
}
enable = 1;
if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR,
(char *)&enable, sizeof(enable)) == -1) {
close(new_s);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
/* If the modbus port is < to 1024, we need the setuid root. */
addr.sin_port = htons(ctx_tcp->port);
if (ctx_tcp->ip[0] == '0') {
/* Listen any addresses */
addr.sin_addr.s_addr = htonl(INADDR_ANY);
} else {
/* Listen only specified IP address */
addr.sin_addr.s_addr = inet_addr(ctx_tcp->ip);
}
if (bind(new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
close(new_s);
return -1;
}
if (listen(new_s, nb_connection) == -1) {
close(new_s);
return -1;
}
return new_s;
}
int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection)
{
int rc;
struct addrinfo *ai_list;
struct addrinfo *ai_ptr;
struct addrinfo ai_hints;
const char *node;
const char *service;
int new_s;
modbus_tcp_pi_t *ctx_tcp_pi;
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
ctx_tcp_pi = ctx->backend_data;
#ifdef OS_WIN32
if (_modbus_tcp_init_win32() == -1) {
return -1;
}
#endif
if (ctx_tcp_pi->node[0] == 0) {
node = NULL; /* == any */
} else {
node = ctx_tcp_pi->node;
}
if (ctx_tcp_pi->service[0] == 0) {
service = "502";
} else {
service = ctx_tcp_pi->service;
}
memset(&ai_hints, 0, sizeof (ai_hints));
/* If node is not NULL, than the AI_PASSIVE flag is ignored. */
ai_hints.ai_flags |= AI_PASSIVE;
#ifdef AI_ADDRCONFIG
ai_hints.ai_flags |= AI_ADDRCONFIG;
#endif
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_addr = NULL;
ai_hints.ai_canonname = NULL;
ai_hints.ai_next = NULL;
ai_list = NULL;
rc = getaddrinfo(node, service, &ai_hints, &ai_list);
if (rc != 0) {
if (ctx->debug) {
fprintf(stderr, "Error returned by getaddrinfo: %s\n", gai_strerror(rc));
}
errno = ECONNREFUSED;
return -1;
}
new_s = -1;
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
int flags = ai_ptr->ai_socktype;
int s;
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
#endif
s = socket(ai_ptr->ai_family, flags, ai_ptr->ai_protocol);
if (s < 0) {
if (ctx->debug) {
perror("socket");
}
continue;
} else {
int enable = 1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(void *)&enable, sizeof (enable));
if (rc != 0) {
close(s);
if (ctx->debug) {
perror("setsockopt");
}
continue;
}
}
rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
if (rc != 0) {
close(s);
if (ctx->debug) {
perror("bind");
}
continue;
}
rc = listen(s, nb_connection);
if (rc != 0) {
close(s);
if (ctx->debug) {
perror("listen");
}
continue;
}
new_s = s;
break;
}
freeaddrinfo(ai_list);
if (new_s < 0) {
return -1;
}
return new_s;
}
int modbus_tcp_accept(modbus_t *ctx, int *s)
{
struct sockaddr_in addr;
socklen_t addrlen;
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
addrlen = sizeof(addr);
#ifdef HAVE_ACCEPT4
/* Inherit socket flags and use accept4 call */
ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC);
#else
ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen);
#endif
if (ctx->s == -1) {
return -1;
}
if (ctx->debug) {
printf("The client connection from %s is accepted\n",
inet_ntoa(addr.sin_addr));
}
return ctx->s;
}
int modbus_tcp_pi_accept(modbus_t *ctx, int *s)
{
struct sockaddr_storage addr;
socklen_t addrlen;
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
addrlen = sizeof(addr);
#ifdef HAVE_ACCEPT4
/* Inherit socket flags and use accept4 call */
ctx->s = accept4(*s, (struct sockaddr *)&addr, &addrlen, SOCK_CLOEXEC);
#else
ctx->s = accept(*s, (struct sockaddr *)&addr, &addrlen);
#endif
if (ctx->s == -1) {
return -1;
}
if (ctx->debug) {
printf("The client connection is accepted.\n");
}
return ctx->s;
}
static int _modbus_tcp_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read)
{
int s_rc;
while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) {
if (errno == EINTR) {
if (ctx->debug) {
fprintf(stderr, "A non blocked signal was caught\n");
}
/* Necessary after an error */
FD_ZERO(rset);
FD_SET(ctx->s, rset);
} else {
return -1;
}
}
if (s_rc == 0) {
errno = ETIMEDOUT;
return -1;
}
return s_rc;
}
static void _modbus_tcp_free(modbus_t *ctx) {
free(ctx->backend_data);
free(ctx);
}
const modbus_backend_t _modbus_tcp_backend = {
_MODBUS_BACKEND_TYPE_TCP,
_MODBUS_TCP_HEADER_LENGTH,
_MODBUS_TCP_CHECKSUM_LENGTH,
MODBUS_TCP_MAX_ADU_LENGTH,
_modbus_set_slave,
_modbus_tcp_build_request_basis,
_modbus_tcp_build_response_basis,
_modbus_tcp_prepare_response_tid,
_modbus_tcp_send_msg_pre,
_modbus_tcp_send,
_modbus_tcp_receive,
_modbus_tcp_recv,
_modbus_tcp_check_integrity,
_modbus_tcp_pre_check_confirmation,
_modbus_tcp_connect,
_modbus_tcp_close,
_modbus_tcp_flush,
_modbus_tcp_select,
_modbus_tcp_free
};
const modbus_backend_t _modbus_tcp_pi_backend = {
_MODBUS_BACKEND_TYPE_TCP,
_MODBUS_TCP_HEADER_LENGTH,
_MODBUS_TCP_CHECKSUM_LENGTH,
MODBUS_TCP_MAX_ADU_LENGTH,
_modbus_set_slave,
_modbus_tcp_build_request_basis,
_modbus_tcp_build_response_basis,
_modbus_tcp_prepare_response_tid,
_modbus_tcp_send_msg_pre,
_modbus_tcp_send,
_modbus_tcp_receive,
_modbus_tcp_recv,
_modbus_tcp_check_integrity,
_modbus_tcp_pre_check_confirmation,
_modbus_tcp_pi_connect,
_modbus_tcp_close,
_modbus_tcp_flush,
_modbus_tcp_select,
_modbus_tcp_free
};
modbus_t* modbus_new_tcp(const char *ip, int port)
{
modbus_t *ctx;
modbus_tcp_t *ctx_tcp;
size_t dest_size;
size_t ret_size;
#if defined(OS_BSD)
/* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
handler for SIGPIPE. */
struct sigaction sa;
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0) {
/* The debug flag can't be set here... */
fprintf(stderr, "Could not install SIGPIPE handler.\n");
return NULL;
}
#endif
ctx = (modbus_t *)malloc(sizeof(modbus_t));
if (ctx == NULL) {
return NULL;
}
_modbus_init_common(ctx);
/* Could be changed after to reach a remote serial Modbus device */
ctx->slave = MODBUS_TCP_SLAVE;
ctx->backend = &_modbus_tcp_backend;
ctx->backend_data = (modbus_tcp_t *)malloc(sizeof(modbus_tcp_t));
if (ctx->backend_data == NULL) {
modbus_free(ctx);
errno = ENOMEM;
return NULL;
}
ctx_tcp = (modbus_tcp_t *)ctx->backend_data;
if (ip != NULL) {
dest_size = sizeof(char) * 16;
ret_size = strlcpy(ctx_tcp->ip, ip, dest_size);
if (ret_size == 0) {
fprintf(stderr, "The IP string is empty\n");
modbus_free(ctx);
errno = EINVAL;
return NULL;
}
if (ret_size >= dest_size) {
fprintf(stderr, "The IP string has been truncated\n");
modbus_free(ctx);
errno = EINVAL;
return NULL;
}
} else {
ctx_tcp->ip[0] = '0';
}
ctx_tcp->port = port;
ctx_tcp->t_id = 0;
return ctx;
}
modbus_t* modbus_new_tcp_pi(const char *node, const char *service)
{
modbus_t *ctx;
modbus_tcp_pi_t *ctx_tcp_pi;
size_t dest_size;
size_t ret_size;
ctx = (modbus_t *)malloc(sizeof(modbus_t));
if (ctx == NULL) {
return NULL;
}
_modbus_init_common(ctx);
/* Could be changed after to reach a remote serial Modbus device */
ctx->slave = MODBUS_TCP_SLAVE;
ctx->backend = &_modbus_tcp_pi_backend;
ctx->backend_data = (modbus_tcp_pi_t *)malloc(sizeof(modbus_tcp_pi_t));
if (ctx->backend_data == NULL) {
modbus_free(ctx);
errno = ENOMEM;
return NULL;
}
ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data;
if (node == NULL) {
/* The node argument can be empty to indicate any hosts */
ctx_tcp_pi->node[0] = 0;
} else {
dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH;
ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size);
if (ret_size == 0) {
fprintf(stderr, "The node string is empty\n");
modbus_free(ctx);
errno = EINVAL;
return NULL;
}
if (ret_size >= dest_size) {
fprintf(stderr, "The node string has been truncated\n");
modbus_free(ctx);
errno = EINVAL;
return NULL;
}
}
if (service != NULL) {
dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH;
ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size);
} else {
/* Empty service is not allowed, error catched below. */
ret_size = 0;
}
if (ret_size == 0) {
fprintf(stderr, "The service string is empty\n");
modbus_free(ctx);
errno = EINVAL;
return NULL;
}
if (ret_size >= dest_size) {
fprintf(stderr, "The service string has been truncated\n");
modbus_free(ctx);
errno = EINVAL;
return NULL;
}
ctx_tcp_pi->t_id = 0;
return ctx;
}

52
src/modbus-tcp.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef MODBUS_TCP_H
#define MODBUS_TCP_H
#include "modbus.h"
MODBUS_BEGIN_DECLS
#if defined(_WIN32) && !defined(__CYGWIN__)
/* Win32 with MinGW, supplement to <errno.h> */
#include <winsock2.h>
#if !defined(ECONNRESET)
#define ECONNRESET WSAECONNRESET
#endif
#if !defined(ECONNREFUSED)
#define ECONNREFUSED WSAECONNREFUSED
#endif
#if !defined(ETIMEDOUT)
#define ETIMEDOUT WSAETIMEDOUT
#endif
#if !defined(ENOPROTOOPT)
#define ENOPROTOOPT WSAENOPROTOOPT
#endif
#if !defined(EINPROGRESS)
#define EINPROGRESS WSAEINPROGRESS
#endif
#endif
#define MODBUS_TCP_DEFAULT_PORT 502
#define MODBUS_TCP_SLAVE 0xFF
/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5
* TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes
*/
#define MODBUS_TCP_MAX_ADU_LENGTH 260
MODBUS_API modbus_t* modbus_new_tcp(const char *ip_address, int port);
MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s);
MODBUS_API modbus_t* modbus_new_tcp_pi(const char *node, const char *service);
MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s);
MODBUS_END_DECLS
#endif /* MODBUS_TCP_H */

53
src/modbus-version.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright © 2010-2014 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MODBUS_VERSION_H
#define MODBUS_VERSION_H
/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */
#define LIBMODBUS_VERSION_MAJOR (3)
/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */
#define LIBMODBUS_VERSION_MINOR (1)
/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */
#define LIBMODBUS_VERSION_MICRO (6)
/* The full version, like 1.2.3 */
#define LIBMODBUS_VERSION 3.1.6
/* The full version, in string form (suited for string concatenation)
*/
#define LIBMODBUS_VERSION_STRING "3.1.6"
/* Numerically encoded version, eg. v1.2.3 is 0x010203 */
#define LIBMODBUS_VERSION_HEX ((LIBMODBUS_VERSION_MAJOR << 16) | \
(LIBMODBUS_VERSION_MINOR << 8) | \
(LIBMODBUS_VERSION_MICRO << 0))
/* Evaluates to True if the version is greater than @major, @minor and @micro
*/
#define LIBMODBUS_VERSION_CHECK(major,minor,micro) \
(LIBMODBUS_VERSION_MAJOR > (major) || \
(LIBMODBUS_VERSION_MAJOR == (major) && \
LIBMODBUS_VERSION_MINOR > (minor)) || \
(LIBMODBUS_VERSION_MAJOR == (major) && \
LIBMODBUS_VERSION_MINOR == (minor) && \
LIBMODBUS_VERSION_MICRO >= (micro)))
#endif /* MODBUS_VERSION_H */

1911
src/modbus.c Normal file

File diff suppressed because it is too large Load Diff

293
src/modbus.h Normal file
View File

@ -0,0 +1,293 @@
/*
* Copyright © 2001-2013 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef MODBUS_H
#define MODBUS_H
/* Add this for macros that defined unix flavor */
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
#ifndef _MSC_VER
#include <stdint.h>
#else
#include "stdint.h"
#endif
#include "modbus-version.h"
#if defined(_MSC_VER)
# if defined(DLLBUILD)
/* define DLLBUILD when building the DLL */
# define MODBUS_API __declspec(dllexport)
# else
# define MODBUS_API __declspec(dllimport)
# endif
#else
# define MODBUS_API
#endif
#ifdef __cplusplus
# define MODBUS_BEGIN_DECLS extern "C" {
# define MODBUS_END_DECLS }
#else
# define MODBUS_BEGIN_DECLS
# define MODBUS_END_DECLS
#endif
MODBUS_BEGIN_DECLS
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef OFF
#define OFF 0
#endif
#ifndef ON
#define ON 1
#endif
/* Modbus function codes */
#define MODBUS_FC_READ_COILS 0x01
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
#define MODBUS_FC_READ_INPUT_REGISTERS 0x04
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
#define MODBUS_FC_REPORT_SLAVE_ID 0x11
#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
#define MODBUS_BROADCAST_ADDRESS 0
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
* Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
* (chapter 6 section 11 page 29)
* Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0)
*/
#define MODBUS_MAX_READ_BITS 2000
#define MODBUS_MAX_WRITE_BITS 1968
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15)
* Quantity of Registers to read (2 bytes): 1 to 125 (0x7D)
* (chapter 6 section 12 page 31)
* Quantity of Registers to write (2 bytes) 1 to 123 (0x7B)
* (chapter 6 section 17 page 38)
* Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79)
*/
#define MODBUS_MAX_READ_REGISTERS 125
#define MODBUS_MAX_WRITE_REGISTERS 123
#define MODBUS_MAX_WR_WRITE_REGISTERS 121
#define MODBUS_MAX_WR_READ_REGISTERS 125
/* The size of the MODBUS PDU is limited by the size constraint inherited from
* the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256
* bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server
* address (1 byte) - CRC (2 bytes) = 253 bytes.
*/
#define MODBUS_MAX_PDU_LENGTH 253
/* Consequently:
* - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256
* bytes.
* - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes.
* so the maximum of both backend in 260 bytes. This size can used to allocate
* an array of bytes to store responses and it will be compatible with the two
* backends.
*/
#define MODBUS_MAX_ADU_LENGTH 260
/* Random number to avoid errno conflicts */
#define MODBUS_ENOBASE 112345678
/* Protocol exceptions */
enum {
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01,
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE,
MODBUS_EXCEPTION_ACKNOWLEDGE,
MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY,
MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE,
MODBUS_EXCEPTION_MEMORY_PARITY,
MODBUS_EXCEPTION_NOT_DEFINED,
MODBUS_EXCEPTION_GATEWAY_PATH,
MODBUS_EXCEPTION_GATEWAY_TARGET,
MODBUS_EXCEPTION_MAX
};
#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
/* Native libmodbus error codes */
#define EMBBADCRC (EMBXGTAR + 1)
#define EMBBADDATA (EMBXGTAR + 2)
#define EMBBADEXC (EMBXGTAR + 3)
#define EMBUNKEXC (EMBXGTAR + 4)
#define EMBMDATA (EMBXGTAR + 5)
#define EMBBADSLAVE (EMBXGTAR + 6)
extern const unsigned int libmodbus_version_major;
extern const unsigned int libmodbus_version_minor;
extern const unsigned int libmodbus_version_micro;
typedef struct _modbus modbus_t;
typedef struct _modbus_mapping_t {
int nb_bits;
int start_bits;
int nb_input_bits;
int start_input_bits;
int nb_input_registers;
int start_input_registers;
int nb_registers;
int start_registers;
uint8_t *tab_bits;
uint8_t *tab_input_bits;
uint16_t *tab_input_registers;
uint16_t *tab_registers;
} modbus_mapping_t;
typedef enum
{
MODBUS_ERROR_RECOVERY_NONE = 0,
MODBUS_ERROR_RECOVERY_LINK = (1<<1),
MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2)
} modbus_error_recovery_mode;
MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave);
MODBUS_API int modbus_get_slave(modbus_t* ctx);
MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery);
MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
MODBUS_API int modbus_get_socket(modbus_t *ctx);
MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
MODBUS_API int modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
MODBUS_API int modbus_get_header_length(modbus_t *ctx);
MODBUS_API int modbus_connect(modbus_t *ctx);
MODBUS_API void modbus_close(modbus_t *ctx);
MODBUS_API void modbus_free(modbus_t *ctx);
MODBUS_API int modbus_flush(modbus_t *ctx);
MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
MODBUS_API const char *modbus_strerror(int errnum);
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
MODBUS_API int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb,
const uint16_t *src, int read_addr, int read_nb,
uint16_t *dest);
MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
MODBUS_API modbus_mapping_t* modbus_mapping_new_start_address(
unsigned int start_bits, unsigned int nb_bits,
unsigned int start_input_bits, unsigned int nb_input_bits,
unsigned int start_registers, unsigned int nb_registers,
unsigned int start_input_registers, unsigned int nb_input_registers);
MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
int nb_registers, int nb_input_registers);
MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);
MODBUS_API int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);
MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req,
int req_length, modbus_mapping_t *mb_mapping);
MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
unsigned int exception_code);
/**
* UTILS FUNCTIONS
**/
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
(((int64_t)tab_int16[(index) ] << 48) + \
((int64_t)tab_int16[(index) + 1] << 32) + \
((int64_t)tab_int16[(index) + 2] << 16) + \
(int64_t)tab_int16[(index) + 3])
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) + tab_int16[(index) + 1])
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) + tab_int8[(index) + 1])
#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
do { \
tab_int8[(index)] = (value) >> 8; \
tab_int8[(index) + 1] = (value) & 0xFF; \
} while (0)
#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \
do { \
tab_int16[(index) ] = (value) >> 16; \
tab_int16[(index) + 1] = (value); \
} while (0)
#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \
do { \
tab_int16[(index) ] = (value) >> 48; \
tab_int16[(index) + 1] = (value) >> 32; \
tab_int16[(index) + 2] = (value) >> 16; \
tab_int16[(index) + 3] = (value); \
} while (0)
MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value);
MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits,
const uint8_t *tab_byte);
MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits);
MODBUS_API float modbus_get_float(const uint16_t *src);
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
MODBUS_API void modbus_set_float(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest);
#include "modbus-tcp.h"
#include "modbus-rtu.h"
MODBUS_END_DECLS
#endif /* MODBUS_H */

716
thread.cpp Normal file
View File

@ -0,0 +1,716 @@
#include "thread.h"
#include "camera.h"
#include "QMutex"
#include "QSemaphore"
#include <QtEndian> // 引入Qt处理字节序的头文件
#include <QBuffer>
#include "QTimer"
#include <QTime>
#include <QElapsedTimer>
QImage image[5];
//三个线程实例化对象
ProcessImg *processimg;
SendThread *sendthread;
RecvThread *recvthread;
QMutex judge_connect_mutex;
//RGB相机类跟光谱相机类实例化对象
CameraL *cameraL;
SpecCamera *speccamera;
bool is_connected;
extern tomato tomato;
//TCP协议
extern QTcpServer server;
extern QTcpSocket *clientSocket;
extern int start_flag;
bool spec_flag = 0;
extern bool is_timeout;
extern modbus_t* CZ;
QMutex imgR_muex;
//外部引用管道
HANDLE hPipe;
HANDLE specPipe;
HANDLE RPipe;
QString pipeName = "\\\\.\\pipe\\rgb_receive";
QString pipeName2 = "\\\\.\\pipe\\spec_receive";
QString rePipe = "\\\\.\\pipe\\rgb_send";
extern int save_flag;
extern int fruit_flag;
uint countimgR = 1;
uint countimgL = 1;
uint countimgTopT = 1;
uint countimgTopM = 1;
uint countimgTopB = 1;
int ImgQueue_Alignment = 1;
//右侧相机回调
extern void __stdcall onImageDataCallBackFunc1(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
if (pFrameInfo) //帧信息有效
{
//img 原始图
QImage img(pData,pFrameInfo->nWidth,pFrameInfo->nHeight,QImage::Format_RGB888); //构造图像
//复制临时图 为了后续处理
QImage tempimg = img;
tempimg = tempimg.rgbSwapped();
QImage queueImg = tempimg;
QString filePath = "./image/R";
QString imagePath = QString("%1/%2.bmp").arg(filePath).arg(countimgR);
if(save_flag)
{
tempimg.save(imagePath);
}
emit cameraL->send_Rgbimage(tempimg);
countimgR++;
// cameraL->rightImgQueue.push(queueImg);
// qDebug()<<"右侧相机队列长度: "<<cameraL->rightImgQueue.size();
}
return ;
}
extern void __stdcall onOfflineCallBackFunc1(unsigned int nMsgType, void* pUser)
{
//qDebug()<<"camera offline";
judge_connect_mutex.lock();
is_connected = false;
judge_connect_mutex.unlock();
return;
}
//左侧相机回调
extern void __stdcall onImageDataCallBackFunc2(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
if (pFrameInfo)
{
// qDebug()<<"左侧相机帧号: "<<pFrameInfo->nFrameNum;
QImage imgL(pData,pFrameInfo->nWidth,pFrameInfo->nHeight,QImage::Format_RGB888);
QImage tempimg = imgL;
tempimg = tempimg.rgbSwapped();
QImage queueImg = tempimg;
QString filePath = "./image/L";
QString imagePath = QString("%1/%2.bmp").arg(filePath).arg(countimgL);
if(save_flag)
{
tempimg.save(imagePath);
}
countimgL++;
emit cameraL->send_Rgbimage1(tempimg);
// cameraL->leftImgQueue.push(queueImg);
// qDebug()<<"左侧相机队列长度: "<<cameraL->leftImgQueue.size();
}
return ;
}
extern void __stdcall onOfflineCallBackFunc2(unsigned int nMsgType, void* pUser)
{
judge_connect_mutex.lock();
is_connected = false;
judge_connect_mutex.unlock();
return;
}
//顶部相机回调ip[4] 200 数据指针 数据是8位 图像默认格式是BGR 当前帧的结构体
extern void __stdcall onImageDataCallBackFunc(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
if (pFrameInfo) //帧信息有效
{
qDebug()<<"彩色相机触发次数:"<<countimgTopT;
QImage imgT(pData,pFrameInfo->nWidth,pFrameInfo->nHeight,QImage::Format_RGB888); //构造图像
/*
*
* */
int width = imgT.width();
int height = imgT.height();
int partHeight = height / 3;
QImage topPart = imgT.copy(0, 0, width, partHeight);
QImage middlePart = imgT.copy(0, partHeight, width, partHeight);
QImage bottomPart = imgT.copy(0, 2 * partHeight, width, partHeight);
/*
* +
* */
QImage tempimg = bottomPart;
tempimg = tempimg.rgbSwapped();
QImage queueImg = tempimg;
QString filePath = "./image/T/bottom";
QString imagePath = QString("%1/%2.bmp").arg(filePath).arg(countimgTopB);
if(save_flag)
{
tempimg.save(imagePath);
}
countimgTopB++;
QImage tempimg1 = middlePart;
tempimg1 = tempimg1.rgbSwapped();
QImage queueImg1 = tempimg1;
QString filePath1 = "./image/T/middle";
QString imagePath1 = QString("%1/%2.bmp").arg(filePath1).arg(countimgTopM);
if(save_flag)
{
tempimg1.save(imagePath1);
}
countimgTopM++;
QImage tempimg2 = topPart;
tempimg2 = tempimg2.rgbSwapped();
QImage queueImg2 = tempimg2;
QString filePath2 = "./image/T/top";
QString imagePath2 = QString("%1/%2.bmp").arg(filePath2).arg(countimgTopT);
if(save_flag)
{
tempimg2.save(imagePath2);
}
countimgTopT++;
emit cameraL->send_Rgbimage2(tempimg,tempimg1,tempimg2);
cameraL->topImgTQueue.push(queueImg);
cameraL->topImgMQueue.push(queueImg1);
cameraL->topImgBQueue.push(queueImg2);
}
return ;
}
extern void __stdcall onOfflineCallBackFunc(unsigned int nMsgType, void* pUser)
{
//qDebug()<<"camera offline";
judge_connect_mutex.lock();
is_connected = false;
judge_connect_mutex.unlock();
return;
}
int spec_count;
//光谱相机的回调函数 拿到数据后触发信号 并转到曹函数 cal 处理数据 如果放在回调处理数据会很慢
void __stdcall RTSpecStreamingCallback(void *pContext, unsigned char *pData, unsigned long dataLength)
{
if(pData)
{
// spec_count++;
// qDebug()<<"光谱相机触发次数:"<<spec_count;
if(fruit_flag)
{
speccamera->cal(pData);
}
else
{
pData = nullptr;
}
}
return;
}
//显示光谱伪彩色 不用管
void __stdcall RTRGBViewCallback(void* pContext, unsigned char* pRData, unsigned char* pGData, unsigned char* pBData, unsigned long dataLength)
{
unsigned short* pShortRData = (unsigned short*)pRData;
unsigned short* pShortGData = (unsigned short*)pGData;
unsigned short* pShortBData = (unsigned short*)pBData;
unsigned short m_usCurrentMax = 1;
unsigned short m_usCurrentMin = 65535;
for (int i = 0; i < speccamera->m_iWidth; i++) //拉伸
{
if (pShortRData[i] > m_usCurrentMax)
m_usCurrentMax = pShortRData[i];
if (pShortGData[i] > m_usCurrentMax)
m_usCurrentMax = pShortGData[i];
if (pShortBData[i] > m_usCurrentMax)
m_usCurrentMax = pShortBData[i];
if (pShortRData[i] < m_usCurrentMin)
m_usCurrentMin = pShortRData[i];
if (pShortGData[i] < m_usCurrentMin)
m_usCurrentMin = pShortGData[i];
if (pShortBData[i] < m_usCurrentMin)
m_usCurrentMin = pShortBData[i];
}
int iLineByte = (speccamera->m_iWidth * 3 + 3) / 4 * 4; //字节对齐
memcpy(speccamera->m_pusSaveBuffer, speccamera->m_pusSaveBuffer + iLineByte, (speccamera->m_iHeight - 1)*iLineByte*sizeof(unsigned short));
for (int i = speccamera->m_iWidth - 1; i >= 0; i = i - 1)
{
int sel = (speccamera->m_iHeight - 1)* iLineByte + i * 3;
if (sel < iLineByte * speccamera->m_iHeight )
{
speccamera->m_pusSaveBuffer[sel + 2] = pShortRData[i];
speccamera->m_pusSaveBuffer[sel + 1] = pShortGData[i];
speccamera->m_pusSaveBuffer[sel + 0] = pShortBData[i];
}
} //拷贝
speccamera->m_iDrawCount++;
// qDebug()<<"speccamera->m_iDrawCount: "<<speccamera->m_iDrawCount;
for (int i = 0; i < speccamera->m_iHeight; i++)
{
for (int j = 0; j < speccamera->m_iWidth; j++)
{
if (m_usCurrentMax - m_usCurrentMin*0.9 == 0)
{
speccamera->m_pRefreshBuffer[i*iLineByte+j*3 + 0] = 0;
speccamera->m_pRefreshBuffer[i*iLineByte+j*3 + 1] = 0;
speccamera->m_pRefreshBuffer[i*iLineByte+j*3 + 2] = 0;
}
else
{
speccamera->m_pRefreshBuffer[i*iLineByte+j*3 + 0] = (speccamera->m_pusSaveBuffer[i*iLineByte+j*3 + 2]-m_usCurrentMin*0.9)*255.0/(m_usCurrentMax-m_usCurrentMin*0.9);
speccamera->m_pRefreshBuffer[i*iLineByte+j*3 + 1] = (speccamera->m_pusSaveBuffer[i*iLineByte+j*3 + 1]-m_usCurrentMin*0.9)*255.0/(m_usCurrentMax-m_usCurrentMin*0.9);
speccamera->m_pRefreshBuffer[i*iLineByte+j*3 + 2] = (speccamera->m_pusSaveBuffer[i*iLineByte+j*3 + 0]-m_usCurrentMin*0.9)*255.0/(m_usCurrentMax-m_usCurrentMin*0.9);
}
}
}
QImage image(speccamera->m_pRefreshBuffer,speccamera->m_iWidth, speccamera->m_iHeight, QImage::Format_RGB888);
// speccamera->specImgQueue.push(speccamera->m_pRefreshBuffer);
emit speccamera->show_SpecImg(image);
return;
}
ProcessImg::ProcessImg(QObject *parent) : QThread(parent)
{
}
ProcessImg::~ProcessImg()
{
}
void ProcessImg::exitThread()
{
stop_mutex.lock();
m_stop = true;
stop_mutex.unlock();
}
//光谱数据处理线程 当 槽函数 cal 收集到指定数量的照片 也就是 满足 if (speccamera->frameData.size() == speccamera->totalFrames)
//就会进行处理
void ProcessImg::run()
{
while(1)
{
// 检查是否已收集到足够的帧
if (speccamera->SpecData_vector.size() == 25)
{
// 处理这些帧,例如拼接成一张大图
// qDebug()<<"25行光谱图开始拼接、存队列";
spec_count++;
msleep(100);
speccamera->test_count = 0;
// qDebug()<<"光谱相机触发次数"<<spec_count;
speccamera->specfullImage = new unsigned short[30 * 25 * 13];//宽 * 高 * 谱段数
for (int i = 0; i < 25; i++)
{
// qDebug()<<"容量:"<<speccamera->SpecData_vector.capacity();
// qDebug()<<"容量实际大小:"<<speccamera->SpecData_vector.size();
memcpy(speccamera->specfullImage + i * 30 * 13, speccamera->SpecData_vector[i], 30 * 13 * sizeof(unsigned short));
}
speccamera->specImgQueue.push(speccamera->specfullImage);
// qDebug()<<"光谱入队成功";
if(save_flag)
{
static int file_index = 1;
std::string spec_filename = "./image/specImg/spec" + std::to_string(file_index);
FILE *fp = fopen(spec_filename.c_str(), "wb");
int ret = fwrite(speccamera->specfullImage, 1, 30 * 25 * 13 * sizeof(unsigned short), fp);
std::fflush(fp);
fclose(fp);
file_index++;
qDebug()<<"save img success";
qDebug()<<"test_count: "<<speccamera->test_count;
}
speccamera->SpecData_vector.clear();
speccamera->test_count = 0;
}
}
}
//接收线程
RecvThread::RecvThread(QObject *parent) : QThread(parent)
{
}
RecvThread::~RecvThread()
{
}
//通过 pipe 管道 从python端读取数据
void RecvThread::run()
{
while (1)
{
// 读取4字节的数据长度信息
DWORD bytesRead;
quint32 dataLength;
QByteArray lengthBytes(4, 0);
BOOL success = ReadFile(RPipe, lengthBytes.data(), 4, &bytesRead, NULL);
if (!success || bytesRead != 4)
{
qDebug() << "Failed to read length from pipe:" << GetLastError();
CloseHandle(RPipe);
return;
}
QDataStream lengthStream(lengthBytes);
lengthStream.setByteOrder(QDataStream::BigEndian);
lengthStream >> dataLength;
// qDebug() << "应该接收到的数据长度:" << dataLength;
// 根据读取到的数据长度,读取对应长度的数据
QByteArray data(dataLength, 0);
success = ReadFile(RPipe, data.data(), dataLength, &bytesRead, NULL);
if (!success || bytesRead != dataLength) {
qDebug() << "Failed to read data from pipe:" << GetLastError();
CloseHandle(RPipe);
return;
}
// qDebug() << "接收到的数据长度:" << bytesRead;
// 解析数据
QDataStream dataStream(data);
dataStream.setByteOrder(QDataStream::BigEndian);
// 解析命令和其他数据
QString cmd;
quint16 brix, diameter, defectNum, height, width;
quint8 greenPercentage, weight;
quint32 totalDefectArea;
char cmdChars[2];
dataStream.readRawData(cmdChars, 2);
cmd = QString::fromLatin1(cmdChars, 2).trimmed().toUpper();
// 判断指令是否为 "RE"
if (cmd == "RE")
{
// qDebug() << "Received 'RE' command. Processing accordingly.";
// 继续解析其他数据
dataStream >> brix >> greenPercentage >> diameter >> weight >> defectNum >> totalDefectArea >> height >> width;
// qDebug() << "命令:" << cmd;
// qDebug() << "Brix:" << brix << ", Green Percentage:" << greenPercentage << ", Diameter:" << diameter;
// qDebug() << "Weight:" << weight << ", Defect Number:" << defectNum << ", Total Defect Area:" << totalDefectArea;
// qDebug() << "Height:" << height << ", Width:" << width;
// // 剩余的数据为图像数据
// qDebug() << "data: " <<data.size() <<"Current position in data stream:" << dataStream.device()->pos();
QByteArray imageData = data.right(data.size() - dataStream.device()->pos());
// qDebug() << "imageData size: " << imageData.size();
QImage imageResult((uchar*)imageData.constData(), width, height, QImage::Format_RGB888);
if (!imageResult.isNull()) {
// qDebug() << "Image loaded successfully";
QImage swappedImage = imageResult.rgbSwapped();
emit recv_Data(brix,greenPercentage,diameter,weight,defectNum,totalDefectArea,height,width,swappedImage);
// imageResult.save("D:/WeChat Files/wxid_jjgh0jfum83v12/FileStorage/File/2024-06/1.png");
}
else
{
qDebug() << "Error: Failed to load image from raw data";
}
// qDebug() << "接收并处理完成";
}
}
}
SendThread::SendThread(QObject *parent) : QThread(parent)
{
}
SendThread::~SendThread()
{
}
bool SendThread::sendData(HANDLE &hPipe, const QByteArray &data)
{
DWORD bytesWritten;
if (!WriteFile(hPipe, data.constData(), static_cast<DWORD>(data.size()), &bytesWritten, nullptr) || bytesWritten == 0) {
DWORD dwError = GetLastError();
qDebug()<<""<<countimgTopT<<""<<"发送失败时字节数: "<<data.size();
qDebug() << "Failed to write to pipe or no bytes written. Error:" << dwError;
return false;
}
// qDebug()<<"第"<<countimgTopT<<"次"<<"发送成功字节数: "<<data.size();
return true;
}
HANDLE SendThread::reconnect(const QString &pipeName)
{
CloseHandle(hPipe); // Close the old handle before reconnecting
HANDLE hPipe = CreateFile((LPCWSTR)pipeName.utf16(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
qWarning() << "Failed to connect to pipe:" << GetLastError();
}
return hPipe;
}
void SendThread::SendImgToPython()
{
}
//通过 pipe 管道 给python端发送数据
void SendThread::run()
{
if(Yure_Flag && (hPipe != INVALID_HANDLE_VALUE))
{
// qDebug() << "预热前";
QByteArray dataToSend;
QDataStream dataStream(&dataToSend, QIODevice::WriteOnly);
dataStream.setByteOrder(QDataStream::BigEndian);
quint32 lenth = 2;
dataStream << lenth;
// 添加命令
QString cmd = "YR";
dataStream.writeRawData(cmd.toLocal8Bit().constData(), 2);
// 发送数据
DWORD yr_Written;
if (!WriteFile(hPipe, dataToSend, 6, &yr_Written, nullptr) || yr_Written != 6) {
DWORD dwError = GetLastError();
qDebug() << "Failed to write to pipe or incorrect number of bytes written. Error:" << dwError;
return;
}
else
{
qDebug() << "预热成功" ;
}
}
while(1)
{
//读取五个RGB队列的队首数据
if(cameraL->topImgTQueue.size()>15)
{
QElapsedTimer timer;
timer.start();
//数据对齐
if(ImgQueue_Alignment)
{
cameraL->topImgMQueue.pop();
cameraL->topImgMQueue.pop();
cameraL->topImgBQueue.pop();
cameraL->topImgBQueue.pop();
cameraL->topImgBQueue.pop();
cameraL->topImgTQueue.pop();
ImgQueue_Alignment = 0;
}
QImage temp[5];
temp[0] = cameraL->topImgTQueue.front();
cameraL->topImgTQueue.pop();
qDebug()<<"顶部相机顶队列大小:"<<cameraL->topImgTQueue.size();
temp[1] = cameraL->topImgMQueue.front();
cameraL->topImgMQueue.pop();
qDebug()<<"顶部相机中队列大小:"<<cameraL->topImgMQueue.size();
temp[2] = cameraL ->topImgBQueue.front();
cameraL -> topImgBQueue.pop();
qDebug()<<"顶部相机下队列大小:"<<cameraL->topImgBQueue.size();
// temp[3] = cameraL->leftImgQueue.front();
// cameraL->leftImgQueue.pop();
// qDebug()<<"左侧相机队列大小:"<<cameraL->leftImgQueue.size();
// temp[4] = cameraL->rightImgQueue.front();
// cameraL->rightImgQueue.pop();
// qDebug()<<"右侧相机队列大小:"<<cameraL->rightImgQueue.size();
//循环五次 将 temp[5] 中的所有图像数据转换为字节流 用于 pipe 管道发送
for(int i=0;i<3;i++)
{
// Qt_image_count ++;
// QString filePath = "C:/Users/succtech/Desktop/Qt_image";
// QString imagePath = QString("%1/%2.bmp").arg(filePath).arg(Qt_image_count);
// temp[i].save(imagePath);
int width = temp[i].width();
int height = temp[i].height();
// qDebug()<<"原始图像字节: "<<temp[i].byteCount();
QByteArray imgData(reinterpret_cast<const char*>(temp[i].bits()), temp[i].byteCount());
// qDebug()<<"现在图像字节: "<<imgData.size();
QDataStream stream(&imgData, QIODevice::ReadWrite);
stream.setByteOrder(QDataStream::BigEndian);
QByteArray dataToSend;
QDataStream dataStream(&dataToSend, QIODevice::WriteOnly);
dataStream.setByteOrder(QDataStream::BigEndian);
quint32 length = static_cast<quint32>(imgData.size() + 6);
dataStream << length;
if(fruit_flag) //百香果
{
// 添加命令
QString cmd = "PF";
dataStream.writeRawData(cmd.toLocal8Bit().constData(), 2);
// Dimensions
dataStream << static_cast<quint16>(height);
dataStream << static_cast<quint16>(width);
// Image data
dataStream.writeRawData(imgData.constData(), imgData.size());
if (!sendData(hPipe, dataToSend))
{
qWarning() << "Failed to send data, attempting to reconnect...";
specPipe = reconnect(pipeName2);
if (specPipe == INVALID_HANDLE_VALUE) return;
if (!sendData(specPipe, dataToSend)) return;
return;
}
//当第五张RGB图发送完之后发送光谱图
if(i==2)
{
unsigned short* specTemp = speccamera->specImgQueue.front();
// qDebug()<<"光谱数据大小:"<<specTemp;
speccamera->specImgQueue.pop();
// qDebug() << "光谱队列长度: " << speccamera->specImgQueue.size();
int specWidth = 30;
int specHeight = 25;
int specBands = 13;
QByteArray specByte,specByte_tosend;
specByte.append(reinterpret_cast<const char*>(specTemp), specWidth * specHeight * specBands * sizeof(unsigned short));
qDebug()<<"specByte: "<<specByte.size();
QDataStream spec_stream(&specByte_tosend, QIODevice::ReadWrite);
spec_stream.setByteOrder(QDataStream::BigEndian);
quint32 length = static_cast<quint32>(specByte.size() + 8);
spec_stream << length;
// 添加命令
QString cmd = "PF";
spec_stream.writeRawData(cmd.toLocal8Bit().constData(), 2);
// Dimensions
spec_stream << static_cast<quint16>(specHeight);
spec_stream << static_cast<quint16>(specWidth);
spec_stream << static_cast<quint16>(specBands);
specByte_tosend.append(specByte);
if (!sendData(specPipe, specByte_tosend))
{
qWarning() << "Failed to send data, attempting to reconnect...";
specPipe = reconnect(pipeName2);
if (specPipe == INVALID_HANDLE_VALUE) return;
if (!sendData(specPipe, specByte_tosend)) return;
return;
}
// qDebug() << "write to spec_pipe." << specByte_tosend.size();
msleep(10);
// qDebug() << "发送完成:";
}
}
else //番茄
{
// 添加命令
QString cmd = "TO";
dataStream.writeRawData(cmd.toLocal8Bit().constData(), 2);
// Dimensions
dataStream << static_cast<quint16>(height);
dataStream << static_cast<quint16>(width);
// Image data
dataStream.writeRawData(imgData.constData(), imgData.size());
if (!sendData(hPipe, dataToSend))
{
qWarning() << "Failed to send data, attempting to reconnect...";
specPipe = reconnect(pipeName2);
if (specPipe == INVALID_HANDLE_VALUE) return;
if (!sendData(specPipe, dataToSend)) return;
return;
}
// if(i == 2)
// {
// // qDebug() << "光谱队列长度: " << speccamera->specImgQueue.size();
// speccamera->specImgQueue.pop();
// qDebug() << "发送完成:";
// }
}
// qint64 elapsed = timer.elapsed();
// qDebug()<<"间隔时间"<<elapsed;
// msleep(100);
}
// qint64 elapsed = timer.elapsed();
// qDebug()<<"间隔时间"<<elapsed;
}
}
}

101
thread.h Normal file
View File

@ -0,0 +1,101 @@
#ifndef THREAD_H
#define THREAD_H
#include "QThread"
#include "QFile"
#include <queue>
#include <QFileDialog>
#include "camera.h"
#include "queue"
#include "QMutex"
#include <QTcpServer>
#include <QTcpSocket>
#include "src/modbus.h"
#include <QImage>
//处理线程 负责光谱相机的完整数据拼接
class ProcessImg : public QThread
{
Q_OBJECT
public:
explicit ProcessImg(QObject *parent = nullptr);
~ProcessImg();
void exitThread();
QMutex stop_mutex;
int spec_count;
protected:
void run();
private:
bool m_stop;
signals:
public slots:
private:
};
//发送线程 负责使用管道 pipe 给python端发送五张图片
//五张图片分别为 顶部 上、中、下三张 左 右 两张
//待修改:
//1、数据对齐
class SendThread: public QThread
{
Q_OBJECT
protected:
void run();
public:
explicit SendThread(QObject *parent = NULL);
~SendThread();
bool sendData(HANDLE &hPipe, const QByteArray &data);
HANDLE reconnect(const QString &pipeName);
int Yure_Flag = 1;
int Qt_image_count = 0;
private:
void SendImgToPython();
signals:
private slots:
//void sendimg_topySlots(cv::Mat img1,cv::Mat img2,cv::Mat img3,cv::Mat img4,cv::Mat img5);
};
//接收线程 负责接收python端通过 pipe 返回的数据 长径 短径 缺陷数量 缺陷面积 并在窗口展示
//待修改:
//1、具体返回值放在哪里 窗口位置需要调整
//2、判断条件写死 具体数据写活
class RecvThread: public QThread
{
Q_OBJECT
protected:
void run();
public:
explicit RecvThread(QObject *parent = NULL);
~RecvThread();
public slots:
signals:
void recv_Data(quint16,quint8,quint16,quint8,quint16,quint32,quint16,quint16,QImage);
};
#endif // THREAD_H

59
tomato1227.pro Normal file
View File

@ -0,0 +1,59 @@
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
camera.cpp \
main.cpp \
src/modbus-data.c \
src/modbus-rtu.c \
src/modbus-tcp.c \
src/modbus.c \
thread.cpp \
widget.cpp
HEADERS += \
camera.h \
src/modbus-private.h \
src/modbus-rtu-private.h \
src/modbus-rtu.h \
src/modbus-tcp-private.h \
src/modbus-tcp.h \
src/modbus-version.h \
src/modbus.h \
thread.h \
widget.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
src/modbus.lib
LIBS += -Ldll -lws2_32
#查看所有路径知否正确 RGB opencv 光谱相机
#建议改成绝对路径
# rgb camera SDK
LIBS += -LD:/porject/20240425/rgbcamera_sdk/Libraries/win64 -lMvCameraControl
INCLUDEPATH += D:/porject/20240425/rgbcamera_sdk/Includes
DEPENDPATH += D:/porject/20240425/rgbcamera_sdk/Includes
# spec
INCLUDEPATH += D:/porject/20240425/RT_SDK/SDK/include
DEPENDPATH += D:/porject/20240425/RT_SDK/SDK/include
LIBS += -LD:/porject/20240425/RT_SDK/SDK/lib -lSpectrolDll

863
ui_widget.h Normal file
View File

@ -0,0 +1,863 @@
/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 5.15.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPlainTextEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
QGridLayout *gridLayout_10;
QTabWidget *tabWidget;
QWidget *tab_3;
QGridLayout *gridLayout_12;
QLabel *label;
QLabel *label_23;
QWidget *tab;
QGroupBox *groupBox_9;
QGridLayout *gridLayout_11;
QGroupBox *groupBox;
QLabel *label_14;
QWidget *layoutWidget_2;
QHBoxLayout *horizontalLayout_14;
QLabel *label_RGB;
QLabel *RGBstatus;
QLabel *label_RGB_3;
QLabel *RGBstatus_1;
QLabel *label_RGB_2;
QLabel *RGBstatus_2;
QLabel *label_9;
QLabel *spec_camera_status;
QLabel *label_11;
QLabel *plc_status;
QLabel *label_17;
QLabel *jxs_status;
QWidget *layoutWidget;
QVBoxLayout *verticalLayout_5;
QWidget *layoutWidget1;
QHBoxLayout *horizontalLayout_8;
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
QLabel *label_26;
QPushButton *passionBtn;
QPushButton *tomatoBtn;
QPushButton *btn_start;
QPushButton *show_resoult;
QPushButton *btn_setparam;
QPushButton *btn_save;
QPushButton *view_results;
QPushButton *btn_quit;
QVBoxLayout *verticalLayout_2;
QGroupBox *groupBox_4;
QGridLayout *gridLayout_2;
QGroupBox *groupBox_2;
QGridLayout *gridLayout;
QLabel *label_topL;
QGroupBox *groupBox_3;
QGridLayout *gridLayout_3;
QLabel *label_topM;
QGroupBox *groupBox_5;
QGridLayout *gridLayout_4;
QLabel *label_topR;
QGroupBox *groupBox_6;
QGridLayout *gridLayout_5;
QLabel *showimg_right;
QGroupBox *groupBox_7;
QGridLayout *gridLayout_6;
QLabel *showimg_left;
QGroupBox *groupBox_8;
QGridLayout *gridLayout_7;
QLabel *spec_camera_show;
QWidget *tab_2;
QGroupBox *groupBox_11;
QGridLayout *gridLayout_13;
QLabel *label_28;
QSpinBox *exSpinBox_left;
QLabel *label_29;
QSpinBox *wbSpinBox_left_2;
QLabel *label_30;
QSpinBox *GainSpinBox_left;
QGroupBox *groupBox_13;
QGridLayout *gridLayout_16;
QLabel *label_38;
QSpinBox *exspinBox_top;
QLabel *label_39;
QSpinBox *wbspinBox_top;
QLabel *label_40;
QSpinBox *GainspinBox_top;
QGroupBox *groupBox_15;
QGridLayout *gridLayout_18;
QLabel *label_46;
QSpinBox *exspinBox_right;
QLabel *label_47;
QSpinBox *wbspinBox_right;
QLabel *label_48;
QSpinBox *GainspinBox_right;
QPushButton *btn_enterparam;
QPushButton *tab3_ReturnToMain;
QPushButton *choose_to;
QPushButton *choose_pa;
QLineEdit *InputNum1;
QLineEdit *InputNum5;
QLabel *label_5;
QLineEdit *InputNum4;
QPushButton *set_threshoid;
QLabel *label_32;
QLineEdit *InputNum3;
QLabel *label_33;
QLabel *label_10;
QLabel *label_18;
QLineEdit *InputNum2;
QLabel *label_12;
QLabel *label_21;
QWidget *tab_4;
QLabel *show_resultsImg;
QLabel *label_3;
QPushButton *tab4_ReturnToMain;
QPlainTextEdit *resoult_info_label;
QPushButton *choose_pa_2;
QLabel *label_13;
QPushButton *choose_to_2;
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName(QString::fromUtf8("Widget"));
Widget->resize(2000, 1038);
gridLayout_10 = new QGridLayout(Widget);
gridLayout_10->setObjectName(QString::fromUtf8("gridLayout_10"));
tabWidget = new QTabWidget(Widget);
tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
tabWidget->setStyleSheet(QString::fromUtf8(""));
tab_3 = new QWidget();
tab_3->setObjectName(QString::fromUtf8("tab_3"));
gridLayout_12 = new QGridLayout(tab_3);
gridLayout_12->setObjectName(QString::fromUtf8("gridLayout_12"));
label = new QLabel(tab_3);
label->setObjectName(QString::fromUtf8("label"));
QFont font;
font.setPointSize(36);
font.setBold(false);
label->setFont(font);
label->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
label->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
gridLayout_12->addWidget(label, 1, 1, 2, 2);
label_23 = new QLabel(tab_3);
label_23->setObjectName(QString::fromUtf8("label_23"));
label_23->setPixmap(QPixmap(QString::fromUtf8("H:/\351\241\271\347\233\256\344\273\243\347\240\201/tobacco/uppermachine-tobacco/image/njfu.jpg")));
gridLayout_12->addWidget(label_23, 0, 0, 1, 1);
tabWidget->addTab(tab_3, QString());
tab = new QWidget();
tab->setObjectName(QString::fromUtf8("tab"));
groupBox_9 = new QGroupBox(tab);
groupBox_9->setObjectName(QString::fromUtf8("groupBox_9"));
groupBox_9->setGeometry(QRect(9, 9, 20, 20));
gridLayout_11 = new QGridLayout(groupBox_9);
gridLayout_11->setObjectName(QString::fromUtf8("gridLayout_11"));
groupBox = new QGroupBox(tab);
groupBox->setObjectName(QString::fromUtf8("groupBox"));
groupBox->setGeometry(QRect(10, -20, 1961, 161));
QFont font1;
font1.setPointSize(12);
groupBox->setFont(font1);
label_14 = new QLabel(groupBox);
label_14->setObjectName(QString::fromUtf8("label_14"));
label_14->setGeometry(QRect(330, 40, 511, 51));
QFont font2;
font2.setFamily(QString::fromUtf8("\345\215\216\346\226\207\346\245\267\344\275\223"));
font2.setPointSize(36);
font2.setBold(false);
font2.setItalic(false);
label_14->setFont(font2);
label_14->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 36pt \"\345\215\216\346\226\207\346\245\267\344\275\223\";"));
label_14->setTextFormat(Qt::PlainText);
layoutWidget_2 = new QWidget(groupBox);
layoutWidget_2->setObjectName(QString::fromUtf8("layoutWidget_2"));
layoutWidget_2->setGeometry(QRect(2, 90, 841, 61));
horizontalLayout_14 = new QHBoxLayout(layoutWidget_2);
horizontalLayout_14->setObjectName(QString::fromUtf8("horizontalLayout_14"));
horizontalLayout_14->setContentsMargins(0, 0, 0, 0);
label_RGB = new QLabel(layoutWidget_2);
label_RGB->setObjectName(QString::fromUtf8("label_RGB"));
QFont font3;
font3.setPointSize(12);
font3.setBold(true);
label_RGB->setFont(font3);
label_RGB->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout_14->addWidget(label_RGB);
RGBstatus = new QLabel(layoutWidget_2);
RGBstatus->setObjectName(QString::fromUtf8("RGBstatus"));
RGBstatus->setStyleSheet(QString::fromUtf8("color: rgb(255, 0, 0);"));
horizontalLayout_14->addWidget(RGBstatus);
label_RGB_3 = new QLabel(layoutWidget_2);
label_RGB_3->setObjectName(QString::fromUtf8("label_RGB_3"));
label_RGB_3->setFont(font3);
label_RGB_3->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout_14->addWidget(label_RGB_3);
RGBstatus_1 = new QLabel(layoutWidget_2);
RGBstatus_1->setObjectName(QString::fromUtf8("RGBstatus_1"));
RGBstatus_1->setStyleSheet(QString::fromUtf8("color: rgb(255, 0, 0);"));
horizontalLayout_14->addWidget(RGBstatus_1);
label_RGB_2 = new QLabel(layoutWidget_2);
label_RGB_2->setObjectName(QString::fromUtf8("label_RGB_2"));
label_RGB_2->setFont(font3);
label_RGB_2->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout_14->addWidget(label_RGB_2);
RGBstatus_2 = new QLabel(layoutWidget_2);
RGBstatus_2->setObjectName(QString::fromUtf8("RGBstatus_2"));
RGBstatus_2->setStyleSheet(QString::fromUtf8("color: rgb(255, 0, 0);"));
horizontalLayout_14->addWidget(RGBstatus_2);
label_9 = new QLabel(layoutWidget_2);
label_9->setObjectName(QString::fromUtf8("label_9"));
label_9->setFont(font3);
label_9->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout_14->addWidget(label_9);
spec_camera_status = new QLabel(layoutWidget_2);
spec_camera_status->setObjectName(QString::fromUtf8("spec_camera_status"));
spec_camera_status->setStyleSheet(QString::fromUtf8("color: rgb(255, 0, 0);"));
horizontalLayout_14->addWidget(spec_camera_status);
label_11 = new QLabel(layoutWidget_2);
label_11->setObjectName(QString::fromUtf8("label_11"));
label_11->setFont(font3);
label_11->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout_14->addWidget(label_11);
plc_status = new QLabel(layoutWidget_2);
plc_status->setObjectName(QString::fromUtf8("plc_status"));
plc_status->setStyleSheet(QString::fromUtf8("color: rgb(255, 0, 0);"));
horizontalLayout_14->addWidget(plc_status);
label_17 = new QLabel(layoutWidget_2);
label_17->setObjectName(QString::fromUtf8("label_17"));
label_17->setFont(font3);
label_17->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout_14->addWidget(label_17);
jxs_status = new QLabel(layoutWidget_2);
jxs_status->setObjectName(QString::fromUtf8("jxs_status"));
jxs_status->setStyleSheet(QString::fromUtf8("color: rgb(255, 0, 0);"));
horizontalLayout_14->addWidget(jxs_status);
layoutWidget = new QWidget(groupBox);
layoutWidget->setObjectName(QString::fromUtf8("layoutWidget"));
layoutWidget->setGeometry(QRect(790, 220, 574, 356));
verticalLayout_5 = new QVBoxLayout(layoutWidget);
verticalLayout_5->setObjectName(QString::fromUtf8("verticalLayout_5"));
verticalLayout_5->setContentsMargins(0, 0, 0, 0);
layoutWidget1 = new QWidget(tab);
layoutWidget1->setObjectName(QString::fromUtf8("layoutWidget1"));
layoutWidget1->setGeometry(QRect(10, 140, 1951, 791));
horizontalLayout_8 = new QHBoxLayout(layoutWidget1);
horizontalLayout_8->setObjectName(QString::fromUtf8("horizontalLayout_8"));
horizontalLayout_8->setContentsMargins(0, 0, 0, 0);
verticalLayout = new QVBoxLayout();
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
horizontalLayout = new QHBoxLayout();
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
label_26 = new QLabel(layoutWidget1);
label_26->setObjectName(QString::fromUtf8("label_26"));
label_26->setFont(font3);
label_26->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout->addWidget(label_26);
passionBtn = new QPushButton(layoutWidget1);
passionBtn->setObjectName(QString::fromUtf8("passionBtn"));
passionBtn->setFont(font3);
passionBtn->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout->addWidget(passionBtn);
tomatoBtn = new QPushButton(layoutWidget1);
tomatoBtn->setObjectName(QString::fromUtf8("tomatoBtn"));
tomatoBtn->setFont(font3);
tomatoBtn->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
horizontalLayout->addWidget(tomatoBtn);
verticalLayout->addLayout(horizontalLayout);
btn_start = new QPushButton(layoutWidget1);
btn_start->setObjectName(QString::fromUtf8("btn_start"));
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(btn_start->sizePolicy().hasHeightForWidth());
btn_start->setSizePolicy(sizePolicy);
QFont font4;
font4.setFamily(QString::fromUtf8("\345\256\213\344\275\223"));
font4.setPointSize(30);
font4.setBold(false);
font4.setItalic(false);
btn_start->setFont(font4);
btn_start->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 30pt \"\345\256\213\344\275\223\";"));
verticalLayout->addWidget(btn_start);
show_resoult = new QPushButton(layoutWidget1);
show_resoult->setObjectName(QString::fromUtf8("show_resoult"));
sizePolicy.setHeightForWidth(show_resoult->sizePolicy().hasHeightForWidth());
show_resoult->setSizePolicy(sizePolicy);
show_resoult->setFont(font4);
show_resoult->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 30pt \"\345\256\213\344\275\223\";"));
verticalLayout->addWidget(show_resoult);
btn_setparam = new QPushButton(layoutWidget1);
btn_setparam->setObjectName(QString::fromUtf8("btn_setparam"));
sizePolicy.setHeightForWidth(btn_setparam->sizePolicy().hasHeightForWidth());
btn_setparam->setSizePolicy(sizePolicy);
btn_setparam->setFont(font4);
btn_setparam->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 30pt \"\345\256\213\344\275\223\";"));
verticalLayout->addWidget(btn_setparam);
btn_save = new QPushButton(layoutWidget1);
btn_save->setObjectName(QString::fromUtf8("btn_save"));
sizePolicy.setHeightForWidth(btn_save->sizePolicy().hasHeightForWidth());
btn_save->setSizePolicy(sizePolicy);
btn_save->setFont(font4);
btn_save->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 30pt \"\345\256\213\344\275\223\";"));
verticalLayout->addWidget(btn_save);
view_results = new QPushButton(layoutWidget1);
view_results->setObjectName(QString::fromUtf8("view_results"));
sizePolicy.setHeightForWidth(view_results->sizePolicy().hasHeightForWidth());
view_results->setSizePolicy(sizePolicy);
view_results->setFont(font4);
view_results->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 30pt \"\345\256\213\344\275\223\";"));
verticalLayout->addWidget(view_results);
btn_quit = new QPushButton(layoutWidget1);
btn_quit->setObjectName(QString::fromUtf8("btn_quit"));
sizePolicy.setHeightForWidth(btn_quit->sizePolicy().hasHeightForWidth());
btn_quit->setSizePolicy(sizePolicy);
btn_quit->setFont(font4);
btn_quit->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);\n"
"font: 30pt \"\345\256\213\344\275\223\";"));
verticalLayout->addWidget(btn_quit);
horizontalLayout_8->addLayout(verticalLayout);
verticalLayout_2 = new QVBoxLayout();
verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
groupBox_4 = new QGroupBox(layoutWidget1);
groupBox_4->setObjectName(QString::fromUtf8("groupBox_4"));
groupBox_4->setFont(font3);
groupBox_4->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_2 = new QGridLayout(groupBox_4);
gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2"));
groupBox_2 = new QGroupBox(groupBox_4);
groupBox_2->setObjectName(QString::fromUtf8("groupBox_2"));
groupBox_2->setFont(font3);
gridLayout = new QGridLayout(groupBox_2);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
label_topL = new QLabel(groupBox_2);
label_topL->setObjectName(QString::fromUtf8("label_topL"));
label_topL->setStyleSheet(QString::fromUtf8("background-color: rgb(170, 170, 255);"));
gridLayout->addWidget(label_topL, 0, 0, 1, 1);
gridLayout_2->addWidget(groupBox_2, 0, 0, 1, 1);
groupBox_3 = new QGroupBox(groupBox_4);
groupBox_3->setObjectName(QString::fromUtf8("groupBox_3"));
groupBox_3->setFont(font3);
gridLayout_3 = new QGridLayout(groupBox_3);
gridLayout_3->setObjectName(QString::fromUtf8("gridLayout_3"));
label_topM = new QLabel(groupBox_3);
label_topM->setObjectName(QString::fromUtf8("label_topM"));
label_topM->setStyleSheet(QString::fromUtf8("background-color: rgb(170, 170, 255);"));
gridLayout_3->addWidget(label_topM, 0, 1, 1, 1);
gridLayout_2->addWidget(groupBox_3, 0, 1, 1, 1);
groupBox_5 = new QGroupBox(groupBox_4);
groupBox_5->setObjectName(QString::fromUtf8("groupBox_5"));
groupBox_5->setFont(font3);
gridLayout_4 = new QGridLayout(groupBox_5);
gridLayout_4->setObjectName(QString::fromUtf8("gridLayout_4"));
label_topR = new QLabel(groupBox_5);
label_topR->setObjectName(QString::fromUtf8("label_topR"));
label_topR->setStyleSheet(QString::fromUtf8("background-color: rgb(170, 170, 255);"));
gridLayout_4->addWidget(label_topR, 0, 0, 1, 1);
gridLayout_2->addWidget(groupBox_5, 0, 2, 1, 1);
groupBox_6 = new QGroupBox(groupBox_4);
groupBox_6->setObjectName(QString::fromUtf8("groupBox_6"));
groupBox_6->setFont(font3);
gridLayout_5 = new QGridLayout(groupBox_6);
gridLayout_5->setObjectName(QString::fromUtf8("gridLayout_5"));
showimg_right = new QLabel(groupBox_6);
showimg_right->setObjectName(QString::fromUtf8("showimg_right"));
showimg_right->setStyleSheet(QString::fromUtf8("background-color: rgb(170, 170, 255);"));
gridLayout_5->addWidget(showimg_right, 0, 0, 1, 1);
gridLayout_2->addWidget(groupBox_6, 1, 0, 1, 1);
groupBox_7 = new QGroupBox(groupBox_4);
groupBox_7->setObjectName(QString::fromUtf8("groupBox_7"));
groupBox_7->setFont(font3);
gridLayout_6 = new QGridLayout(groupBox_7);
gridLayout_6->setObjectName(QString::fromUtf8("gridLayout_6"));
showimg_left = new QLabel(groupBox_7);
showimg_left->setObjectName(QString::fromUtf8("showimg_left"));
showimg_left->setStyleSheet(QString::fromUtf8("background-color: rgb(170, 170, 255);"));
gridLayout_6->addWidget(showimg_left, 0, 0, 1, 1);
gridLayout_2->addWidget(groupBox_7, 1, 1, 1, 1);
groupBox_8 = new QGroupBox(groupBox_4);
groupBox_8->setObjectName(QString::fromUtf8("groupBox_8"));
groupBox_8->setFont(font3);
gridLayout_7 = new QGridLayout(groupBox_8);
gridLayout_7->setObjectName(QString::fromUtf8("gridLayout_7"));
spec_camera_show = new QLabel(groupBox_8);
spec_camera_show->setObjectName(QString::fromUtf8("spec_camera_show"));
spec_camera_show->setStyleSheet(QString::fromUtf8("background-color: rgb(170, 170, 255);"));
gridLayout_7->addWidget(spec_camera_show, 0, 0, 1, 1);
gridLayout_2->addWidget(groupBox_8, 1, 2, 1, 1);
verticalLayout_2->addWidget(groupBox_4);
horizontalLayout_8->addLayout(verticalLayout_2);
horizontalLayout_8->setStretch(1, 1);
tabWidget->addTab(tab, QString());
tab_2 = new QWidget();
tab_2->setObjectName(QString::fromUtf8("tab_2"));
groupBox_11 = new QGroupBox(tab_2);
groupBox_11->setObjectName(QString::fromUtf8("groupBox_11"));
groupBox_11->setGeometry(QRect(40, 50, 531, 231));
QFont font5;
font5.setPointSize(16);
font5.setBold(true);
groupBox_11->setFont(font5);
groupBox_11->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13 = new QGridLayout(groupBox_11);
gridLayout_13->setObjectName(QString::fromUtf8("gridLayout_13"));
label_28 = new QLabel(groupBox_11);
label_28->setObjectName(QString::fromUtf8("label_28"));
label_28->setFont(font5);
label_28->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13->addWidget(label_28, 0, 0, 1, 1);
exSpinBox_left = new QSpinBox(groupBox_11);
exSpinBox_left->setObjectName(QString::fromUtf8("exSpinBox_left"));
QFont font6;
font6.setBold(true);
exSpinBox_left->setFont(font6);
exSpinBox_left->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13->addWidget(exSpinBox_left, 0, 1, 1, 1);
label_29 = new QLabel(groupBox_11);
label_29->setObjectName(QString::fromUtf8("label_29"));
label_29->setFont(font5);
label_29->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13->addWidget(label_29, 1, 0, 1, 1);
wbSpinBox_left_2 = new QSpinBox(groupBox_11);
wbSpinBox_left_2->setObjectName(QString::fromUtf8("wbSpinBox_left_2"));
wbSpinBox_left_2->setFont(font6);
wbSpinBox_left_2->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13->addWidget(wbSpinBox_left_2, 1, 1, 1, 1);
label_30 = new QLabel(groupBox_11);
label_30->setObjectName(QString::fromUtf8("label_30"));
label_30->setFont(font5);
label_30->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13->addWidget(label_30, 2, 0, 1, 1);
GainSpinBox_left = new QSpinBox(groupBox_11);
GainSpinBox_left->setObjectName(QString::fromUtf8("GainSpinBox_left"));
GainSpinBox_left->setFont(font6);
GainSpinBox_left->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_13->addWidget(GainSpinBox_left, 2, 1, 1, 1);
groupBox_13 = new QGroupBox(tab_2);
groupBox_13->setObjectName(QString::fromUtf8("groupBox_13"));
groupBox_13->setGeometry(QRect(40, 350, 531, 231));
groupBox_13->setFont(font5);
groupBox_13->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16 = new QGridLayout(groupBox_13);
gridLayout_16->setObjectName(QString::fromUtf8("gridLayout_16"));
label_38 = new QLabel(groupBox_13);
label_38->setObjectName(QString::fromUtf8("label_38"));
label_38->setFont(font5);
label_38->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16->addWidget(label_38, 0, 0, 1, 1);
exspinBox_top = new QSpinBox(groupBox_13);
exspinBox_top->setObjectName(QString::fromUtf8("exspinBox_top"));
exspinBox_top->setFont(font6);
exspinBox_top->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16->addWidget(exspinBox_top, 0, 1, 1, 1);
label_39 = new QLabel(groupBox_13);
label_39->setObjectName(QString::fromUtf8("label_39"));
label_39->setFont(font5);
label_39->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16->addWidget(label_39, 1, 0, 1, 1);
wbspinBox_top = new QSpinBox(groupBox_13);
wbspinBox_top->setObjectName(QString::fromUtf8("wbspinBox_top"));
wbspinBox_top->setFont(font6);
wbspinBox_top->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16->addWidget(wbspinBox_top, 1, 1, 1, 1);
label_40 = new QLabel(groupBox_13);
label_40->setObjectName(QString::fromUtf8("label_40"));
label_40->setFont(font5);
label_40->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16->addWidget(label_40, 2, 0, 1, 1);
GainspinBox_top = new QSpinBox(groupBox_13);
GainspinBox_top->setObjectName(QString::fromUtf8("GainspinBox_top"));
GainspinBox_top->setFont(font6);
GainspinBox_top->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_16->addWidget(GainspinBox_top, 2, 1, 1, 1);
groupBox_15 = new QGroupBox(tab_2);
groupBox_15->setObjectName(QString::fromUtf8("groupBox_15"));
groupBox_15->setGeometry(QRect(700, 60, 531, 231));
groupBox_15->setFont(font5);
groupBox_15->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18 = new QGridLayout(groupBox_15);
gridLayout_18->setObjectName(QString::fromUtf8("gridLayout_18"));
label_46 = new QLabel(groupBox_15);
label_46->setObjectName(QString::fromUtf8("label_46"));
label_46->setFont(font5);
label_46->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18->addWidget(label_46, 0, 0, 1, 1);
exspinBox_right = new QSpinBox(groupBox_15);
exspinBox_right->setObjectName(QString::fromUtf8("exspinBox_right"));
exspinBox_right->setFont(font6);
exspinBox_right->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18->addWidget(exspinBox_right, 0, 1, 1, 1);
label_47 = new QLabel(groupBox_15);
label_47->setObjectName(QString::fromUtf8("label_47"));
label_47->setFont(font5);
label_47->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18->addWidget(label_47, 1, 0, 1, 1);
wbspinBox_right = new QSpinBox(groupBox_15);
wbspinBox_right->setObjectName(QString::fromUtf8("wbspinBox_right"));
wbspinBox_right->setFont(font6);
wbspinBox_right->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18->addWidget(wbspinBox_right, 1, 1, 1, 1);
label_48 = new QLabel(groupBox_15);
label_48->setObjectName(QString::fromUtf8("label_48"));
label_48->setFont(font5);
label_48->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18->addWidget(label_48, 2, 0, 1, 1);
GainspinBox_right = new QSpinBox(groupBox_15);
GainspinBox_right->setObjectName(QString::fromUtf8("GainspinBox_right"));
GainspinBox_right->setFont(font6);
GainspinBox_right->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
gridLayout_18->addWidget(GainspinBox_right, 2, 1, 1, 1);
btn_enterparam = new QPushButton(tab_2);
btn_enterparam->setObjectName(QString::fromUtf8("btn_enterparam"));
btn_enterparam->setGeometry(QRect(710, 380, 161, 91));
btn_enterparam->setFont(font5);
btn_enterparam->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
tab3_ReturnToMain = new QPushButton(tab_2);
tab3_ReturnToMain->setObjectName(QString::fromUtf8("tab3_ReturnToMain"));
tab3_ReturnToMain->setGeometry(QRect(1300, 820, 241, 91));
tab3_ReturnToMain->setFont(font5);
tab3_ReturnToMain->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
choose_to = new QPushButton(tab_2);
choose_to->setObjectName(QString::fromUtf8("choose_to"));
choose_to->setGeometry(QRect(340, 710, 241, 141));
choose_to->setFont(font5);
choose_to->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
choose_pa = new QPushButton(tab_2);
choose_pa->setObjectName(QString::fromUtf8("choose_pa"));
choose_pa->setGeometry(QRect(40, 710, 231, 141));
choose_pa->setFont(font5);
choose_pa->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
InputNum1 = new QLineEdit(tab_2);
InputNum1->setObjectName(QString::fromUtf8("InputNum1"));
InputNum1->setGeometry(QRect(774, 720, 81, 30));
QFont font7;
font7.setPointSize(16);
font7.setBold(false);
InputNum1->setFont(font7);
InputNum5 = new QLineEdit(tab_2);
InputNum5->setObjectName(QString::fromUtf8("InputNum5"));
InputNum5->setGeometry(QRect(1050, 780, 94, 30));
InputNum5->setFont(font7);
label_5 = new QLabel(tab_2);
label_5->setObjectName(QString::fromUtf8("label_5"));
label_5->setGeometry(QRect(720, 630, 108, 21));
label_5->setFont(font5);
InputNum4 = new QLineEdit(tab_2);
InputNum4->setObjectName(QString::fromUtf8("InputNum4"));
InputNum4->setGeometry(QRect(820, 780, 81, 30));
InputNum4->setFont(font7);
set_threshoid = new QPushButton(tab_2);
set_threshoid->setObjectName(QString::fromUtf8("set_threshoid"));
set_threshoid->setGeometry(QRect(720, 830, 261, 91));
set_threshoid->setStyleSheet(QString::fromUtf8("font: 30pt \"\345\256\213\344\275\223\";"));
label_32 = new QLabel(tab_2);
label_32->setObjectName(QString::fromUtf8("label_32"));
label_32->setGeometry(QRect(720, 780, 86, 31));
label_32->setFont(font5);
InputNum3 = new QLineEdit(tab_2);
InputNum3->setObjectName(QString::fromUtf8("InputNum3"));
InputNum3->setGeometry(QRect(1140, 720, 91, 28));
InputNum3->setFont(font7);
label_33 = new QLabel(tab_2);
label_33->setObjectName(QString::fromUtf8("label_33"));
label_33->setGeometry(QRect(940, 780, 86, 31));
label_33->setFont(font5);
label_10 = new QLabel(tab_2);
label_10->setObjectName(QString::fromUtf8("label_10"));
label_10->setGeometry(QRect(717, 720, 43, 31));
label_10->setFont(font5);
label_18 = new QLabel(tab_2);
label_18->setObjectName(QString::fromUtf8("label_18"));
label_18->setGeometry(QRect(870, 720, 43, 31));
label_18->setFont(font5);
InputNum2 = new QLineEdit(tab_2);
InputNum2->setObjectName(QString::fromUtf8("InputNum2"));
InputNum2->setGeometry(QRect(930, 720, 81, 30));
InputNum2->setFont(font7);
label_12 = new QLabel(tab_2);
label_12->setObjectName(QString::fromUtf8("label_12"));
label_12->setGeometry(QRect(40, 630, 141, 21));
label_12->setFont(font5);
label_21 = new QLabel(tab_2);
label_21->setObjectName(QString::fromUtf8("label_21"));
label_21->setGeometry(QRect(1030, 720, 91, 31));
label_21->setFont(font5);
tabWidget->addTab(tab_2, QString());
tab_4 = new QWidget();
tab_4->setObjectName(QString::fromUtf8("tab_4"));
show_resultsImg = new QLabel(tab_4);
show_resultsImg->setObjectName(QString::fromUtf8("show_resultsImg"));
show_resultsImg->setGeometry(QRect(10, 60, 816, 683));
show_resultsImg->setStyleSheet(QString::fromUtf8("background-color: rgb(0, 0, 0);"));
label_3 = new QLabel(tab_4);
label_3->setObjectName(QString::fromUtf8("label_3"));
label_3->setGeometry(QRect(10, -10, 181, 81));
label_3->setFont(font5);
tab4_ReturnToMain = new QPushButton(tab_4);
tab4_ReturnToMain->setObjectName(QString::fromUtf8("tab4_ReturnToMain"));
tab4_ReturnToMain->setGeometry(QRect(1530, 830, 291, 101));
tab4_ReturnToMain->setStyleSheet(QString::fromUtf8("font: 30pt \"\345\256\213\344\275\223\";"));
resoult_info_label = new QPlainTextEdit(tab_4);
resoult_info_label->setObjectName(QString::fromUtf8("resoult_info_label"));
resoult_info_label->setGeometry(QRect(950, 60, 871, 681));
resoult_info_label->setStyleSheet(QString::fromUtf8("font: 10pt \"\345\276\256\350\275\257\351\233\205\351\273\221\";"));
choose_pa_2 = new QPushButton(tab_4);
choose_pa_2->setObjectName(QString::fromUtf8("choose_pa_2"));
choose_pa_2->setGeometry(QRect(10, 830, 161, 91));
choose_pa_2->setFont(font5);
choose_pa_2->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
label_13 = new QLabel(tab_4);
label_13->setObjectName(QString::fromUtf8("label_13"));
label_13->setGeometry(QRect(10, 750, 141, 21));
label_13->setFont(font5);
choose_to_2 = new QPushButton(tab_4);
choose_to_2->setObjectName(QString::fromUtf8("choose_to_2"));
choose_to_2->setGeometry(QRect(210, 830, 161, 91));
choose_to_2->setFont(font5);
choose_to_2->setStyleSheet(QString::fromUtf8("color: rgb(0, 0, 0);"));
tabWidget->addTab(tab_4, QString());
gridLayout_10->addWidget(tabWidget, 0, 0, 1, 1);
retranslateUi(Widget);
tabWidget->setCurrentIndex(2);
QMetaObject::connectSlotsByName(Widget);
} // setupUi
void retranslateUi(QWidget *Widget)
{
Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
label->setText(QCoreApplication::translate("Widget", "\346\236\234\350\224\254\345\210\206\351\200\211\347\240\224\345\217\221\345\233\242\351\230\237", nullptr));
label_23->setText(QString());
tabWidget->setTabText(tabWidget->indexOf(tab_3), QCoreApplication::translate("Widget", "Tab1", nullptr));
groupBox_9->setTitle(QString());
groupBox->setTitle(QString());
label_14->setText(QCoreApplication::translate("Widget", "\346\236\234\350\224\254\345\210\206\351\200\211\347\240\224\345\217\221\345\233\242\351\230\237", nullptr));
label_RGB->setText(QCoreApplication::translate("Widget", "\345\267\246\344\276\247\347\233\270\346\234\272", nullptr));
RGBstatus->setText(QCoreApplication::translate("Widget", "\346\234\252\350\277\236\346\216\245", nullptr));
label_RGB_3->setText(QCoreApplication::translate("Widget", "\345\217\263\344\276\247\347\233\270\346\234\272", nullptr));
RGBstatus_1->setText(QCoreApplication::translate("Widget", "\346\234\252\350\277\236\346\216\245", nullptr));
label_RGB_2->setText(QCoreApplication::translate("Widget", "\351\241\266\351\203\250\347\233\270\346\234\272", nullptr));
RGBstatus_2->setText(QCoreApplication::translate("Widget", "\346\234\252\350\277\236\346\216\245", nullptr));
label_9->setText(QCoreApplication::translate("Widget", "\345\205\211\350\260\261\347\233\270\346\234\272", nullptr));
spec_camera_status->setText(QCoreApplication::translate("Widget", "\346\234\252\350\277\236\346\216\245", nullptr));
label_11->setText(QCoreApplication::translate("Widget", "PLC", nullptr));
plc_status->setText(QCoreApplication::translate("Widget", "\346\234\252\350\277\236\346\216\245", nullptr));
label_17->setText(QCoreApplication::translate("Widget", " \346\234\272\346\242\260\346\211\213", nullptr));
jxs_status->setText(QCoreApplication::translate("Widget", "\346\234\252\350\277\236\346\216\245", nullptr));
label_26->setText(QCoreApplication::translate("Widget", "\345\275\223\345\211\215\345\210\206\351\200\211\346\236\234\350\224\254\357\274\232", nullptr));
passionBtn->setText(QCoreApplication::translate("Widget", "\347\231\276\351\246\231\346\236\234", nullptr));
tomatoBtn->setText(QCoreApplication::translate("Widget", "\347\225\252\350\214\204", nullptr));
btn_start->setText(QCoreApplication::translate("Widget", "\345\274\200\345\247\213\351\207\207\351\233\206", nullptr));
show_resoult->setText(QCoreApplication::translate("Widget", "\345\210\206\351\200\211\347\225\214\351\235\242", nullptr));
btn_setparam->setText(QCoreApplication::translate("Widget", "\350\256\276\347\275\256\345\217\202\346\225\260", nullptr));
btn_save->setText(QCoreApplication::translate("Widget", "\344\277\235\345\255\230\345\233\276\347\211\207", nullptr));
view_results->setText(QCoreApplication::translate("Widget", "\346\237\245\347\234\213\347\273\223\346\236\234", nullptr));
btn_quit->setText(QCoreApplication::translate("Widget", "\351\200\200\345\207\272\347\263\273\347\273\237", nullptr));
groupBox_4->setTitle(QCoreApplication::translate("Widget", "\347\233\270\346\234\272", nullptr));
groupBox_2->setTitle(QCoreApplication::translate("Widget", "\351\241\266\351\203\250\344\270\213", nullptr));
label_topL->setText(QString());
groupBox_3->setTitle(QCoreApplication::translate("Widget", "\351\241\266\351\203\250\344\270\255", nullptr));
label_topM->setText(QString());
groupBox_5->setTitle(QCoreApplication::translate("Widget", "\351\241\266\351\203\250\344\270\212", nullptr));
label_topR->setText(QString());
groupBox_6->setTitle(QCoreApplication::translate("Widget", "\345\267\246\344\276\247", nullptr));
showimg_right->setText(QString());
groupBox_7->setTitle(QCoreApplication::translate("Widget", "\345\217\263\344\276\247", nullptr));
showimg_left->setText(QString());
groupBox_8->setTitle(QCoreApplication::translate("Widget", "\345\205\211\350\260\261\347\233\270\346\234\272", nullptr));
spec_camera_show->setText(QString());
tabWidget->setTabText(tabWidget->indexOf(tab), QCoreApplication::translate("Widget", "Tab2", nullptr));
groupBox_11->setTitle(QCoreApplication::translate("Widget", "\345\267\246\344\276\247\347\233\270\346\234\272", nullptr));
label_28->setText(QCoreApplication::translate("Widget", "\346\233\235\345\205\211\346\227\266\351\227\264\357\274\232", nullptr));
label_29->setText(QCoreApplication::translate("Widget", "\347\231\275\345\271\263\350\241\241\357\274\232", nullptr));
label_30->setText(QCoreApplication::translate("Widget", "\345\242\236\347\233\212\357\274\232", nullptr));
groupBox_13->setTitle(QCoreApplication::translate("Widget", "\351\241\266\344\276\247\347\233\270\346\234\272", nullptr));
label_38->setText(QCoreApplication::translate("Widget", "\346\233\235\345\205\211\346\227\266\351\227\264\357\274\232", nullptr));
label_39->setText(QCoreApplication::translate("Widget", "\347\231\275\345\271\263\350\241\241\357\274\232", nullptr));
label_40->setText(QCoreApplication::translate("Widget", "\345\242\236\347\233\212\357\274\232", nullptr));
groupBox_15->setTitle(QCoreApplication::translate("Widget", "\345\217\263\344\276\247\347\233\270\346\234\272", nullptr));
label_46->setText(QCoreApplication::translate("Widget", "\346\233\235\345\205\211\346\227\266\351\227\264\357\274\232", nullptr));
label_47->setText(QCoreApplication::translate("Widget", "\347\231\275\345\271\263\350\241\241\357\274\232", nullptr));
label_48->setText(QCoreApplication::translate("Widget", "\345\242\236\347\233\212\357\274\232", nullptr));
btn_enterparam->setText(QCoreApplication::translate("Widget", "\350\256\276\347\275\256\347\233\270\346\234\272\345\217\202\346\225\260", nullptr));
tab3_ReturnToMain->setText(QCoreApplication::translate("Widget", " \350\277\224\345\233\236\344\270\273\347\225\214\351\235\242", nullptr));
choose_to->setText(QCoreApplication::translate("Widget", "\347\225\252\350\214\204", nullptr));
choose_pa->setText(QCoreApplication::translate("Widget", " \347\231\276\351\246\231\346\236\234", nullptr));
InputNum1->setText(QString());
InputNum5->setText(QString());
label_5->setText(QCoreApplication::translate("Widget", "\351\230\210\345\200\274\346\240\207\345\207\206\357\274\232", nullptr));
InputNum4->setText(QString());
set_threshoid->setText(QCoreApplication::translate("Widget", " \350\256\276\345\256\232\351\230\210\345\200\274", nullptr));
label_32->setText(QCoreApplication::translate("Widget", "\347\274\272\351\231\267\346\225\260\351\207\217", nullptr));
label_33->setText(QCoreApplication::translate("Widget", "\347\274\272\351\231\267\351\235\242\347\247\257", nullptr));
label_10->setText(QCoreApplication::translate("Widget", "\347\263\226\345\272\246", nullptr));
label_18->setText(QCoreApplication::translate("Widget", "\347\233\264\345\276\204", nullptr));
InputNum2->setText(QString());
label_12->setText(QCoreApplication::translate("Widget", "\350\257\267\351\200\211\346\213\251\346\236\234\350\224\254\357\274\232", nullptr));
label_21->setText(QCoreApplication::translate("Widget", "\351\242\234\350\211\262\345\215\240\346\257\224", nullptr));
tabWidget->setTabText(tabWidget->indexOf(tab_2), QCoreApplication::translate("Widget", "Tab3", nullptr));
show_resultsImg->setText(QString());
label_3->setText(QCoreApplication::translate("Widget", "\345\244\204\347\220\206\347\273\223\346\236\234\345\233\276\357\274\232", nullptr));
tab4_ReturnToMain->setText(QCoreApplication::translate("Widget", "\350\277\224\345\233\236\344\270\273\347\225\214\351\235\242", nullptr));
choose_pa_2->setText(QCoreApplication::translate("Widget", " \347\231\276\351\246\231\346\236\234", nullptr));
label_13->setText(QCoreApplication::translate("Widget", "\345\275\223\345\211\215\345\210\206\347\261\273\346\236\234\350\224\254\357\274\232", nullptr));
choose_to_2->setText(QCoreApplication::translate("Widget", "\347\225\252\350\214\204", nullptr));
tabWidget->setTabText(tabWidget->indexOf(tab_4), QCoreApplication::translate("Widget", "Tab4", nullptr));
} // retranslateUi
};
namespace Ui {
class Widget: public Ui_Widget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H

663
widget.cpp Normal file
View File

@ -0,0 +1,663 @@
#include "widget.h"
#include "ui_widget.h"
#include "camera.h"
#include <QDateTime>
#include "QSemaphore"
#include "vector"
#include <QTcpServer>
#include <QTcpSocket>
#define sys_file2 "./sys_daytime2.txt"
//外部引用
extern ProcessImg *processimg;
extern SendThread *sendthread;
extern RecvThread *recvthread;
extern CameraL *cameraL;
extern SpecCamera *speccamera;
extern QSemaphore emptybuff;
extern tomato tomato;
// c++的省略命名申明
using namespace std;
bool is_timeout;
int start_flag=1;
int save_flag=0;
//0为番茄 1为百香果
int fruit_flag;
int resoult_count = 1;
int i = 1;
//外部用用
extern int camStatusret;
extern int camStatusret1;
extern int camStatusret2;
extern int img_count;
extern int img_count1;
extern int img_count2;
//定义管道名称
extern HANDLE hPipe;
extern HANDLE specPipe;
extern HANDLE RPipe;
extern QString pipeName;
extern QString pipeName2;
extern QString rePipe;
int pipe_counter=1;
//这是 Widge 的构造函数 在你点击运行之后 会执行
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//直接导入本地时间(方法二)
QFile daytime_file(sys_file2);
QDateTime lastDateTime;
if (daytime_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString line;
QTextStream in(&daytime_file);
while (!in.atEnd()) {
line = in.readLine();
}
QStringList parts = line.split(" - ");
if (parts.size() > 0) {
lastDateTime = QDateTime::fromString(parts.first(), "yyyy-MM-dd HH:mm:ss");
}
daytime_file.close();
}
if (lastDateTime.isValid() && lastDateTime > QDateTime::currentDateTime()) {
QMessageBox::about(this, "错误", "时间校验不通过");
throw ExpiredException();
}
QDateTime expiryDate = QDateTime::fromString("2024-08-01 14:00:00", "yyyy-MM-dd HH:mm:ss");
QDateTime currentDate = QDateTime::currentDateTime();
if (currentDate >= expiryDate) {
throw ExpiredException();
}
//处理光谱数据的线程
processimg = new ProcessImg();
//发送给python端线程
sendthread = new SendThread();
//接收python端线程
recvthread = new RecvThread();
//new 代表给三个线程开辟空间
initparam();
/**连接相机*/
cameraL = new CameraL();
//初始化RGB相机
bool RGB_ret = cameraL->initCameraL();
if(camStatusret ==0 && RGB_ret)
{
ui->RGBstatus->setText("已连接");
ui->RGBstatus->setStyleSheet("QLabel{color:rgb(0,255,0);}");
}
if(camStatusret1 ==0 && RGB_ret)
{
ui->RGBstatus_1->setText("已连接");
ui->RGBstatus_1->setStyleSheet("QLabel{color:rgb(0,255,0);}");
}
if(camStatusret2 ==0 && RGB_ret)
{
ui->RGBstatus_2->setText("已连接");
ui->RGBstatus_2->setStyleSheet("QLabel{color:rgb(0,255,0);}");
}
//初始化光谱相机
speccamera = new SpecCamera();
bool spec_ret = speccamera->init_SpecCamera();
if(spec_ret == true)
{
ui->spec_camera_status->setText("已连接");
ui->spec_camera_status->setStyleSheet("QLabel{color:rgb(0,255,0);}");
}
//初始化称重模块
connectModbus_CZ();
//初始化机械手
connect_socket_Jxs();
//管道建立连接
connectPipe();
}
Widget::~Widget()
{
delete ui;
}
//初始话参数 并非实际相机的参数 而是 ui 界面上的 tab3 上的控件参数
void Widget::initparam()
{
camStatusret=-1;
camStatusret1=-1;
camStatusret2=-1;
ui->tabWidget->setCurrentIndex(2);
setMinimumSize(QSize(1200, 600));
setWindowState(Qt::WindowMaximized);
}
void Widget::connectModbus_CZ()
{
//称重模块
CZ = modbus_new_tcp("192.168.101.110",2000);
modbus_set_slave(CZ, 1); //设置modbus从机地址
int status = modbus_connect(CZ);
if(status == -1)
{
qDebug() << "modbus connect failed";
}
else
{
ui->plc_status->setStyleSheet("QLabel{color:rgb(0,255,0);}");
ui->plc_status->setText("已连接");
}
modbus_set_response_timeout(CZ, 0, 1000000);
}
void Widget::connect_socket_Jxs()
{
server = new QTcpServer();
server->listen(QHostAddress::Any, 21123);
bool is_timeout;
server->waitForNewConnection(5000, &is_timeout);
if(is_timeout == true)
{
qDebug() << "Jxs connect failed";
return;
}
socket = server->nextPendingConnection();
ui->jxs_status->setStyleSheet("QLabel{color:rgb(0,255,0);}");
ui->jxs_status->setText("已连接");
}
void Widget::connectPipe()
{
// 创建并连接到命名管道
hPipe = CreateFile(
(LPCWSTR)pipeName.utf16(), // 管道名称
GENERIC_WRITE, // 写入访问
0, // 不共享
NULL, // 默认安全属性
OPEN_EXISTING, // 打开现有管道
FILE_ATTRIBUTE_NORMAL, // 默认属性
NULL // 没有模板文件
);
// 创建并连接到命名管道(光谱相机发送)
specPipe = CreateFile(
(LPCWSTR)pipeName2.utf16(), // 管道名称
GENERIC_WRITE, // 写入访问
0, // 不共享
NULL, // 默认安全属性
OPEN_EXISTING, // 打开现有管道
FILE_ATTRIBUTE_NORMAL, // 默认属性
NULL // 没有模板文件
);
// 创建并连接到命名管道
RPipe = CreateFile(
(LPCWSTR)rePipe.utf16(), // 管道名称
GENERIC_READ, // 更改为读取访问
0, // 不共享
NULL, // 默认安全属性
OPEN_EXISTING, // 打开现有管道
FILE_ATTRIBUTE_NORMAL, // 默认属性
NULL // 没有模板文件
);
if (RPipe == INVALID_HANDLE_VALUE) {
qWarning() << "Failed to open pipe for reading:" << GetLastError();
return;
}
}
//点击 ui 界面的开始按钮
void Widget::on_btn_start_clicked()
{
//start_flag 为 1
if(start_flag)
{
QFile file(sys_file2);
if (file.open(QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss") << " - " << "\n";
file.close();
}
ui->btn_start->setStyleSheet("background-color: red;");
cameraL->open_camera2();
cameraL->set_acquisition_mode();
cameraL->start_capture();
speccamera->start_capture();
//开启线程 也就是执行一次对应线程的 run() 函数 在 thread 里面有 即执行三个线程的 while1 死循环 一直监听
processimg->start();
sendthread->start();
recvthread->start();
//连接信号跟槽函数
connect(cameraL,SIGNAL(send_Rgbimage(QImage&)),this,SLOT(showimg(QImage&)),Qt::BlockingQueuedConnection);
connect(cameraL,SIGNAL(send_Rgbimage1(QImage&)),this,SLOT(showimg1(QImage&)),Qt::BlockingQueuedConnection);
connect(cameraL,SIGNAL(send_Rgbimage2(QImage&,QImage&,QImage&)),this,SLOT(showimg2(QImage&,QImage&,QImage&)),Qt::BlockingQueuedConnection);
connect(speccamera,SIGNAL(show_SpecImg(QImage)),this,SLOT(show_specImgSlots(QImage)));
connect(recvthread,SIGNAL(recv_Data(quint16,quint8,quint16,quint8,quint16,quint32,quint16,quint16,QImage)),this,SLOT(receiveDataSlots(quint16,quint8,quint16,quint8,quint16,quint32,quint16,quint16,QImage)));
start_flag=0;
ui->tabWidget->setCurrentIndex(3);
// qDebug()<<"Start Success!";
}
//start_flag 为 0
else
{
QFile file(sys_file2);
if (file.open(QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
out << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss") << " - " << "\n";
file.close();
}
ui->btn_start->setStyleSheet("background-color: white;");
cameraL->stop_capture();
speccamera->stop_capture();
disconnect(cameraL,SIGNAL(send_Rgbimage(QImage&)),this,SLOT(showimg(QImage&)));
disconnect(cameraL,SIGNAL(send_Rgbimage1(QImage&)),this,SLOT(showimg1(QImage&)));
disconnect(cameraL,SIGNAL(send_Rgbimage2(QImage&,QImage&,QImage&)),this,SLOT(showimg2(QImage&,QImage&,QImage&)));
disconnect(speccamera,SIGNAL(show_SpecImg(QImage)),this,SLOT(show_specImgSlots(QImage)));
disconnect(recvthread,SIGNAL(recv_Data(quint16,quint8,quint16,quint8,quint16,quint32,quint16,quint16,QImage)),this,SLOT(receiveDataSlots(quint16,quint8,quint16,quint8,quint16,quint32,quint16,quint16,QImage)));
ui->showimg_left->clear();
ui->showimg_right->clear();
ui->label_topL->clear();
ui->label_topM->clear();
ui->label_topR->clear();
start_flag=1;
// qDebug() << "Stop Success!";
}
}
//右侧
void Widget::showimg(QImage& img)
{
pix = QPixmap::fromImage(img.scaled(ui->showimg_left->width(),ui->showimg_left->height(),Qt::KeepAspectRatio));
ui->showimg_left->setScaledContents(true);
ui->showimg_left->setPixmap(pix);
ui->showimg_left->show();
}
//左侧
void Widget::showimg1(QImage& img)
{
pix = QPixmap::fromImage(img.scaled(ui->showimg_right->width(),ui->showimg_right->height(),Qt::KeepAspectRatio));
ui->showimg_right->setScaledContents(true);
ui->showimg_right->setPixmap(pix);
ui->showimg_right->show();
}
//顶部
void Widget::showimg2(QImage& img,QImage& img1,QImage& img2)
{
pix = QPixmap::fromImage(img.scaled(ui->label_topL->width(),ui->label_topL->height(),Qt::KeepAspectRatio));
ui->label_topL->setScaledContents(true);
ui->label_topL->setPixmap(pix);
ui->label_topL->show();
pix1 = QPixmap::fromImage(img1.scaled(ui->label_topM->width(),ui->label_topM->height(),Qt::KeepAspectRatio));
ui->label_topM->setScaledContents(true);
ui->label_topM->setPixmap(pix1);
ui->label_topM->show();
pix2 = QPixmap::fromImage(img2.scaled(ui->label_topR->width(),ui->label_topR->height(),Qt::KeepAspectRatio));
ui->label_topR->setScaledContents(true);
ui->label_topR->setPixmap(pix2);
ui->label_topR->show();
}
void Widget::on_save_img_clicked()
{
save_flag=1;
}
//退出
void Widget::on_btn_quit_clicked()
{
qApp->quit();
}
//保存图片按钮
void Widget::on_btn_save_clicked()
{
if(!save_flag)
{
ui->btn_save->setStyleSheet("background-color: red;");
save_flag=1;
}
else
{
ui->btn_save->setStyleSheet("background-color: white;");
save_flag=0;
}
}
void Widget::on_btn_setparam_clicked()
{
ui->tabWidget->setCurrentIndex(2);
}
//导入参数 实际没用 因为相机参数会预先写死 并不会在运行途中更换
void Widget::on_btn_enterparam_clicked()
{
}
//接受线程收到数据 长径、短径等等 在 ui 界面显示
void Widget::receiveDataSlots(quint16 brix,quint8 greenPercentage,quint16 diameter,quint8 weight,quint16 defectNum,quint32 totalDefectArea,quint16 height,quint16 width,QImage img)
{
img = img.rgbSwapped();
pix = QPixmap::fromImage(img.scaled(ui->show_resultsImg->width(),ui->show_resultsImg->height(),Qt::KeepAspectRatio));
ui->show_resultsImg->setScaledContents(true);
ui->show_resultsImg->setPixmap(pix);
ui->show_resultsImg->show();
float brix_float = brix;
float totalDefectArea_float = totalDefectArea;
float greenPercentage_float = greenPercentage;
float diameter_float = diameter;
// ui->Tdangdu_line->setText(QString::number(brix_float/1000)); //
// ui->Zhijing_line->setText(QString::number(diameter_float/100)); //cm
// ui->Shuliang_line->setText(QString::number(defectNum));
// ui->Mianji_line->setText(QString::number(totalDefectArea_float/1000)); //cm^2
// ui->Zhanbi_line->setText(QString::number(greenPercentage_float/100)); //
//重量是g
//读取称重数据
uint16_t read = 0;
modbus_read_registers(CZ,0x40000,1,&read);
qDebug()<<"称重数据"<<read;
int level = 5;
//百香果
if(fruit_flag)
{
if(diameter = 0)
{
level = 4;
}
else
{
if(defectNum = 0)
{
level = 4;
}
else
{
if(totalDefectArea_float/1000 > 2)
{
level = 4;
}
else
{
if(read > 90)
{
level = 1;
}
else if(read < 65)
{
level = 3;
}
else
{
level = 2;
}
}
}
}
}
//番茄
else
{
if(diameter == 0)
{
level = 5;
}
else
{
if(defectNum != 0)
{
level = 5;
}
else
{
if(totalDefectArea_float/1000 >= Quexianmianji)
{
level = 5;
}
else
{
if(greenPercentage_float/100 >= Yansezhanbi)
{
if(diameter_float/100 >= Zhijing -2)
{
level = 3;
}
else
{
level = 4;
}
}
else
{
if(diameter_float/100 >= Zhijing)
{
level = 1;
}
else
{
level = 2;
}
}
}
}
}
}
// ui->resoult_line->setText(QString::number(level));
qDebug()<<"果子等级为:"<<level;
//窗口显示
if(resoult_count%25 == 0)
{
ui->resoult_info_label->clear();
}
QString resout_info;
QString fruit_resoult_flag;
//百香果
if(fruit_flag)
{
if(defectNum == 0)
{
fruit_resoult_flag = "";
}
else
{
fruit_resoult_flag = "";
}
resout_info = QString("当前百香果编号为:%1 糖度值为: %2 直径为: %3 %4皱缩 缺陷面积为: %5 最终识别结果为:%6 ").arg(resoult_count).arg(brix_float/1000).arg(diameter_float/100).arg(fruit_resoult_flag).arg(totalDefectArea_float/1000).arg(level);
ui->resoult_info_label->appendPlainText(resout_info);
}
//番茄
else
{
if(defectNum == 0)
{
fruit_resoult_flag = "";
}
else
{
fruit_resoult_flag = "";
}
resout_info = QString("当前番茄编号为:%1 直径为: %2 %3裂口 缺陷面积为: %4 绿色占比为: %5 最终识别结果为:%6 ").arg(resoult_count).arg(diameter_float/100).arg(fruit_resoult_flag).arg(totalDefectArea_float/1000).arg(greenPercentage_float/100).arg(level);
ui->resoult_info_label->appendPlainText(resout_info);
}
resoult_count++ ;
QFile file("./image/output.txt");
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
QTextStream out(&file);
out << resout_info << "\n";
file.close();
}
i++;
// 给机械手发送信息
QString txt = QString("[X:0;Y:0;A:0;ATTR:%1;ID:%2]").arg(level).arg(fruit_flag);
QByteArray byteArray = txt.toUtf8();
socket->write(byteArray);
// qDebug()<<"发送机械手信息等级为:"<<level;
//给plc发送信息
uint16_t write[2];
write[0] = resoult_count;
write[1] = level;
modbus_write_registers(CZ,0x40001,2,write);
// qDebug()<<"发送plc信息等级为:"<<level;
//称重数据在 read 里面
}
//显示光谱伪彩色图 实际效果很差 就取消了
//展示一条折线
void Widget::show_specImgSlots(QImage img)
{
// qDebug()<<"2";
QTransform transform;
transform.rotate(90.0);
img = img.transformed(transform, Qt::FastTransformation);
pix = QPixmap::fromImage(img.scaled(ui->spec_camera_show->width(),ui->spec_camera_show->height(),Qt::KeepAspectRatio));
ui->spec_camera_show->setScaledContents(true);
ui->spec_camera_show->setPixmap(pix);
ui->spec_camera_show->show();
}
void Widget::on_view_results_clicked()
{
QString resoult_path = "C:/Users/succtech/Desktop/20240425/image";
// 打开文件选择对话框,并设置默认路径
QString filePath = QFileDialog::getOpenFileName(this, "查看结果", resoult_path);
}
void Widget::on_tab4_ReturnToMain_clicked()
{
ui->tabWidget->setCurrentIndex(1);
}
void Widget::on_choose_pa_clicked()
{
fruit_flag = 1;
//tab2
ui->passionBtn->setStyleSheet("background-color: red;");
ui->tomatoBtn->setStyleSheet("background-color: white;");
//tab3
ui->choose_pa->setStyleSheet("background-color: red;");
ui->choose_to->setStyleSheet("background-color: white;");
//tab4
ui->choose_pa_2->setStyleSheet("background-color: red;");
ui->choose_to_2->setStyleSheet("background-color: white;");
ui->tabWidget->setCurrentIndex(1);
}
void Widget::on_choose_to_clicked()
{
fruit_flag = 0;
//tab2
ui->tomatoBtn->setStyleSheet("background-color: red;");
ui->passionBtn->setStyleSheet("background-color: white;");
//tab3
ui->choose_to->setStyleSheet("background-color: red;");
ui->choose_pa->setStyleSheet("background-color: white;");
//tab4
ui->choose_to_2->setStyleSheet("background-color: red;");
ui->choose_pa_2->setStyleSheet("background-color: white;");
ui->tabWidget->setCurrentIndex(1);
}
void Widget::on_tab3_ReturnToMain_clicked()
{
ui->tabWidget->setCurrentIndex(1);
}
void Widget::on_show_resoult_clicked()
{
ui->tabWidget->setCurrentIndex(3);
}
void Widget::on_set_threshoid_clicked()
{
Tangdu = ui->InputNum1->text().toInt();
Zhijing = ui->InputNum2->text().toInt();
Yansezhanbi = ui->InputNum3->text().toInt();
Quexianshuliang = ui->InputNum4->text().toInt();
Quexianmianji = ui->InputNum5->text().toInt();
}

114
widget.h Normal file
View File

@ -0,0 +1,114 @@
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "QMessageBox"
#include "thread.h"
#include <unistd.h>
#include "QMessageBox"
#include "src/modbus.h"
#include <QImage>
#define SAVE_IMAGE_PATH "F:/tomato/tomato1227/image/left"
QT_BEGIN_NAMESPACE
//ui界面的类 窗口的各种控件按钮、文字输入行、label等
namespace Ui { class Widget; }
QT_END_NAMESPACE
class ExpiredException : public std::exception {
public:
const char* what() const throw() {
return "程序试用期已过,请联系供应商!";
}
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
int Tangdu;
int Zhijing = 7;
float Yansezhanbi = 0.3;
int Quexianshuliang = 0;
float Quexianmianji = 0.5;
void initparam();
void connectModbus_CZ();
void connect_socket_Jxs();
void connectPipe();
QTcpServer *server;
QTcpSocket *socket;
modbus_t* CZ;
private slots:
void on_btn_start_clicked();
void showimg1(QImage&);
void showimg(QImage&);
void showimg2(QImage&,QImage&,QImage&);
void on_save_img_clicked();
void on_btn_quit_clicked();
void on_btn_save_clicked();
void on_btn_setparam_clicked();
void on_btn_enterparam_clicked();
void receiveDataSlots(quint16,quint8,quint16,quint8,quint16,quint32,quint16,quint16,QImage);
void show_specImgSlots(QImage);
void on_view_results_clicked();
void on_tab4_ReturnToMain_clicked();
void on_choose_pa_clicked();
void on_choose_to_clicked();
void on_tab3_ReturnToMain_clicked();
void on_show_resoult_clicked();
void on_set_threshoid_clicked();
private:
Ui::Widget *ui;
//画板 负责接收QImage并显示 不能直接显示QImage变量
QPixmap pix;
QPixmap pix1;
QPixmap pix2;
int m_index;
//存图标志位
int save_img=0;
//tcp变量
QTcpServer* server_to_lowermachine = nullptr;
QTcpSocket* lower_machine = nullptr;
volatile bool is_running = false;
};
#endif // WIDGET_H

1491
widget.ui Normal file

File diff suppressed because it is too large Load Diff