添加按钮与指示灯相关串口通信

This commit is contained in:
zc 2024-05-07 00:06:36 -07:00
parent b6215897df
commit 77cbb4ad3d
24 changed files with 982 additions and 391 deletions

View File

@ -5,6 +5,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <QProcess> #include <QProcess>
#include <QTime> #include <QTime>
#include <chrono>
LinkObject* Channel::audioInput = nullptr; LinkObject* Channel::audioInput = nullptr;
LinkObject* Channel::audioOutput = nullptr; LinkObject* Channel::audioOutput = nullptr;
@ -16,10 +17,6 @@ extern LinkObject* vo1;
Channel::Channel(QObject* parent) Channel::Channel(QObject* parent)
: QObject(parent) : QObject(parent)
{ {
timer = new QTimer();
timer->setInterval(duration);
timer->stop();
videoInput = nullptr; videoInput = nullptr;
videoOutput = nullptr; videoOutput = nullptr;
videoEncoder = nullptr; videoEncoder = nullptr;
@ -48,8 +45,6 @@ Channel::Channel(QObject* parent)
rtspServer = Link::create("Rtsp"); rtspServer = Link::create("Rtsp");
rtspServer->start(); rtspServer->start();
} }
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
} }
/** /**
@ -88,8 +83,9 @@ void Channel::init()
record = Link::create("Mux"); record = Link::create("Mux");
QVariantMap dataMp4; QVariantMap dataMp4;
dataMp4["format"] = "mp4"; dataMp4["format"] = "mp4";
// dataMp4["lowLatency"] = true; dataMp4["segmentDuration"] = duration;
// dataMp4["filecache"] = 20480000; dataMp4["lowLatency"] = true;
dataMp4["filecache"] = 20480000;
record->setData(dataMp4); record->setData(dataMp4);
videoInput->linkV(videoEncoder)->linkV(record); videoInput->linkV(videoEncoder)->linkV(record);
audioInput->linkA(audioEncoder)->linkA(record); audioInput->linkA(audioEncoder)->linkA(record);
@ -127,40 +123,20 @@ void Channel::init()
file->linkV(videoDecoder)->linkV(videoOutput); file->linkV(videoDecoder)->linkV(videoOutput);
} }
/**
* @brief
*/
void Channel::onTimeout()
{
while (int secs = QTime::currentTime().second() % 60 != 0) {
}
QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime);
// 重新分片
record->invoke("segment", path);
}
/** /**
* @brief * @brief
*/ */
void Channel::startRecord() void Channel::startRecord()
{ {
if (!isMountDisk()) {
Log::error("there are no disk mounted");
return;
}
while (int secs = QTime::currentTime().second() % 60 != 0) { while (int secs = QTime::currentTime().second() % 60 != 0) {
} }
QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm"); QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
QVariantMap dataRecord; QVariantMap dataRecord;
QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime); QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime + "_%02d");
dataRecord["path"] = path; dataRecord["path"] = path;
record->start(dataRecord); record->start(dataRecord);
isRecord = true; isRecord = true;
Log::info("{} start recording...", channelName.toStdString()); Log::info("{} start recording...", channelName.toStdString());
timer->start();
emit showRecordState(true); emit showRecordState(true);
} }
@ -171,7 +147,6 @@ void Channel::stopRecord()
{ {
Log::info("{} stop recording...", channelName.toStdString()); Log::info("{} stop recording...", channelName.toStdString());
record->stop(true); record->stop(true);
timer->stop();
emit showRecordState(false); emit showRecordState(false);
} }
@ -245,7 +220,6 @@ void Channel::startPlayLive()
void Channel::back() void Channel::back()
{ {
Log::info("{} back 10s", channelName.toStdString()); Log::info("{} back 10s", channelName.toStdString());
qDebug() << channelName << "back 10s";
int curPos = file->invoke("getPosition").toInt(); int curPos = file->invoke("getPosition").toInt();
curPos -= 10 * 1000; curPos -= 10 * 1000;
file->invoke("seek", curPos); file->invoke("seek", curPos);
@ -277,13 +251,15 @@ void Channel::togglePause()
} }
/** /**
* @brief * @brief
* @return
*/ */
bool Channel::isMountDisk() void Channel::showFinishPromot()
{ {
QString info = Tool::writeCom(QString("df %1").arg(Constant::MountedPath)); videoInput->unLinkV(videoDecoder);
if (!info.contains(Constant::MountedPath)) QVariantMap dataImage;
return false; dataImage["path"] = Constant::FinishImagePath;
return true; image->setData(dataImage);
} image->start(dataImage);
image->linkV(videoOutput);
state = Finish;
}

View File

@ -12,7 +12,8 @@ public:
Stop, Stop,
Playback, Playback,
Pause, Pause,
Error Error,
Finish
}; };
explicit Channel(QObject* parent = nullptr); explicit Channel(QObject* parent = nullptr);
void init(); void init();
@ -31,7 +32,7 @@ public:
LinkObject* record; LinkObject* record;
// 单个视频时长 // 单个视频时长
int duration = 10 * 60 * 1000; int duration = 1 * 60 * 1000;
bool isRecord = false; bool isRecord = false;
LinkObject* file; LinkObject* file;
@ -55,19 +56,11 @@ public:
void startPlayLive(); void startPlayLive();
void showFinishPromot();
signals: signals:
void playEnd(); void playEnd();
void showRecordState(bool state); void showRecordState(bool state);
private slots:
void onTimeout();
private:
// 用于分段录制的计时器
QTimer* timer;
private:
bool isMountDisk();
}; };
#endif // CHANNEL_H #endif // CHANNEL_H

View File

@ -28,6 +28,7 @@ void CheckStorageThread::run()
int64_t available = Tool::getAvailableStorage(Constant::MountedPath); int64_t available = Tool::getAvailableStorage(Constant::MountedPath);
if (available < THRESHOLD) { if (available < THRESHOLD) {
Log::info("there are not enough storage, then remove some files..."); Log::info("there are not enough storage, then remove some files...");
emit diskWillFull();
// 获取文件列表 // 获取文件列表
QStringList fileList = Tool::getFileList(QString("%1/%2").arg(Constant::VideoPath).arg(Constant::MainChannel)); QStringList fileList = Tool::getFileList(QString("%1/%2").arg(Constant::VideoPath).arg(Constant::MainChannel));
if (!fileList.isEmpty()) { if (!fileList.isEmpty()) {
@ -52,6 +53,8 @@ void CheckStorageThread::run()
Log::error("remove file {} failed", path.toStdString()); Log::error("remove file {} failed", path.toStdString());
} }
} }
} else {
emit diskNotFull();
} }
QThread::msleep(INTERVAL); QThread::msleep(INTERVAL);
} }

View File

@ -8,6 +8,10 @@ class CheckStorageThread : public QThread {
public: public:
CheckStorageThread(); CheckStorageThread();
signals:
void diskWillFull(); // 磁盘空间不足
void diskNotFull(); // 磁盘空间充足
protected: protected:
void run() override; void run() override;
}; };

View File

@ -22,6 +22,7 @@ public:
static constexpr char* MountedPath = "/root/usb"; static constexpr char* MountedPath = "/root/usb";
static constexpr char* ErrorImagePath = "/opt/RecordControlApplication/images/error.jpeg"; static constexpr char* ErrorImagePath = "/opt/RecordControlApplication/images/error.jpeg";
static constexpr char* EmptyImagePath = "/opt/RecordControlApplication/images/empty.jpeg"; static constexpr char* EmptyImagePath = "/opt/RecordControlApplication/images/empty.jpeg";
static constexpr char* FinishImagePath = "/opt/RecordControlApplication/images/finish.jpeg";
static constexpr char* MainChannel = "HDMI-A"; static constexpr char* MainChannel = "HDMI-A";
static constexpr char* SecondaryChannel = "HDMI-B"; static constexpr char* SecondaryChannel = "HDMI-B";
}; };

View File

@ -1,5 +1,4 @@
#include "Message.h" #include "Message.h"
#include <QDebug>
#include <QSpacerItem> #include <QSpacerItem>
#include <QTimer> #include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
@ -69,7 +68,6 @@ void Message::showMessage(QString content, int duration, Level level)
// apeend new // apeend new
QPushButton* btn = generateButton(content, level); QPushButton* btn = generateButton(content, level);
layout->insertWidget(count, btn); layout->insertWidget(count, btn);
qDebug() << this->children();
QTimer* timer = new QTimer(); QTimer* timer = new QTimer();
timer->setInterval(duration); timer->setInterval(duration);

View File

@ -32,7 +32,6 @@ SOURCES += \
main.cpp \ main.cpp \
Widget.cpp \ Widget.cpp \
TcpServer.cpp \ TcpServer.cpp \
TcpController.cpp \
Channel.cpp \ Channel.cpp \
Menu.cpp\ Menu.cpp\
ProgressBar.cpp \ ProgressBar.cpp \
@ -40,19 +39,28 @@ SOURCES += \
Tool.cpp \ Tool.cpp \
CheckStorageThread.cpp \ CheckStorageThread.cpp \
Constant.h \ Constant.h \
Log.cpp Log.cpp \
TcpRequest.cpp \
TcpConnectionHandler.cpp \
TcpRequestHandler.cpp \
TcpResponse.cpp \
SerialPortTool.cpp
HEADERS += \ HEADERS += \
Widget.h \ Widget.h \
TcpServer.h \ TcpServer.h \
TcpController.h \
Channel.h \ Channel.h \
Menu.h \ Menu.h \
ProgressBar.h \ ProgressBar.h \
Message.h \ Message.h \
Tool.h \ Tool.h \
CheckStorageThread.h \ CheckStorageThread.h \
Log.h Log.h \
TcpRequest.h \
TcpConnectionHandler.h \
TcpRequestHandler.h \
TcpResponse.h \
SerialPortTool.h
FORMS += \ FORMS += \
Menu.ui \ Menu.ui \

275
SerialPortTool.cpp Normal file
View File

@ -0,0 +1,275 @@
#include "SerialPortTool.h"
#include "Log.h"
SerialPortTool::SerialPortTool(QObject* parent)
: QObject(parent)
{
serialPort = new QSerialPort();
connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReayRead()));
connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError)));
timer = new QTimer();
timer->setInterval(3000);
timer->stop();
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
auto makeCommand = [=](QString lightNum, QString enable) {
return QByteArray::fromHex(QString("A2 B2 08 02 %1 %2 C2 D2").arg(lightNum).arg(enable).toLatin1());
};
cmdMap[PlaybackStart] = makeCommand("01", "01");
cmdMap[PlaybackEnd] = makeCommand("01", "00");
cmdMap[RecordStart] = makeCommand("02", "01");
cmdMap[RecordEnd] = makeCommand("02", "00");
cmdMap[PlayPause] = makeCommand("03", "01");
cmdMap[PlayResume] = makeCommand("03", "00");
cmdMap[DiskWillFull] = makeCommand("04", "01");
cmdMap[DiskNotFull] = makeCommand("04", "00");
cmdMap[PowerOn] = makeCommand("05", "01");
cmdMap[PowerOff] = makeCommand("05", "00");
cmdMap[Forward] = makeCommand("06", "01");
cmdMap[LoopOn] = makeCommand("07", "01");
cmdMap[LoopOff] = makeCommand("07", "00");
cmdMap[Back] = makeCommand("08", "01");
cmdMap[MainChannelOn] = makeCommand("09", "01");
cmdMap[MainChannelOff] = makeCommand("09", "00");
cmdMap[SecondaryChannelOn] = makeCommand("0A", "01");
cmdMap[SecondaryChannelOff] = makeCommand("0A", "00");
}
SerialPortTool::~SerialPortTool()
{
serialPort->close();
delete serialPort;
}
/**
* @brief
*/
void SerialPortTool::open()
{
serialPort->setPortName("ttyAMA2");
serialPort->setBaudRate(QSerialPort::Baud115200);
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
if (serialPort->open(QIODevice::ReadWrite)) {
Log::info("open serial port: /dev/ttyAMA2 success");
} else {
Log::error("open serial port: /dev/ttyAMA2 failed, ready to retry...");
timer->start();
}
}
/**
* @brief
*/
void SerialPortTool::close()
{
serialPort->close();
}
/**
* @brief
*/
void SerialPortTool::onSerialError(QSerialPort::SerialPortError error)
{
if (error == QSerialPort::ResourceError) {
Log::error("serial port break, try to reopen");
serialPort->close();
timer->start();
}
}
/**
* @brief
*/
void SerialPortTool::onTimeout()
{
timer->stop();
bool ret = serialPort->open(QIODevice::ReadWrite);
if (!ret) {
Log::info("reopen serial port: /dev/ttyAMA2 success");
timer->start();
} else {
Log::error("reopen serial port: /dev/ttyAMA2 failed, ready to retry...");
}
}
/**
* @brief
*/
void SerialPortTool::onReayRead()
{
// 当缓冲区中有大于8个字节的数据时才处理
if (serialPort->bytesAvailable() < 8)
return;
// 由于本项目串口的数据频率小,可以将全部读到的数据当做一条指令来解析
// 当发送数据频率很大时,可以使用另外的缓冲区来存储读到的数据,然后读取缓冲区的数据进行指令的解析
// 将缓冲区的数据全部读取出来
QByteArray data = serialPort->readAll();
qDebug() << "data:" << data;
// 对接受的数据进行协议解析
// 查找包头和包尾
QByteArray head = QByteArray::fromHex("A2 B2");
QByteArray tail = QByteArray::fromHex("C2 D2");
//... 0xA2 0xB2 0x08 0x01 0x01 0x00 0xC2 0xD2 ...
// || ||
// indexHead indexTail
int indexHead = data.indexOf(head);
int indexTail = data.indexOf(tail);
// 没有查找到包头和包尾
if (indexHead == -1 || indexTail == -1)
return;
// 从接收的数据中截取出一帧数据
QByteArray frame = data.mid(indexHead, indexTail + 2 - indexHead);
if (frame.length() <= 4)
return;
// 对一帧数据进行校验
// 长度校验不通过
if (frame[2] != frame.length())
return;
// 命令类型
if (data.at(3) != 0x01)
return;
switch (frame[4]) {
case 1:
emit btnPlaybackClicked();
break;
case 2:
emit btnPreviousClicked();
break;
case 3:
emit btnPauseClicked();
break;
case 4:
emit btnReturnClicked();
break;
case 5:
emit btnBackClicked();
break;
case 6:
emit btnNextClicked();
break;
case 7:
emit btnForwardClicked();
break;
case 8:
emit btnDoneClicked();
break;
default:
break;
}
}
void SerialPortTool::onPlaybackStart()
{
serialPort->write(cmdMap.value(PlaybackStart));
serialPort->flush();
}
void SerialPortTool::onPlaybackEnd()
{
serialPort->write(cmdMap.value(PlaybackEnd));
serialPort->flush();
}
void SerialPortTool::onRecordStart()
{
serialPort->write(cmdMap.value(RecordStart));
serialPort->flush();
}
void SerialPortTool::onRecordEnd()
{
serialPort->write(cmdMap.value(RecordEnd));
serialPort->flush();
}
void SerialPortTool::onPlayPause()
{
serialPort->write(cmdMap.value(PlayPause));
serialPort->flush();
}
void SerialPortTool::onPlayResume()
{
serialPort->write(cmdMap.value(PlayResume));
serialPort->flush();
}
void SerialPortTool::onDiskWillFull()
{
serialPort->write(cmdMap.value(DiskWillFull));
serialPort->flush();
}
void SerialPortTool::onDiskNotFull()
{
serialPort->write(cmdMap.value(DiskNotFull));
serialPort->flush();
}
void SerialPortTool::onPowerOn()
{
serialPort->write(cmdMap.value(PowerOn));
serialPort->flush();
}
void SerialPortTool::onPowerOff()
{
serialPort->write(cmdMap.value(PowerOff));
serialPort->flush();
}
void SerialPortTool::onFoward()
{
serialPort->write(cmdMap.value(Forward));
serialPort->flush();
}
void SerialPortTool::onBack()
{
serialPort->write(cmdMap.value(Back));
serialPort->flush();
}
void SerialPortTool::onLoopOn()
{
serialPort->write(cmdMap.value(LoopOn));
serialPort->flush();
}
void SerialPortTool::onLoopOff()
{
serialPort->write(cmdMap.value(LoopOff));
serialPort->flush();
}
void SerialPortTool::onMainChannelOn()
{
serialPort->write(cmdMap.value(MainChannelOn));
serialPort->flush();
}
void SerialPortTool::onMainChannelOff()
{
serialPort->write(cmdMap.value(MainChannelOff));
serialPort->flush();
}
void SerialPortTool::onSecondaryChannelOn()
{
serialPort->write(cmdMap.value(SecondaryChannelOn));
serialPort->flush();
}
void SerialPortTool::onSecondaryChannelOff()
{
serialPort->write(cmdMap.value(SecondaryChannelOff));
serialPort->flush();
}

77
SerialPortTool.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef SERIALPORT_H
#define SERIALPORT_H
#include <QMap>
#include <QObject>
#include <QSerialPort>
#include <QTimer>
class SerialPortTool : public QObject {
Q_OBJECT
public:
explicit SerialPortTool(QObject* parent = 0);
~SerialPortTool();
void open();
void close();
public slots:
void onPlaybackStart();
void onPlaybackEnd();
void onRecordStart();
void onRecordEnd();
void onPlayPause();
void onPlayResume();
void onDiskWillFull();
void onDiskNotFull();
void onPowerOn();
void onPowerOff();
void onFoward();
void onBack();
void onLoopOn();
void onLoopOff();
void onMainChannelOn();
void onMainChannelOff();
void onSecondaryChannelOn();
void onSecondaryChannelOff();
private slots:
void onSerialError(QSerialPort::SerialPortError error);
void onTimeout();
void onReayRead();
signals:
void btnPlaybackClicked();
void btnPreviousClicked();
void btnPauseClicked();
void btnReturnClicked();
void btnBackClicked();
void btnNextClicked();
void btnForwardClicked();
void btnDoneClicked();
private:
enum CommandType {
PlaybackStart,
PlaybackEnd,
RecordStart,
RecordEnd,
PlayPause,
PlayResume,
DiskWillFull,
DiskNotFull,
PowerOn,
PowerOff,
Forward,
Back,
LoopOn,
LoopOff,
MainChannelOn,
MainChannelOff,
SecondaryChannelOff,
SecondaryChannelOn
};
QSerialPort* serialPort; // 串口
QMap<CommandType, QByteArray> cmdMap; // 指令列表
QTimer* timer;
};
#endif // SERIALPORT_H

38
TcpConnectionHandler.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "TcpConnectionHandler.h"
#include "Log.h"
#include "TcpResponse.h"
TcpConnectionHandler::TcpConnectionHandler(QTcpSocket* socket, TcpRequestHandler* handler, QObject* parent)
: socket(socket)
, handler(handler)
, QObject(parent)
{
request = new TcpRequest();
response = new TcpResponse(socket);
connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
}
TcpConnectionHandler::~TcpConnectionHandler()
{
delete request;
delete response;
}
void TcpConnectionHandler::onReadyRead()
{
bool ret = request->readFromSocket(socket);
if (!ret) {
response->error(request->getErrorString());
return;
}
handler->service(request, response);
}
void TcpConnectionHandler::onDisconnected()
{
Log::info("one client disconnected");
emit disconnected();
socket->close();
}

29
TcpConnectionHandler.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef TCPCONNECTIONHANDLER_H
#define TCPCONNECTIONHANDLER_H
#include "TcpRequest.h"
#include "TcpRequestHandler.h"
#include <QObject>
#include <QTcpSocket>
class TcpConnectionHandler : public QObject {
Q_OBJECT
public:
explicit TcpConnectionHandler(QTcpSocket* socket, TcpRequestHandler* handler, QObject* parent = nullptr);
~TcpConnectionHandler();
private slots:
void onReadyRead();
void onDisconnected();
signals:
void disconnected();
private:
QTcpSocket* socket;
TcpRequest* request;
TcpResponse* response;
TcpRequestHandler* handler;
};
#endif // TCPCONNECTIONHANDLER_H

View File

@ -1,59 +0,0 @@
#ifndef TCPCONTROLLER_H
#define TCPCONTROLLER_H
#include <QDir>
#include <QMap>
#include <QObject>
#include <QTcpSocket>
#include <functional>
using Routes = QMap<QString, std::function<void(QTcpSocket*)>>;
class TcpController : public QObject {
Q_OBJECT
public:
TcpController(QObject* parent = nullptr);
Routes getRoutes();
private:
Routes routes;
void setIpaddr(QTcpSocket* socket);
void setName(QTcpSocket* socket);
void getName(QTcpSocket* socket);
void setVideoEnc(QTcpSocket* socket);
void getVideoEnc(QTcpSocket* socket);
void getFileList(QTcpSocket* socket);
void deleteFile(QTcpSocket* socket);
void setRecordMode(QTcpSocket* socket);
void setPlaybackMode(QTcpSocket* socket);
void reboot(QTcpSocket* socket);
inline QVariantMap parseParams(QTcpSocket* socket);
};
/**
* @brief tcp请求参数
* @param socket
* @return
*/
inline QVariantMap TcpController::parseParams(QTcpSocket* socket)
{
QVariantMap params;
// exmaple:data1=xxx\r\ndata2=xxx\r\n...
QStringList data = QString(socket->readAll()).split("\r\n");
for (int i = 0; i < data.length(); i++) {
if (data.at(i).contains("=")) {
QStringList pair = data.at(i).split("=");
params.insert(pair[0], pair[1]);
}
}
return params;
}
#endif // TCPCONTROLLER_H

73
TcpRequest.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "TcpRequest.h"
#include "Log.h"
#include <Json.h>
QString TcpRequest::getUrl()
{
return url;
}
/**
* @brief
*/
QVariantMap TcpRequest::getBodyParams()
{
QVariantMap params = this->parseParams();
return params;
}
QString TcpRequest::getErrorString()
{
return errorString;
}
/**
* @brief
*/
bool TcpRequest::readFromSocket(QTcpSocket* socket)
{
// 读取第一行
QString str = socket->readLine().trimmed();
if (!str.contains("Url: ")) {
Log::error(QString("receive error headers: %1").arg(str).toStdString());
socket->readAll();
socket->flush();
errorString = "未知请求路径参数:" + str;
return false;
} else {
url = str.split(": ")[1];
}
// 读取第二行
str = socket->readLine().trimmed();
if (!str.contains("Content-Length: ")) {
contentLength = 0;
} else {
contentLength = str.split(": ")[1].toLongLong();
}
// 如果请求体长度为0就不再往下读取
if (contentLength == 0) {
socket->readAll();
socket->flush();
return true;
} else {
body.clear();
// 读取空行并丢弃
socket->readLine();
// 循环读取数据直到读取了content-length长度数据
qint64 readSize = 0;
while (socket->bytesAvailable() > 0) {
char bytes[contentLength];
qint64 size = socket->read(bytes, contentLength - readSize);
if (size == -1) {
errorString = "socket读取数据出错";
return false;
}
readSize += size;
body += QString::fromLocal8Bit(bytes, size);
if (readSize >= contentLength) {
break;
}
}
}
return true;
}

42
TcpRequest.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef TCPREQUEST_H
#define TCPREQUEST_H
#include <QTcpSocket>
class TcpRequest {
public:
bool readFromSocket(QTcpSocket* socket);
QString getUrl();
QVariantMap getBodyParams();
QString getErrorString();
private:
QString url; // 请求路径
qint64 contentLength = 0; // 请求体长度
QString body; // 请求体
QString errorString;
private:
inline QVariantMap parseParams();
};
/**
* @brief
*/
inline QVariantMap TcpRequest::parseParams()
{
QVariantMap map;
QStringList list = body.split("&");
for (QString str : list) {
if (str.isEmpty()) {
continue;
} else {
QString key = str.split("=")[0];
QString value = str.split("=")[1];
map[key] = value;
}
}
return map;
}
#endif // TCPREQUEST_H

View File

@ -1,4 +1,4 @@
#include "TcpController.h" #include "TcpRequestHandler.h"
#include "Channel.h" #include "Channel.h"
#include "Constant.h" #include "Constant.h"
#include "Json.h" #include "Json.h"
@ -9,34 +9,49 @@
extern const QList<Channel*> channelList; extern const QList<Channel*> channelList;
TcpController::TcpController(QObject* parent) TcpRequestHandler::TcpRequestHandler(QObject* parent)
: QObject(parent) : QObject(parent)
{ {
// 映射请求路径与处理函数
routes.insert("set_ipaddr", std::bind(&TcpController::setIpaddr, this, std::placeholders::_1));
routes.insert("set_video_enc", std::bind(&TcpController::setVideoEnc, this, std::placeholders::_1));
routes.insert("get_video_enc", std::bind(&TcpController::getVideoEnc, this, std::placeholders::_1));
routes.insert("get_file_list", std::bind(&TcpController::getFileList, this, std::placeholders::_1));
routes.insert("delete_file", std::bind(&TcpController::deleteFile, this, std::placeholders::_1));
routes.insert("set_name", std::bind(&TcpController::setName, this, std::placeholders::_1));
routes.insert("get_name", std::bind(&TcpController::getName, this, std::placeholders::_1));
routes.insert("set_record_mode", std::bind(&TcpController::setRecordMode, this, std::placeholders::_1));
routes.insert("set_playback_mode", std::bind(&TcpController::setPlaybackMode, this, std::placeholders::_1));
routes.insert("reoot", std::bind(&TcpController::reboot, this, std::placeholders::_1));
} }
Routes TcpController::getRoutes() void TcpRequestHandler::service(TcpRequest* request, TcpResponse* response)
{ {
return this->routes; QString url = request->getUrl();
response->setUrl(url);
if (url == "/set_ip_addr") {
this->setIpaddr(request, response);
} else if (url == "/set_name") {
this->setName(request, response);
} else if (url == "/get_name") {
this->getName(request, response);
} else if (url == "/set_video_enc") {
this->setVideoEnc(request, response);
} else if (url == "/get_video_enc") {
this->getVideoEnc(request, response);
} else if (url == "/get_file_list") {
this->getFileList(request, response);
} else if (url == "/delete_file") {
this->deleteFile(request, response);
} else if (url == "/set_record_mode") {
this->setRecordMode(request, response);
} else if (url == "/set_playback_mode") {
this->setPlaybackMode(request, response);
} else if (url == "/reboot") {
this->reboot(request, response);
} else if (url == "/set_current_time") {
this->setCurrentTime(request, response);
} else {
response->error("访问路径未找到");
}
} }
/** /**
* @brief ip * @brief ip
* @param socket * @param socket
*/ */
void TcpController::setIpaddr(QTcpSocket* socket) void TcpRequestHandler::setIpaddr(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
QVariantMap netMap = Json::loadFile(Constant::NetConfigPath).toMap(); QVariantMap netMap = Json::loadFile(Constant::NetConfigPath).toMap();
if (params.contains("ip")) if (params.contains("ip"))
@ -47,7 +62,7 @@ void TcpController::setIpaddr(QTcpSocket* socket)
netMap["gateway"] = params.value("gateway"); netMap["gateway"] = params.value("gateway");
Json::saveFile(netMap, Constant::NetConfigPath); Json::saveFile(netMap, Constant::NetConfigPath);
socket->write("url=set_ipaddr\r\nstatus=success"); reponse->success();
Tool::writeCom(Constant::NetScriptPath); Tool::writeCom(Constant::NetScriptPath);
} }
@ -55,41 +70,41 @@ void TcpController::setIpaddr(QTcpSocket* socket)
* @brief * @brief
* @param socket * @param socket
*/ */
void TcpController::setName(QTcpSocket* socket) void TcpRequestHandler::setName(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (params.isEmpty() || !params.contains("name")) { if (params.isEmpty() || !params.contains("name")) {
Log::error("setName params error, missing param \"name\""); Log::error("setName params error, missing param \"name\"");
socket->write("url=set_name\r\nstatus=error"); reponse->error("名称不能为空");
return; return;
} }
QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap();
cfg["name"] = params.value("name"); cfg["name"] = params.value("name");
Json::saveFile(cfg, Constant::ConfigurationPath); Json::saveFile(cfg, Constant::ConfigurationPath);
socket->write("url=set_name\r\nstatus=success"); reponse->success();
} }
/** /**
* @brief * @brief
*/ */
void TcpController::getName(QTcpSocket* socket) void TcpRequestHandler::getName(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap config = Json::loadFile(Constant::ConfigurationPath).toMap(); QVariantMap config = Json::loadFile(Constant::ConfigurationPath).toMap();
QString name = config["name"].toString(); QString name = config["name"].toString();
QString data = QString("url=get_name\r\nstatus=success\r\nname=%1").arg(name); QString data = QString("url=get_name\r\nstatus=success\r\nname=%1").arg(name);
socket->write(data.toStdString().data()); reponse->success("", name);
} }
/** /**
* @brief * @brief
* @param socket * @param socket
*/ */
void TcpController::setVideoEnc(QTcpSocket* socket) void TcpRequestHandler::setVideoEnc(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (params.isEmpty() || !params.contains("interface")) { if (params.isEmpty() || !params.contains("interface")) {
Log::error("setVideoEncode params error, missing param \"interface\""); Log::error("setVideoEncode params error, missing param \"interface\"");
socket->write("url=set_video_enc\r\nstatus=error"); reponse->error("缺少接口参数");
return; return;
} }
@ -101,7 +116,7 @@ void TcpController::setVideoEnc(QTcpSocket* socket)
} }
if (chn) { if (chn) {
// 修改分辨率和帧率 // 修改分辨率和帧率
chn->videoEncoder->setData(params); // chn->videoEncoder->setData(params);
// 更新配置文件 // 更新配置文件
QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap();
QVariantList interfaces = cfg.value("interface").toList(); QVariantList interfaces = cfg.value("interface").toList();
@ -121,10 +136,10 @@ void TcpController::setVideoEnc(QTcpSocket* socket)
} }
cfg["interface"] = interfaces; cfg["interface"] = interfaces;
Json::saveFile(cfg, Constant::ConfigurationPath); Json::saveFile(cfg, Constant::ConfigurationPath);
socket->write("url=set_video_enc\r\nstatus=success"); reponse->success("修改录制参数成功,重启后生效");
} else { } else {
Log::error("set video encode params error, no such interface {}", interface.toStdString()); Log::error("set video encode params error, no such interface {}", interface.toStdString());
socket->write("url=set_video_enc\r\nstatus=error"); reponse->error(QString("无对应接口: %1").arg(interface));
} }
} }
@ -132,9 +147,9 @@ void TcpController::setVideoEnc(QTcpSocket* socket)
* @brief * @brief
* @param socket * @param socket
*/ */
void TcpController::getVideoEnc(QTcpSocket* socket) void TcpRequestHandler::getVideoEnc(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (params.contains("interface")) { if (params.contains("interface")) {
QString interface = params.value("interface").toString(); QString interface = params.value("interface").toString();
@ -149,21 +164,11 @@ void TcpController::getVideoEnc(QTcpSocket* socket)
targetInterfaceCfg = item; targetInterfaceCfg = item;
} }
} }
QString width;
QString height;
QString framerate;
QVariantMap encodeV = targetInterfaceCfg.value("encV").toMap(); QVariantMap encodeV = targetInterfaceCfg.value("encV").toMap();
width = encodeV.value("width").toString(); reponse->success("", encodeV);
height = encodeV.value("height").toString();
framerate = encodeV.value("framerate").toString();
QString data = QString("url=get_video_enc\r\nstatus=success\r\nwidth=%1\r\nheight=%2\r\nframerate=%3")
.arg(width)
.arg(height)
.arg(framerate);
socket->write(data.toStdString().data());
} else { } else {
Log::error("getVideoEnc params error, missing param \"interface\""); Log::error("getVideoEnc params error, missing param \"interface\"");
socket->write("url=get_video_enc\r\nstatus=error"); reponse->error("缺少接口参数");
return; return;
} }
} }
@ -172,12 +177,12 @@ void TcpController::getVideoEnc(QTcpSocket* socket)
* @brief * @brief
* @param socket * @param socket
*/ */
void TcpController::getFileList(QTcpSocket* socket) void TcpRequestHandler::getFileList(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (params.isEmpty() || !params.contains("interface")) { if (params.isEmpty() || !params.contains("interface")) {
Log::error("getFileList params error, missing param \"interface\""); Log::error("getFileList params error, missing param \"interface\"");
socket->write("url=get_file_list\r\nstatus=error"); reponse->error("缺少接口参数");
return; return;
} }
@ -186,26 +191,24 @@ void TcpController::getFileList(QTcpSocket* socket)
QStringList videoList = Tool::getFileList(videoPath); QStringList videoList = Tool::getFileList(videoPath);
if (videoList.isEmpty()) { // 复制两份测试用
Log::error("getFileList error, video dir is empty "); QStringList copy = videoList;
socket->write("url=get_file_list\r\nstatus=error"); QStringList copy1 = videoList;
return; videoList.append(copy);
} videoList.append(copy1);
QString data; reponse->success("获取文件成功", videoList);
data = "url=get_file_list\r\nstatus=success\r\n" + videoList.join("\r\n");
socket->write(data.toStdString().data());
} }
/** /**
* @brief * @brief
* @param socket * @param socket
*/ */
void TcpController::deleteFile(QTcpSocket* socket) void TcpRequestHandler::deleteFile(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (!params.contains("interface") || !params.contains("filename")) { if (!params.contains("interface") || !params.contains("filename")) {
Log::error("deleteFile params error, missing params \"interface\" or \"filename\""); Log::error("deleteFile params error, missing params \"interface\" or \"filename\"");
socket->write("url=delete_file\r\nstatus=error"); reponse->error("缺少端口或文件名");
return; return;
} }
@ -219,10 +222,10 @@ void TcpController::deleteFile(QTcpSocket* socket)
if (video.exists()) { if (video.exists()) {
video.remove(); video.remove();
Log::info("remove video {}", filePath.toStdString()); Log::info("remove video {}", filePath.toStdString());
socket->write("url=delete_file\r\nstatus=success"); reponse->success("删除成功");
} else { } else {
Log::error("error, file: {} dont exist", filePath.toStdString()); Log::error("error, file: {} dont exist", filePath.toStdString());
socket->write("url=delete_file\r\nstatus=error"); reponse->error("文件不存在");
} }
} }
@ -230,12 +233,12 @@ void TcpController::deleteFile(QTcpSocket* socket)
* @brief / * @brief /
* @param socket * @param socket
*/ */
void TcpController::setRecordMode(QTcpSocket* socket) void TcpRequestHandler::setRecordMode(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (!params.contains("recordMode")) { if (!params.contains("recordMode")) {
Log::error("setRecordMode params error, missing param \"recordMode\""); Log::error("setRecordMode params error, missing param \"recordMode\"");
socket->write("url=set_record_mode\r\nstatus=error"); reponse->error("缺少录制模式参数");
return; return;
} }
int recordMode = params.value("recordMode").toInt(); int recordMode = params.value("recordMode").toInt();
@ -243,60 +246,93 @@ void TcpController::setRecordMode(QTcpSocket* socket)
&& recordMode != Constant::TwoChannelRecord && recordMode != Constant::TwoChannelRecord
&& recordMode != Constant::NoChannelRecord) { && recordMode != Constant::NoChannelRecord) {
Log::error("setRecordMode params error, unkown param \"recordMode\": {}", recordMode); Log::error("setRecordMode params error, unkown param \"recordMode\": {}", recordMode);
socket->write("url=set_record_mode\r\nstatus=error"); reponse->error("录制参数错误");
return; return;
} }
QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap();
int mode = cfg["recordMode"].toInt(); int mode = cfg["recordMode"].toInt();
if (recordMode == mode) { if (recordMode == mode) {
Log::info("record mode has not changed, there is nothing need to do"); Log::info("record mode has not changed, there is nothing need to do");
socket->write("url=set_record_mode\r\nstatus=success"); reponse->success("录制参数无需改变");
return; return;
} }
// 更新配置文件 // 更新配置文件
cfg["recordMode"] = recordMode; cfg["recordMode"] = recordMode;
Json::saveFile(cfg, Constant::ConfigurationPath); Json::saveFile(cfg, Constant::ConfigurationPath);
socket->write("url=set_record_mode\r\nstatus=success"); reponse->success("设置录制参数成功, 重启后生效");
} }
/** /**
* @brief / * @brief /
* @param socket * @param socket
*/ */
void TcpController::setPlaybackMode(QTcpSocket* socket) void TcpRequestHandler::setPlaybackMode(TcpRequest* request, TcpResponse* reponse)
{ {
QVariantMap params = this->parseParams(socket); QVariantMap params = request->getBodyParams();
if (!params.contains("playbackMode")) { if (!params.contains("playbackMode")) {
Log::error("setPlaybackMode params error, missing param \"playbackmode\""); Log::error("setPlaybackMode params error, missing param \"playbackmode\"");
socket->write("url=set_playback_mode\r\nstatus=error"); reponse->error("缺少回放模式参数");
return; return;
} }
int playbackMode = params.value("playbackMode").toInt(); int playbackMode = params.value("playbackMode").toInt();
if (playbackMode != Constant::OneChannelPlayback if (playbackMode != Constant::OneChannelPlayback
&& playbackMode != Constant::TwoChannelPlayback) { && playbackMode != Constant::TwoChannelPlayback) {
Log::error("setPlayBack params error, unkown param \"playbackMode\": {}", playbackMode); Log::error("setPlayBack params error, unkown param \"playbackMode\": {}", playbackMode);
socket->write("url=set_playback_mode\r\nstatus=error"); reponse->error("回放模式参数错误");
return;
} }
QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap();
int mode = cfg["playbackMode"].toInt();;
if (playbackMode == mode) {
Log::info("record mode has not changed, there is nothing need to do");
reponse->success("回放参数无需改变");
return;
}
// 跟新配置文件
cfg["playbackMode"] = playbackMode; cfg["playbackMode"] = playbackMode;
Json::saveFile(cfg, Constant::ConfigurationPath); Json::saveFile(cfg, Constant::ConfigurationPath);
socket->write("url=set_playback_mode\r\nstatus=success"); reponse->success("修改回放模式成功,重启后生效");
} }
/** /**
* @brief * @brief
* @param socket * @param socket
*/ */
void TcpController::reboot(QTcpSocket* socket) void TcpRequestHandler::reboot(TcpRequest* request, TcpResponse* reponse)
{ {
// 先停止所有的录制 // 先停止所有的录制
for (Channel* chn : channelList) { for (Channel* chn : channelList) {
chn->stopRecord(); chn->stopRecord();
} }
socket->write("url=reboot\r\nstatus=success"); reponse->success("重启成功");
socket->flush();
// 退出程序等待后台运行的shell脚本重启程序 // 退出程序等待后台运行的shell脚本重启程序
exit(0); exit(0);
}
/**
* @brief
* @param socket
*/
void TcpRequestHandler::setCurrentTime(TcpRequest* request, TcpResponse* reponse)
{
QVariantMap params = request->getBodyParams();
if (!params.contains("time")) {
Log::error("setCurrentTime params error, missing params \"time\"");
reponse->error("缺少时间参数");
return;
}
QString time = params.value("time").toString();
QString cmd = QString("/link/bin/rtc -s time %1").arg(time);
QString result = Tool::writeCom(cmd);
// 修改成功会返回类似信息, "Tue Mar 5 15:51:18 CST 2024"
if (result.isEmpty()) {
reponse->error("时间同步失败");
return;
} else {
reponse->success("时间同步成功");
}
} }

35
TcpRequestHandler.h Executable file
View File

@ -0,0 +1,35 @@
#ifndef TCPCONTROLLER_H
#define TCPCONTROLLER_H
#include "TcpRequest.h"
#include "TcpResponse.h"
#include <QObject>
#include <QTcpSocket>
class TcpRequestHandler : public QObject {
Q_OBJECT
public:
TcpRequestHandler(QObject* parent = nullptr);
void service(TcpRequest* request, TcpResponse* reponse);
private:
void setIpaddr(TcpRequest* request, TcpResponse* reponse);
void setName(TcpRequest* request, TcpResponse* reponse);
void getName(TcpRequest* request, TcpResponse* reponse);
void setVideoEnc(TcpRequest* request, TcpResponse* reponse);
void getVideoEnc(TcpRequest* request, TcpResponse* reponse);
void getFileList(TcpRequest* request, TcpResponse* reponse);
void deleteFile(TcpRequest* request, TcpResponse* reponse);
void setRecordMode(TcpRequest* request, TcpResponse* reponse);
void setPlaybackMode(TcpRequest* request, TcpResponse* reponse);
void reboot(TcpRequest* request, TcpResponse* reponse);
void setCurrentTime(TcpRequest* request, TcpResponse* reponse);
};
#endif // TCPCONTROLLER_H

67
TcpResponse.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "TcpResponse.h"
#include <Json.h>
TcpResponse::TcpResponse(QTcpSocket* socket)
: socket(socket)
{
}
/**
* @brief url
* @param url
*/
void TcpResponse::setUrl(const QString& url)
{
this->url = url;
}
/**
* @brief
*/
void TcpResponse::success(QString msg, QVariant data)
{
QByteArray respone;
// 请求路径
respone += QString("Url: %1\r\n").arg(url).toUtf8();
// 响应数据
QVariantMap map;
map["status"] = "success";
map["msg"] = msg.isEmpty() ? "响应成功" : msg;
map["data"] = data.isNull() ? "" : data;
QByteArray byteData = Json::encode(map).toUtf8();
int length = byteData.length();
// 响应体长度
respone += QString("Content-Length: %1\r\n\r\n").arg(length).toUtf8();
respone += byteData;
socket->write(respone);
socket->flush();
}
/**
* @brief
*/
void TcpResponse::error(QString msg)
{
QByteArray respone;
// 请求路径
respone += QString("Url: %1\r\n").arg(url).toUtf8();
// 响应数据
QVariantMap map;
map["status"] = "error";
map["msg"] = msg.isEmpty() ? "响应失败" : msg;
map["data"] = "";
// 打包成json格式
QByteArray byteData = Json::encode(map).toUtf8();
int length = byteData.length();
// 响应体长度
respone += QString("Content-Length: %1\r\n\r\n").arg(length).toUtf8();
respone += byteData;
socket->write(respone);
socket->flush();
}

20
TcpResponse.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef TCPRESPONSE_H
#define TCPRESPONSE_H
#include <QTcpSocket>
class TcpResponse {
public:
explicit TcpResponse(QTcpSocket* socket);
void success(QString msg = "", QVariant data = QVariant());
void error(QString msg = "");
void setUrl(const QString& url);
private:
QTcpSocket* socket;
QString url;
};
#endif // TCPRESPONSE_H

View File

@ -5,10 +5,46 @@
TcpServer::TcpServer(QObject* parent) TcpServer::TcpServer(QObject* parent)
: QTcpServer(parent) : QTcpServer(parent)
{ {
controller = new TcpController(); handler = new TcpRequestHandler();
connect(this, &TcpServer::newConnection, this, &TcpServer::onNewConnect); connect(this, &TcpServer::newConnection, this, &TcpServer::onNewConnect);
} }
TcpServer::~TcpServer()
{
delete handler;
if (!clients.isEmpty()) {
qDeleteAll(clients);
clients.clear();
}
}
/**
* @brief
*/
void TcpServer::listen()
{
bool ret = QTcpServer::listen(QHostAddress::Any, 8080);
if (!ret) {
Log::error(QString("listen port 8080 failed, %1").arg(errorString()).toStdString());
return;
} else {
Log::info("start tcp server, listen port 8080");
}
}
/**
* @brief tcp服务器
*/
void TcpServer::close()
{
QTcpServer::close();
if (!clients.isEmpty()) {
qDeleteAll(clients);
clients.clear();
}
}
/** /**
* @brief * @brief
*/ */
@ -17,33 +53,13 @@ void TcpServer::onNewConnect()
if (this->hasPendingConnections()) { if (this->hasPendingConnections()) {
QTcpSocket* socket = this->nextPendingConnection(); QTcpSocket* socket = this->nextPendingConnection();
Log::info("new client connected, ip: {}", socket->peerAddress().toString().toStdString()); Log::info("new client connected, ip: {}", socket->peerAddress().toString().toStdString());
clients.push_back(socket); TcpConnectionHandler* connection = new TcpConnectionHandler(socket, handler);
connect(socket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead); clients.push_back(connection);
connect(socket, &QTcpSocket::disconnected, [=] {
QTcpSocket* sk = static_cast<QTcpSocket*>(sender()); connect(connection, &TcpConnectionHandler::disconnected, [=] {
Log::info(" one client disconnect..."); // 从列表中移除该指针
clients.removeOne(sk); TcpConnectionHandler* conn = static_cast<TcpConnectionHandler*>(sender());
clients.removeOne(conn);
}); });
} }
} }
/**
* @brief
*/
void TcpServer::onReadyRead()
{
QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
// url=xxx
QString data = socket->readLine().trimmed();
if (data.contains("=")) {
QString url = data.split("=")[1];
bool contains = controller->getRoutes().contains(url);
if (contains) {
Log::info("tcp server receive request: {}", url.toStdString());
auto handler = controller->getRoutes().value(url);
handler(socket);
} else {
socket->write(QString("url=%1\r\nstatus=error").arg(url).toStdString().data());
}
}
}

View File

@ -1,7 +1,8 @@
#ifndef TCPSERVER_H #ifndef TCPSERVER_H
#define TCPSERVER_H #define TCPSERVER_H
#include "TcpController.h" #include "TcpConnectionHandler.h"
#include "TcpRequestHandler.h"
#include <QTcpServer> #include <QTcpServer>
class TcpServer : public QTcpServer { class TcpServer : public QTcpServer {
@ -9,14 +10,16 @@ class TcpServer : public QTcpServer {
public: public:
TcpServer(QObject* parent = nullptr); TcpServer(QObject* parent = nullptr);
~TcpServer();
void listen();
void close();
private slots: private slots:
void onReadyRead();
void onNewConnect(); void onNewConnect();
private: private:
TcpController* controller; QList<TcpConnectionHandler*> clients;
QList<QTcpSocket*> clients; TcpRequestHandler* handler;
}; };
#endif // TCPSERVER_H #endif // TCPSERVER_H

View File

@ -29,6 +29,7 @@ QStringList Tool::getFileList(QString path)
nameFilters << "*.mp4"; nameFilters << "*.mp4";
dir.setNameFilters(nameFilters); dir.setNameFilters(nameFilters);
fileList = dir.entryList(); fileList = dir.entryList();
return fileList; return fileList;
} }

View File

@ -3,6 +3,7 @@
#include "Constant.h" #include "Constant.h"
#include "Json.h" #include "Json.h"
#include "Log.h" #include "Log.h"
#include "SerialPortTool.h"
#include "TcpServer.h" #include "TcpServer.h"
#include "Tool.h" #include "Tool.h"
#include "ui_Widget.h" #include "ui_Widget.h"
@ -18,6 +19,7 @@ QString curFilename;
extern QList<Channel*> channelList; extern QList<Channel*> channelList;
extern Constant::PlaybackMode playbackMode; extern Constant::PlaybackMode playbackMode;
extern SerialPortTool* serialPortTool;
Widget::Widget(QWidget* parent) Widget::Widget(QWidget* parent)
: QWidget(parent) : QWidget(parent)
@ -46,12 +48,6 @@ Widget::Widget(QWidget* parent)
ui->listWidget->verticalScrollBar()->hide(); ui->listWidget->verticalScrollBar()->hide();
ui->listWidget->hide(); ui->listWidget->hide();
// 初始化串口
initSerialPort();
// 初始化指令列表
initCommandMap();
// 每5秒更新一次进度条 // 每5秒更新一次进度条
progressTimer = new QTimer(); progressTimer = new QTimer();
progressTimer->setInterval(500); progressTimer->setInterval(500);
@ -60,47 +56,51 @@ Widget::Widget(QWidget* parent)
for (Channel* chn : channelList) { for (Channel* chn : channelList) {
connect(chn, SIGNAL(playEnd()), this, SLOT(onPlayEnd())); connect(chn, SIGNAL(playEnd()), this, SLOT(onPlayEnd()));
connect(chn, SIGNAL(showRecordState(bool)), this, SLOT(onShowRecordLabel(bool))); connect(chn, SIGNAL(showRecordState(bool)), this, SLOT(onShowRecordLabel(bool)));
// 监听输入信号的变化
connect(chn->videoInput, &LinkObject::newEvent, [=](QString type, QVariant msg) {
if (type == "signal") {
QVariantMap data = msg.toMap();
bool available = data["avalible"].toBool();
if (available) {
Log::info("video input {} is available",
chn->channelName == Constant::MainChannel ? "HDMI-A" : "HDMI-B");
if (chn->channelName == Constant::MainChannel) {
serialPortTool->onMainChannelOn();
} else {
serialPortTool->onSecondaryChannelOn();
}
} else {
Log::info("video input {} is not available",
chn->channelName == Constant::MainChannel ? "HDMI-A" : "HDMI-B");
if (chn->channelName == Constant::MainChannel) {
serialPortTool->onMainChannelOff();
} else {
serialPortTool->onSecondaryChannelOff();
}
}
}
});
} }
connect(serialPortTool, SIGNAL(btnPlaybackClicked()), this, SLOT(onBtnPlaybackClicked()));
connect(serialPortTool, SIGNAL(btnPreviousClicked()), this, SLOT(onBtnPreviousClicked()));
connect(serialPortTool, SIGNAL(btnPauseClicked()), this, SLOT(onBtnEnterClicked()));
connect(serialPortTool, SIGNAL(btnReturnClicked()), this, SLOT(onBtnReturnClicked()));
connect(serialPortTool, SIGNAL(btnBackClicked()), this, SLOT(onBtnBackClicked()));
connect(serialPortTool, SIGNAL(btnNextClicked()), this, SLOT(onBtnNextClicked()));
connect(serialPortTool, SIGNAL(btnForwardClicked()), this, SLOT(onBtnForwardClicked()));
connect(serialPortTool, SIGNAL(btnDoneClicked()), this, SLOT(onBtnEnterClicked()));
} }
Widget::~Widget() Widget::~Widget()
{ {
serialPort->close();
delete ui; delete ui;
delete timer;
delete progressTimer; delete progressTimer;
delete menu; delete menu;
delete progressBar; delete progressBar;
delete message; delete message;
} }
/**
* @brief
*/
void Widget::initSerialPort()
{
// 用于重连串口的timer
timer = new QTimer();
timer->setInterval(3000);
timer->stop();
serialPort = new QSerialPort();
serialPort->setPortName("ttyAMA2");
serialPort->setBaudRate(QSerialPort::Baud115200);
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
if (serialPort->open(QIODevice::ReadWrite)) {
Log::info("open serial port: /dev/ttyAMA2 success");
} else {
Log::error("open serial port: /dev/ttyAMA2 failed, ready to retry...");
timer->start();
}
connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError)));
connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
/** /**
* @brief * @brief
*/ */
@ -118,21 +118,6 @@ void Widget::showPlayList()
ui->listWidget->setCurrentRow(0); ui->listWidget->setCurrentRow(0);
} }
/**
* @brief
*/
void Widget::onTimeout()
{
timer->stop();
bool ret = serialPort->open(QIODevice::ReadWrite);
if (!ret) {
Log::info("reopen serial port: /dev/ttyAMA2 success");
timer->start();
} else {
Log::error("reopen serial port: /dev/ttyAMA2 failed, ready to retry...");
}
}
/** /**
* @brief * @brief
*/ */
@ -143,80 +128,10 @@ void Widget::onProgressTimeout()
progressBar->setCurrent(pos); progressBar->setCurrent(pos);
} }
/**
* @brief
* @param error
*/
void Widget::onSerialError(QSerialPort::SerialPortError error)
{
if (error == QSerialPort::ResourceError) {
Log::error("serial port break, try to reopen");
serialPort->close();
timer->start();
}
}
/**
* @brief
*/
void Widget::initCommandMap()
{
commandMap.insert("A1", CmdPlayback);
commandMap.insert("A2", CmdNext);
commandMap.insert("A3", CmdPrevious);
commandMap.insert("A4", CmdForward);
commandMap.insert("A5", CmdBack);
commandMap.insert("A6", CmdPause);
commandMap.insert("A7", CmdReturn);
commandMap.insert("A8", CmdEnter);
}
/**
* @brief
*/
void Widget::onReadyRead()
{
serialPort->waitForReadyRead(100);
QString data = serialPort->readLine();
if (!commandMap.contains(data)) {
Log::error("receive unkown command {} from serial port: /dev/ttyAMA2", data.toStdString());
return;
}
Command cmd = commandMap.value(data);
switch (cmd) {
case CmdPlayback:
receivePlayback();
break;
case CmdPrevious:
receivePrevious();
break;
case CmdNext:
receiveNext();
break;
case CmdForward:
seek("forward");
break;
case CmdBack:
seek("next");
break;
case CmdPause:
receivePasue();
break;
case CmdEnter:
receiveEnter();
break;
case CmdReturn:
receiveReturn();
break;
default:
break;
}
}
/** /**
* @brief * @brief
*/ */
void Widget::receivePlayback() void Widget::onBtnPlaybackClicked()
{ {
Log::info("receive command playback, playback mode: {}", playbackMode); Log::info("receive command playback, playback mode: {}", playbackMode);
// 如果是一路回放,显示选择菜单 // 如果是一路回放,显示选择菜单
@ -234,7 +149,7 @@ void Widget::receivePlayback()
/** /**
* @brief * @brief
*/ */
void Widget::receivePrevious() void Widget::onBtnPreviousClicked()
{ {
// 菜单显示则切换菜单选中 // 菜单显示则切换菜单选中
if (menu->isVisible()) { if (menu->isVisible()) {
@ -249,14 +164,12 @@ void Widget::receivePrevious()
ui->listWidget->setCurrentRow(curRow - 1); ui->listWidget->setCurrentRow(curRow - 1);
return; return;
} }
if (isPlayback) {
}
} }
/** /**
* @brief * @brief
*/ */
void Widget::receiveNext() void Widget::onBtnNextClicked()
{ {
if (menu->isVisible()) { if (menu->isVisible()) {
menu->setCurChannel(Constant::SecondaryChannel); menu->setCurChannel(Constant::SecondaryChannel);
@ -270,14 +183,12 @@ void Widget::receiveNext()
ui->listWidget->setCurrentRow(curRow + 1); ui->listWidget->setCurrentRow(curRow + 1);
return; return;
} }
if (isPlayback) {
}
} }
/** /**
* @brief * @brief
*/ */
void Widget::receivePasue() void Widget::onBtnPasueClicked()
{ {
if (!isPlayback) if (!isPlayback)
return; return;
@ -288,8 +199,12 @@ void Widget::receivePasue()
progressBar->showMax(); progressBar->showMax();
if (chn->state == Channel::Pause) { if (chn->state == Channel::Pause) {
progressBar->setState(ProgressBar::Pause); progressBar->setState(ProgressBar::Pause);
// 打开暂停灯
serialPortTool->onPlayPause();
} else { } else {
progressBar->setState(ProgressBar::Play); progressBar->setState(ProgressBar::Play);
// 关闭暂停灯
serialPortTool->onPlayPause();
} }
} }
} }
@ -299,7 +214,7 @@ void Widget::receivePasue()
/** /**
* @brief * @brief
*/ */
void Widget::receiveEnter() void Widget::onBtnEnterClicked()
{ {
// 菜单显示,则关闭菜单显示列表 // 菜单显示,则关闭菜单显示列表
if (menu->isVisible()) { if (menu->isVisible()) {
@ -315,6 +230,7 @@ void Widget::receiveEnter()
playOneChannel(); playOneChannel();
else if (playbackMode == Constant::TwoChannelPlayback) else if (playbackMode == Constant::TwoChannelPlayback)
playTwoChannels(); playTwoChannels();
serialPortTool->onPlaybackStart();
return; return;
} }
} }
@ -323,7 +239,7 @@ void Widget::receiveEnter()
* @brief * @brief
* : ==> ==> * : ==> ==>
*/ */
void Widget::receiveReturn() void Widget::onBtnReturnClicked()
{ {
if (menu->isVisible()) { if (menu->isVisible()) {
menu->hide(); menu->hide();
@ -336,8 +252,10 @@ void Widget::receiveReturn()
if (isPlayback) { if (isPlayback) {
// 停止回放 // 停止回放
for (Channel* chn : channelList) { for (Channel* chn : channelList) {
if (chn->state != Channel::Stop) if (chn->state != Channel::Stop) {
chn->startPlayLive(); chn->startPlayLive();
serialPortTool->onPlaybackEnd();
}
} }
progressBar->hide(); progressBar->hide();
ui->lblA->show(); ui->lblA->show();
@ -354,16 +272,36 @@ void Widget::seek(QString type)
return; return;
for (Channel* chn : channelList) { for (Channel* chn : channelList) {
if (chn->state == Channel::Pause || chn->state == Channel::Playback) { if (chn->state == Channel::Pause || chn->state == Channel::Playback) {
if (type == "forward") if (type == "forward") {
chn->forward(); chn->forward();
else serialPortTool->onFoward();
} else {
chn->back(); chn->back();
serialPortTool->onBack();
}
if (chn->channelName == Constant::MainChannel) if (chn->channelName == Constant::MainChannel)
progressBar->showMax(); progressBar->showMax();
} }
} }
} }
/**
* @brief
*/
void Widget::onBtnForwardClicked()
{
seek("forward");
}
/**
* @brief 退
*/
void Widget::onBtnBackClicked()
{
seek("back");
}
/** /**
* @brief HDMI-OUT0口输出 * @brief HDMI-OUT0口输出
*/ */
@ -478,7 +416,17 @@ void Widget::onPlayEnd()
QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curPlayChannel); QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curPlayChannel);
fileList = Tool::getFileList(dirPath); fileList = Tool::getFileList(dirPath);
if (index == fileList.length() - 1) { if (index == fileList.length() - 1) {
Log::info("no file to play"); Log::info("there is no file to play");
// 显示播放完毕的信息
if (playbackMode == Constant::OneChannelPlayback) {
Channel* chn = findChannelByName(Constant::MainChannel);
if (chn)
chn->showFinishPromot();
} else if (playbackMode == Constant::TwoChannelPlayback) {
for (Channel* chn : channelList) {
chn->showFinishPromot();
}
}
return; return;
} }
Log::info("refresh current play file list"); Log::info("refresh current play file list");

View File

@ -5,11 +5,8 @@
#include "Menu.h" #include "Menu.h"
#include "Message.h" #include "Message.h"
#include "ProgressBar.h" #include "ProgressBar.h"
#include <QDebug>
#include <QDir> #include <QDir>
#include <QMap> #include <QMap>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
@ -19,17 +16,6 @@ class Widget;
class Widget : public QWidget { class Widget : public QWidget {
Q_OBJECT Q_OBJECT
enum Command {
CmdPlayback,
CmdNext,
CmdPrevious,
CmdForward,
CmdBack,
CmdPause,
CmdReturn,
CmdEnter
};
public: public:
explicit Widget(QWidget* parent = 0); explicit Widget(QWidget* parent = 0);
~Widget(); ~Widget();
@ -38,21 +24,23 @@ public slots:
void showPlayList(); void showPlayList();
private slots: private slots:
void onSerialError(QSerialPort::SerialPortError error);
void onTimeout();
void onReadyRead();
void onProgressTimeout(); void onProgressTimeout();
void onPlayEnd(); void onPlayEnd();
void onShowRecordLabel(bool show); void onShowRecordLabel(bool show);
void onBtnPlaybackClicked();
void onBtnPreviousClicked();
void onBtnNextClicked();
void onBtnPasueClicked();
void onBtnEnterClicked();
void onBtnReturnClicked();
void onBtnForwardClicked();
void onBtnBackClicked();
private: private:
Ui::Widget* ui; Ui::Widget* ui;
QStringList fileList; QStringList fileList;
QTimer* timer;
QSerialPort* serialPort;
QMap<QString, Command> commandMap;
QTimer* progressTimer; QTimer* progressTimer;
bool isPlayback = false; bool isPlayback = false;
QString curPlayChannel; QString curPlayChannel;
@ -64,20 +52,9 @@ private:
Message* message; Message* message;
private: private:
void initSerialPort();
void initCommandMap();
void receivePlayback();
void receivePrevious();
void receiveNext();
void seek(QString type); void seek(QString type);
void receivePasue();
void receiveEnter();
void receiveReturn();
void playOneChannel(); void playOneChannel();
void playTwoChannels(); void playTwoChannels();
Channel* findChannelByName(QString name); Channel* findChannelByName(QString name);
}; };

View File

@ -4,13 +4,18 @@
#include "Json.h" #include "Json.h"
#include "Link.h" #include "Link.h"
#include "Log.h" #include "Log.h"
#include "SerialPortTool.h"
#include "TcpServer.h" #include "TcpServer.h"
#include "Tool.h"
#include "Widget.h" #include "Widget.h"
#include <QApplication> #include <QApplication>
#include <QDebug>
#include <unistd.h> #include <unistd.h>
// Tcp服务器
TcpServer* server; TcpServer* server;
// 串口工具
SerialPortTool* serialPortTool;
// 视频输出通道
LinkObject* vo; LinkObject* vo;
LinkObject* vo1; LinkObject* vo1;
@ -60,11 +65,21 @@ bool loadConfiguration(QString path)
*/ */
void startRecord() void startRecord()
{ {
// 判断是否挂载了磁盘
QString info = Tool::writeCom(QString("df %1").arg(Constant::MountedPath));
if (!info.contains(Constant::MountedPath)) {
Log::error("there is no disk mounted");
return;
}
// 一路录制只录制HDMI-A通道 // 一路录制只录制HDMI-A通道
if (recordMode == Constant::OneChannelRecord) { if (recordMode == Constant::OneChannelRecord) {
for (Channel* chn : channelList) { for (Channel* chn : channelList) {
if (chn->channelName == Constant::MainChannel) { if (chn->channelName == Constant::MainChannel) {
chn->startRecord(); chn->startRecord();
// 打开录制灯,关闭环路灯
serialPortTool->onRecordStart();
serialPortTool->onLoopOff();
} }
} }
} }
@ -72,8 +87,16 @@ void startRecord()
else if (recordMode == Constant::TwoChannelRecord) { else if (recordMode == Constant::TwoChannelRecord) {
for (Channel* chn : channelList) { for (Channel* chn : channelList) {
chn->startRecord(); chn->startRecord();
// 打开录制灯,关闭环路灯
serialPortTool->onRecordStart();
serialPortTool->onLoopOff();
} }
} }
// 无录制
else {
// 打开环路灯
serialPortTool->onLoopOn();
}
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@ -140,6 +163,12 @@ int main(int argc, char* argv[])
return 0; return 0;
} }
// 串口
serialPortTool = new SerialPortTool();
serialPortTool->open();
// 打开电源灯
serialPortTool->onPowerOn();
// qt界面 // qt界面
Widget w; Widget w;
w.show(); w.show();
@ -149,12 +178,13 @@ int main(int argc, char* argv[])
// 开启Tcp服务 // 开启Tcp服务
server = new TcpServer(); server = new TcpServer();
server->listen(QHostAddress::Any, 8080); server->listen();
Log::info("start tcp server, listen port 8080...");
// 硬盘检测线程,检测硬盘容量 // 硬盘检测线程,检测硬盘容量
CheckStorageThread* thread = new CheckStorageThread(); CheckStorageThread* thread = new CheckStorageThread();
thread->start(); thread->start();
QObject::connect(thread, SIGNAL(diskWillFull()), serialPortTool, SLOT(onDiskWillFull()));
QObject::connect(thread, SIGNAL(diskNotFull()), serialPortTool, SLOT(onDiskNotFull()));
Log::info("start storage check thread..."); Log::info("start storage check thread...");
return a.exec(); return a.exec();