相机模块待完善

This commit is contained in:
2024-05-21 18:11:13 +08:00
parent b39487a39f
commit cb681bd97a
29 changed files with 260 additions and 330 deletions
@@ -546,7 +546,7 @@ void algoThread::run()
Camera1_Algo(data);
break;
}
oriSaveThread->saveOriImg(data);//保存原图
//oriSaveThread->saveOriImg(data);//保存原图
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
@@ -556,7 +556,7 @@ void algoThread::run()
// data->SNcode = snDatas->waitForSn(data->timeCode);
if (data->resultImgs.size() > 0)
{
resSaveThread->saveResImg(data);//保存结果图
//resSaveThread->saveResImg(data);//保存结果图
bridge.front.showImg("Algorithm", data->resultImgs[0], threadId);//显示原图
//cout << "algoEnd" << data->algoThreadIndex;
}
@@ -715,10 +715,7 @@ public:
handleAiModel* modelPtr;
sqlController* sql;
threadConroller* tController;
imgSaveThread* oriSaveThread;
imgSaveThread* resSaveThread;
sncodeCls* snDatas;
cameraImages* cameraImgs;
QMutex detQueueMutex;
QQueue<detData*> detQueue;
@@ -736,10 +733,7 @@ public:
tController = threadConroller;
modelPtr = &threadConroller->models;
sql = threadConroller->sql;
oriSaveThread = threadConroller->oriSaveThread;
resSaveThread = threadConroller->resSaveThread;
snDatas = &threadConroller->snDatas;
cameraImgs = &threadConroller->cameraImgs;
}
bool addDetTask(detData*);//添加任务
@@ -8,6 +8,7 @@ backEndController::backEndController()
_jsonConfiguration = new jsonConfiguration();
_triggerController = new triggerController();
_cameraController = new cameraController();
_threadConroller = new threadConroller();
std::vector<std::future<void>> futures;//Òì²½³õʼ»¯
futures.push_back(std::async(std::launch::async, &triggerController::init, _triggerController, this));
futures.push_back(std::async(std::launch::async, &cameraController::init, _cameraController, this));
@@ -5,14 +5,17 @@
class triggerController;
class cameraController;
class jsonConfiguration;
class threadConroller;
class backEndController :public QObject
{
Q_OBJECT
public:
bool init = false;
jsonConfiguration* _jsonConfiguration;
triggerController* _triggerController;//´¥·¢Æ÷
cameraController* _cameraController;
jsonConfiguration* _jsonConfiguration;
threadConroller* _threadConroller;
backEndController();
~backEndController();
signals:
+28 -1
View File
@@ -1,9 +1,10 @@
#include<base.h>
#include <filesystem>
typedef Base* (*CreateFunction)();
typedef void (*ReleaseFunction)(Base* animal);
struct correctionStructx
struct correctionStruct
{
std::vector<double> in_X_X0Y;
std::vector<double> in_Y_X0Y;
@@ -29,4 +30,30 @@ typedef struct {
ReleaseFunction destory_ptr; // 释放
int showIndex;
}cameraModule;
static void createDirectoryIfNotExists(const std::string& path) {
// 检查路径是否存在
if (!std::filesystem::exists(path)) {
// 创建路径
std::filesystem::create_directories(path);
//std::cout << "已创建文件夹: " << path << std::endl;
}
else {/*
std::cout << "文件夹已存在: " << path << std::endl;*/
}
}
static std::string getCurrentDateTime() {
// 获取当前时间点
auto now = std::chrono::system_clock::now();
// 转换为字符串
auto currentTime = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 100;
std::tm* localTime = std::localtime(&currentTime);
std::stringstream ss;
ss << std::put_time(localTime, "%H-%M-%S") << "-" << std::setfill('0') << std::setw(2) << ms.count();
return ss.str();
}
#pragma once
@@ -44,14 +44,13 @@ void camera::run()
while (runFlag)
{
getCameraImg(camera_mat);
if (camera_mat.size() > 0)
while(camera_mat.size()>0)
{
int size = imgQueue.size();
for (auto img : camera_mat)
{
imgQueue.enqueue(img);
//emit _backEndController->sendPixmapImageWidgetByIndex(cvMatToQPixMap(img), imgWidgetOri, index);
if (lastUpdateTime.elapsed() > 200)
if (lastUpdateTime.elapsed() > 200)//显示在界面
{
lastUpdateTime.start(); // 记录本次更新时间
QPixmap pixmap;
@@ -62,13 +61,37 @@ void camera::run()
pack->index = index;
emit S1(pack);
}
else
{
showLost++;
qDebug() << cameraModulePtr.moduleName << "Lost show";
}
camera_mat.clear();
}
//防止过大
//while (imgQueue.size() > 0)
//{
// imgQueue.dequeue();
//}
while (imgQueue.size() > 5)
{
imgQueue.dequeue();
imgLost++;
qDebug() << cameraModulePtr.moduleName << "Lost img";
}
getCameraImg(camera_mat);
}
if (data != nullptr)
{
data->mutex->lock();
int imgCnt = data->imgAmountMin;
getCameraImg(camera_mat);
while (camera_mat.size() > 0)
{
for (auto img : camera_mat)
{
}
}
data->mutex->unlock();
}
QThread::msleep(100);
}
@@ -11,6 +11,8 @@
#include <qdebug.h>
#include <globalStruct.h>
#include <qdatetime.h>
#include <back/threadPoolController.h>
//硬触发要求在触发相机前给出触发信号
class backEndController;
class camera : public QThread
{
@@ -25,12 +27,14 @@ public:
QThread::msleep(2000);
}
void run();
void getCameraImg(std::vector<cv::Mat>& mats)
void getImgQueueSize();
void Process(detData* dataIn)
{
QByteArray task_name;
if (cameraModulePtr.module_ptr->sendData(mats, task_name))
qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera_mat size:" << mats.size();
data = dataIn;
}
//相机操作
bool trigger()
{
if (!cameraModulePtr.module_ptr->run())
@@ -40,15 +44,46 @@ public:
}
return true;
}
void getImgQueueSize();
void getCameraImg(std::vector<cv::Mat>& mats)
{
QByteArray task_name;
if (cameraModulePtr.module_ptr->sendData(mats, task_name))
qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera_mat size:" << mats.size();
}
void setDigitalShift(float value)
{
sendData("DigitalShift_" + std::to_string(value));
}
void setExposureTime(float value)
{
sendData("exposure_" + std::to_string(value));
}
void setUserSet(int value)
{
sendData("userset_" + std::to_string(value));
}
signals:
void S1(showImgPack*);
private:
detData* data = nullptr;
void sendData(std::string str)
{
QByteArray task_name(QByteArray::fromStdString(str));
std::vector<cv::Mat> camera_mat;
if (!cameraModulePtr.module_ptr->sendData(camera_mat, task_name))
qCritical() << "[Error] " << "set exposure error!";
}
int index = 0;
int timeout = 5000;
cameraModule cameraModulePtr;
QQueue<cv::Mat>imgQueue;
backEndController* _backEndController;
bool runFlag = true;
int imgLost = 0;//未取用从而丢失的图片数量
int showLost = 0;//因为窗口刷新速率低而未显示的数量
};
class cameraController : public QThread
{
@@ -59,14 +94,14 @@ public:
std::unordered_map<QString, camera*>cameras;//key 相机名
void run()
{
while (1)
{
QThread::msleep(300);
for (auto camera : cameras)
{
camera.second->trigger();
}
}
//while (1)
//{
// //QThread::msleep(300);
// //for (auto camera : cameras)
// //{
// // camera.second->trigger();
// //}
//}
}
private:
bool loadModule(QString keyName, QString moduleName, QByteArray initByte);
@@ -77,6 +112,15 @@ private:
QList<QLibrary*> lib_list;
public slots:
void R1(showImgPack*);
void cameraTrigger(QString Name)
{
auto camera=cameras.find(Name);
if (camera != cameras.end())
{
camera->second->trigger();
}
}
};
#endif
@@ -0,0 +1,12 @@
#include <backEndController.h>
#include <imgSaveThreadController.h>
#include <jsonParsing.h>
imgSaveThreadController::imgSaveThreadController(backEndController* backEndControllerIn)
{
_backEndController = backEndControllerIn;
QString str = _backEndController->_jsonConfiguration->imgSavePath;
oriSaveThread = new imgSaveThread(oriImgSaveThread,str);
resSaveThread = new imgSaveThread(resImgSaveThread,str);
}
@@ -0,0 +1,115 @@
#include <qthread.h>
#include <opencv.hpp>
#include <qqueue.h>
#include <sqliteController/sqlControler.h>
#include <qdatetime.h>
#include <backStruct.h>
class backEndController;
enum imgSaveThreadType
{
oriImgSaveThread,
resImgSaveThread
};
class imgSaveThread :public QThread
{
public:
struct imgSaveStruct {
std::string cameraKeyName;
std::string SNcode;
std::string customName;
std::string format;//留空则按原图bmp,结果图jpg
int showIndex = -1;
unsigned long long timeCode;
std::vector<cv::Mat>imgs;
};
~imgSaveThread()
{
runFlag = false;
while (this->isRunning() == true);
qDebug() << "imgSaveThread Ended";
}
imgSaveThread(imgSaveThreadType typeIn,QString pathIn)
{
type = typeIn;
switch (type)
{
case oriImgSaveThread:
path = (pathIn).toStdString() + "//yuantu//";
break;
case resImgSaveThread:
path = (pathIn).toStdString() + "//result//";
break;
}
}
void run()
{
while (runFlag)
{
while (imgQueue.size()>0)
saveImg(imgQueue.dequeue());
QThread::msleep(100);
}
}
void saveImg(imgSaveStruct& data)
{
try
{
//根据时间获取大概路径
sqlTableDatas sqlRes = sql->selectSingleData("saveHour", data.SNcode);//查找之前的数据
std::string fileSavePath = path;
if (sqlRes.rowDatas.size() > 0 && sqlRes.rowDatas[0][0].value != "")//数据存在
{
fileSavePath += sqlRes.tableName + "//" + sqlRes.rowDatas[0][0].value + "H//";
}
else//数据不存在 新建数据
{
QDateTime currentDateTime = QDateTime::currentDateTime();
std::string ymd = currentDateTime.toString("yyyy-MM-dd").toStdString();
int h = currentDateTime.time().hour();
fileSavePath += ymd + "//" + std::to_string(h) + "H//";
sql->addData(data.SNcode, { { "saveHour",std::to_string(h) } });
}
//有SN码就储存在SN码路径,无则储存在相机路径
if ((data.SNcode).length() != 0)
fileSavePath += data.SNcode + "//";
else
fileSavePath += data.cameraKeyName + "//";
createDirectoryIfNotExists(fileSavePath);//文件夹处理
std::string fileName;
{
for (int i = 0; i < data.imgs.size(); i++)
{
fileName = data.cameraKeyName + "_" + getCurrentDateTime() + "_Count" + std::to_string(i) + ".jpg";
cv::imwrite(fileSavePath + fileName, data.imgs[i]);
qInfo() << QString::fromStdString(data.cameraKeyName + " ori:" + fileSavePath + fileName);
}
}
}
catch (...)
{
return;
}
}
private:
QQueue<imgSaveStruct> imgQueue;
std::string path = "";
std::string basicFormat = "";
imgSaveThreadType type;
sqlController* sql;
bool runFlag = true;
};
class imgSaveThreadController
{
public:
imgSaveThreadController(backEndController* backEndControllerIn);
private:
backEndController* _backEndController;
imgSaveThread* oriSaveThread;
imgSaveThread* resSaveThread;
};
#pragma once
@@ -33,37 +33,6 @@ std::string getCurrentDateTime_2() {
}
int threadAmount = 0;
int maxThread = 16;
/// <summary>
/// 获取当前时间点,以"小时-分钟-秒"返回
/// </summary>
/// <returns></returns>
std::string getCurrentDateTime() {
// 获取当前时间点
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
// 转换为字符串
std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
std::tm* localTime = std::localtime(&currentTime);
std::stringstream ss;
ss << std::put_time(localTime, "%H-%M-%S");
return ss.str();
}
/// <summary>
/// 检查目标文件夹是否存在,不存在则创建
/// </summary>
/// <param name="path"></param>
void createDirectoryIfNotExists(const std::string& path) {
// 检查路径是否存在
if (!std::filesystem::exists(path)) {
// 创建路径
std::filesystem::create_directories(path);
//std::cout << "已创建文件夹: " << path << std::endl;
}
else {/*
std::cout << "文件夹已存在: " << path << std::endl;*/
}
}
////////////THREAD//CONTROLLER////////////////////////
threadConroller::threadConroller(QObject* parent) : QThread(parent)
@@ -72,10 +41,10 @@ threadConroller::threadConroller(QObject* parent) : QThread(parent)
sql = new sqlController;
sql->initPara();
//辅助线程初始化
oriSaveThread->oriThread = true;
oriSaveThread->Init(this);
//oriSaveThread->oriThread = true;
//oriSaveThread->Init(this);
resSaveThread->Init(this);
//resSaveThread->Init(this);
threadAmount = bridge.back.json.algoThreads.algoDataMap.size();
for (int i = 0; i < threadAmount; i++)
{
@@ -215,135 +184,6 @@ threadConroller::~threadConroller()
delete thread;
}
}
////////////////SUBTHREAD////////////////////////
bool folderExists(const std::string& folderPath) {
return std::filesystem::is_directory(folderPath);
}
void imgSaveThread::Init(threadConroller* tC)
{
sql = tC->sql;
snDatas = &tC->snDatas;
}
void imgSaveThread::saveOriginalImg(imgSaveStruct& data)
{
//根据时间获取大概路径
sqlTableDatas sqlRes = sql->selectSingleData("saveHour", data.SNcode);//查找之前的数据
std::string fileSavePath = bridge.back.json.imgSavePath.toStdString();
if (sqlRes.rowDatas.size() > 0 && sqlRes.rowDatas[0][0].value != "")//数据存在
{
fileSavePath += "//yuantu//" + sqlRes.tableName + "//" + sqlRes.rowDatas[0][0].value + "H//";
}
else//数据不存在 新建数据
{
QDateTime currentDateTime = QDateTime::currentDateTime();
std::string ymd = currentDateTime.toString("yyyy-MM-dd").toStdString();
int h = currentDateTime.time().hour();
fileSavePath += "//yuantu//" + ymd + "//" + to_string(h) + "H//";
sql->addData(data.SNcode, { { "saveHour",to_string(h) } });
}
if ((data.SNcode).length() != 0)
fileSavePath += data.SNcode + "//";
else
fileSavePath += "camera_" + data.cameraKeyName + "//";
createDirectoryIfNotExists_2(fileSavePath);//文件夹处理
std::string fileName;
//while (folderExists(resPath) != true);
////////////////
if (data.cameraKeyName.find("3D") != -1 && data.imgs.size() == 2)
{
fileName = data.cameraKeyName + "-" + "_time" + getCurrentDateTime_2();
cv::imwrite(fileSavePath + fileName + ".bmp", data.imgs[0]);
cv::imwrite(fileSavePath + fileName + ".tiff", data.imgs[1]);
}
else
{
std::string fileName;
data.cameraKeyName;
for (int i = 0; i < data.imgs.size(); i++)
{
fileName = data.cameraKeyName + "-" + to_string(i) + "_time" + getCurrentDateTime_2() + ".bmp";
cv::imwrite(fileSavePath + fileName, data.imgs[i]);
qDebug() << "[Debug]" << QString::fromStdString(data.cameraKeyName + " res:" + fileSavePath + fileName);
//cout << resPath << fileName << endl;
}
}
return;
}
void imgSaveThread::saveResultImg(imgSaveStruct& data)
{
try
{
//根据时间获取大概路径
sqlTableDatas sqlRes = sql->selectSingleData("saveHour", data.SNcode);//查找之前的数据
std::string fileSavePath = bridge.back.json.imgSavePath.toStdString();
if (sqlRes.rowDatas.size() > 0 && sqlRes.rowDatas[0][0].value != "")//数据存在
{
fileSavePath += "//result//" + sqlRes.tableName + "//" + sqlRes.rowDatas[0][0].value + "H//";
}
else//数据不存在 新建数据
{
QDateTime currentDateTime = QDateTime::currentDateTime();
std::string ymd = currentDateTime.toString("yyyy-MM-dd").toStdString();
int h = currentDateTime.time().hour();
fileSavePath += "//result//" + ymd + "//" + to_string(h) + "H//";
sql->addData(data.SNcode, { { "saveHour",to_string(h) } });
}
//有SN码就储存在SN码路径,无则储存在相机路径
if ((data.SNcode).length() != 0)
fileSavePath += data.SNcode + "//";
else
fileSavePath += "camera_" + data.cameraKeyName + "//";
createDirectoryIfNotExists_2(fileSavePath);//文件夹处理
std::string fileName;
//while (folderExists(resPath) != true);
//else
{
for (int i = 0; i < data.imgs.size(); i++)
{
fileName = data.cameraKeyName + "_" + getCurrentDateTime_2() + "_time" + to_string(i) + ".jpg";
cv::imwrite(fileSavePath + fileName, data.imgs[i]);
qDebug() << "[Debug]" << QString::fromStdString(data.cameraKeyName + " ori:" + fileSavePath + fileName);
}
}
}
catch (...)
{
return;
}
}
void imgSaveThread::run()
{
if (oriThread == false)
while (m_runFlag == true)
{
while (resQueue.size() > 0)
{
resMutex.lock();
imgSaveStruct data = resQueue.dequeue();
resMutex.unlock();
saveResultImg(data);
}
QThread::msleep(10);
}
else
while (m_runFlag == true)
{
while (oriQueue.size() > 0)
{
oriMutex.lock();
imgSaveStruct data = oriQueue.dequeue();
oriMutex.unlock();
saveOriginalImg(data);
}
QThread::msleep(10);
}
}
////////////////DET//DATA//////////////////
sncodeCls::sncodeCls()
{
@@ -14,6 +14,8 @@
#include <fileManager.h>
#include <qtcpserver.h>
#include <qtcpsocket.h>
#include <mutex>
#include <backStruct.h>
class threadConroller;
class algoThread;
class subThread;
@@ -29,21 +31,7 @@ struct MatchResult {
cv::Rect boundingRect;
double confidence;
};
struct correctionStruct
{
std::vector<double> in_X_X0Y;
std::vector<double> in_Y_X0Y;
std::vector<double> in_Z_X0Y;
std::vector<double> in_Z_X0Y_Value;
std::vector<double> in_X_X0Z;
std::vector<double> in_Y_X0Z;;
std::vector<double> in_Z_X0Z;
std::vector<double> in_Y_X0Z_Value;
std::vector<double> in_X_Y0Z;
std::vector<double> in_Y_Y0Z;
std::vector<double> in_Z_Y0Z;
std::vector<double> in_X_Y0Z_Value;
};
class cameraImages
{
public:
@@ -97,7 +85,7 @@ class detData
{
public:
const unsigned long long timeCode = getTimeCode();//根据时间和随机数生成的值,唯一
QMutex* mutex;
std::string SNcode;
QString cameraKeyName;
@@ -159,6 +147,7 @@ public:
algoThreadName = IN_algoThreadName;
oriShowIndex = IN_oriShowIndex;
SNcode = IN_SN;
mutex = new QMutex();
}
~detData()
{
@@ -166,6 +155,7 @@ public:
img.release();
for (auto img : resultImgs)
img.release();
delete mutex;
//delete cameraImgPtr;
}
@@ -193,7 +183,7 @@ class detDataController
public:
std::unordered_map<unsigned long long, detData*>map;
//std::map<unsigned long long, detData*>tstMap;
QLinkedList< detData> detDatas;
QLinkedList<detData> detDatas;
QMutex dataMutex;
detData* insertDetData(QString IN_cameraKeyName, QString IN_algoThreadName,
std::string IN_SN, int IN_oriShowIndex)
@@ -269,78 +259,6 @@ public:
sncodeCls();
};
/// <summary>
/// 辅助线程
/// </summary>
class imgSaveThread : public QThread
{
Q_OBJECT
public:
bool oriThread = false;
std::string snErrorsnError2= std::string ("snErrNoSn");
struct imgSaveStruct{
std::string cameraKeyName;
std::string SNcode;
int showIndex = -1;
unsigned long long timeCode;
std::vector<cv::Mat>imgs;
};
QMutex mutex;
bool m_runFlag = true;
std::vector<std::pair<std::string, size_t>>snWithHash;
sncodeCls* snDatas;
sqlController* sql;
std::string fileNameTemp;
QMutex oriMutex;
QQueue<imgSaveStruct>oriQueue;
QMutex resMutex;
QQueue<imgSaveStruct>resQueue;
QString dateTimeNow;
void saveOriImg(detData* data)
{
imgSaveStruct saveStruct;
saveStruct.SNcode = data->SNcode;
//saveStruct.snCountIndex = data->snCountIndex;
saveStruct.cameraKeyName = data->cameraKeyName.toStdString();
saveStruct.timeCode = data->timeCode;
saveStruct.showIndex = data->oriShowIndex;
for (auto mat : data->imgs)
saveStruct.imgs.push_back(mat);
oriMutex.lock();
oriQueue.push_back(saveStruct);
oriMutex.unlock();
this->start();
}
void saveResImg(detData* data)
{
imgSaveStruct saveStruct;
saveStruct.SNcode = data->SNcode;
//saveStruct.snCountIndex = data->snCountIndex;
saveStruct.cameraKeyName = data->cameraKeyName.toStdString();
saveStruct.timeCode = data->timeCode;
saveStruct.showIndex = data->resShowIndex;
for (auto mat : data->resultImgs)
saveStruct.imgs.push_back(mat);
resMutex.lock();
resQueue.push_back(saveStruct);
resMutex.unlock();
this->start();
}
void Init(threadConroller* tC);
void run();
private:
void saveOriginalImg(imgSaveStruct&);
void saveResultImg(imgSaveStruct&);
};
/// <summary>
/// 线程池管理
/// </summary>
@@ -360,8 +278,6 @@ public:
QMutex insertQueueMutex;
QQueue<detData*>insertQueue;
std::vector<algoThread*>algoThreadList;
imgSaveThread* oriSaveThread = new imgSaveThread;//原图保存线程
imgSaveThread* resSaveThread = new imgSaveThread;//结果图保存线程
void run();
void addTask(detData*);
bool getFreeThread();
+1 -1
View File
@@ -92,7 +92,7 @@ bool QSetFileTimes(const QString & filepath, QDateTime& CreationTime)
// int i=GetFileTime (hFile, &lpCreationTime, &lpLastAccessTime, &lpLastWriteTime); // 获取文件夹时间属性信息
// SYSTEMTIME stc;
// FILETIME localTime;
// FileTimeToLocalFileTime(&lpCreationTime,&localTime);
// FileTimeToLocalFileTime(&lpCreatxionTime,&localTime);
// FileTimeToSystemTime(&localTime,&stc);//UTC转本地时间
SYSTEMTIME newDay;
newDay.wDayOfWeek=CreationTime.date().dayOfWeek();
-45
View File
@@ -1,45 +0,0 @@
#include "mainwindow.h"
#include <QApplication>
#include <QSystemSemaphore>
#include <QSharedMemory>
#include <QMessageLogger>//日志
#include <QDebug>
#include <qmessagebox.h>
#include <qpushbutton.h>
#include <qlineedit.h>
#include <qlabel.h>
#include <qcombobox.h>
#include <backEndController.h>
#include <QMetaType>
#include <globalStruct.h>
void connectSignalAndSlots(MainWindow* front, backEndController* back)
{
QObject::connect(back, &backEndController::sendPixmapImageWidget, front, &MainWindow::recvPixmapImageWidget);
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
//检测是否已运行
QSystemSemaphore sema("monkey", 1, QSystemSemaphore::Open);
sema.acquire();
QSharedMemory mem("DetUi");
if (mem.create(1) == false)
{
sema.release();
//程序直接退出
exit(0);
}
sema.release();
//////////
qRegisterMetaType<imgWidgetShowType>("imgWidgetShowType");
qRegisterMetaType<showImgPack>("showImgPack");
backEndController _backEnd;
MainWindow w;
connectSignalAndSlots(&w,&_backEnd);
w.show();
return a.exec();
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.