diff --git a/Channel.cpp b/Channel.cpp index 1636a7f..fd0636b 100755 --- a/Channel.cpp +++ b/Channel.cpp @@ -5,6 +5,7 @@ #include #include #include +#include LinkObject* Channel::audioInput = nullptr; LinkObject* Channel::audioOutput = nullptr; @@ -16,10 +17,6 @@ extern LinkObject* vo1; Channel::Channel(QObject* parent) : QObject(parent) { - timer = new QTimer(); - timer->setInterval(duration); - timer->stop(); - videoInput = nullptr; videoOutput = nullptr; videoEncoder = nullptr; @@ -48,8 +45,6 @@ Channel::Channel(QObject* parent) rtspServer = Link::create("Rtsp"); rtspServer->start(); } - - connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout())); } /** @@ -88,8 +83,9 @@ void Channel::init() record = Link::create("Mux"); QVariantMap dataMp4; dataMp4["format"] = "mp4"; - // dataMp4["lowLatency"] = true; - // dataMp4["filecache"] = 20480000; + dataMp4["segmentDuration"] = duration; + dataMp4["lowLatency"] = true; + dataMp4["filecache"] = 20480000; record->setData(dataMp4); videoInput->linkV(videoEncoder)->linkV(record); audioInput->linkA(audioEncoder)->linkA(record); @@ -127,40 +123,20 @@ void Channel::init() 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 开始录制 */ void Channel::startRecord() { - if (!isMountDisk()) { - Log::error("there are no disk mounted"); - return; - } while (int secs = QTime::currentTime().second() % 60 != 0) { } - QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm"); 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; record->start(dataRecord); isRecord = true; Log::info("{} start recording...", channelName.toStdString()); - - timer->start(); emit showRecordState(true); } @@ -171,7 +147,6 @@ void Channel::stopRecord() { Log::info("{} stop recording...", channelName.toStdString()); record->stop(true); - timer->stop(); emit showRecordState(false); } @@ -245,7 +220,6 @@ void Channel::startPlayLive() void Channel::back() { Log::info("{} back 10s", channelName.toStdString()); - qDebug() << channelName << "back 10s"; int curPos = file->invoke("getPosition").toInt(); curPos -= 10 * 1000; file->invoke("seek", curPos); @@ -277,13 +251,15 @@ void Channel::togglePause() } /** - * @brief 判断是否挂载磁盘 - * @return + * @brief 显示播放结束提示 */ -bool Channel::isMountDisk() +void Channel::showFinishPromot() { - QString info = Tool::writeCom(QString("df %1").arg(Constant::MountedPath)); - if (!info.contains(Constant::MountedPath)) - return false; - return true; -} + videoInput->unLinkV(videoDecoder); + QVariantMap dataImage; + dataImage["path"] = Constant::FinishImagePath; + image->setData(dataImage); + image->start(dataImage); + image->linkV(videoOutput); + state = Finish; +} \ No newline at end of file diff --git a/Channel.h b/Channel.h index c796126..b55376a 100755 --- a/Channel.h +++ b/Channel.h @@ -12,7 +12,8 @@ public: Stop, Playback, Pause, - Error + Error, + Finish }; explicit Channel(QObject* parent = nullptr); void init(); @@ -31,7 +32,7 @@ public: LinkObject* record; // 单个视频时长 - int duration = 10 * 60 * 1000; + int duration = 1 * 60 * 1000; bool isRecord = false; LinkObject* file; @@ -55,19 +56,11 @@ public: void startPlayLive(); + void showFinishPromot(); + signals: void playEnd(); void showRecordState(bool state); - -private slots: - void onTimeout(); - -private: - // 用于分段录制的计时器 - QTimer* timer; - -private: - bool isMountDisk(); }; #endif // CHANNEL_H diff --git a/CheckStorageThread.cpp b/CheckStorageThread.cpp index 949493b..eeaf594 100755 --- a/CheckStorageThread.cpp +++ b/CheckStorageThread.cpp @@ -28,6 +28,7 @@ void CheckStorageThread::run() int64_t available = Tool::getAvailableStorage(Constant::MountedPath); if (available < THRESHOLD) { 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)); if (!fileList.isEmpty()) { @@ -52,6 +53,8 @@ void CheckStorageThread::run() Log::error("remove file {} failed", path.toStdString()); } } + } else { + emit diskNotFull(); } QThread::msleep(INTERVAL); } diff --git a/CheckStorageThread.h b/CheckStorageThread.h index ba41d8f..32ba617 100755 --- a/CheckStorageThread.h +++ b/CheckStorageThread.h @@ -8,6 +8,10 @@ class CheckStorageThread : public QThread { public: CheckStorageThread(); +signals: + void diskWillFull(); // 磁盘空间不足 + void diskNotFull(); // 磁盘空间充足 + protected: void run() override; }; diff --git a/Constant.h b/Constant.h index 7ea433e..5588998 100755 --- a/Constant.h +++ b/Constant.h @@ -22,6 +22,7 @@ public: static constexpr char* MountedPath = "/root/usb"; static constexpr char* ErrorImagePath = "/opt/RecordControlApplication/images/error.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* SecondaryChannel = "HDMI-B"; }; diff --git a/Message.cpp b/Message.cpp index 48ec7c4..bdbd8dd 100755 --- a/Message.cpp +++ b/Message.cpp @@ -1,5 +1,4 @@ #include "Message.h" -#include #include #include #include @@ -69,7 +68,6 @@ void Message::showMessage(QString content, int duration, Level level) // apeend new QPushButton* btn = generateButton(content, level); layout->insertWidget(count, btn); - qDebug() << this->children(); QTimer* timer = new QTimer(); timer->setInterval(duration); diff --git a/RecordControlApplication.pro b/RecordControlApplication.pro index 8187c85..98a9e7c 100755 --- a/RecordControlApplication.pro +++ b/RecordControlApplication.pro @@ -32,7 +32,6 @@ SOURCES += \ main.cpp \ Widget.cpp \ TcpServer.cpp \ - TcpController.cpp \ Channel.cpp \ Menu.cpp\ ProgressBar.cpp \ @@ -40,19 +39,28 @@ SOURCES += \ Tool.cpp \ CheckStorageThread.cpp \ Constant.h \ - Log.cpp + Log.cpp \ + TcpRequest.cpp \ + TcpConnectionHandler.cpp \ + TcpRequestHandler.cpp \ + TcpResponse.cpp \ + SerialPortTool.cpp HEADERS += \ Widget.h \ TcpServer.h \ - TcpController.h \ Channel.h \ Menu.h \ ProgressBar.h \ Message.h \ Tool.h \ CheckStorageThread.h \ - Log.h + Log.h \ + TcpRequest.h \ + TcpConnectionHandler.h \ + TcpRequestHandler.h \ + TcpResponse.h \ + SerialPortTool.h FORMS += \ Menu.ui \ diff --git a/SerialPortTool.cpp b/SerialPortTool.cpp new file mode 100644 index 0000000..a7e7100 --- /dev/null +++ b/SerialPortTool.cpp @@ -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(); +} diff --git a/SerialPortTool.h b/SerialPortTool.h new file mode 100644 index 0000000..9ec5627 --- /dev/null +++ b/SerialPortTool.h @@ -0,0 +1,77 @@ +#ifndef SERIALPORT_H +#define SERIALPORT_H +#include +#include +#include +#include + +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 cmdMap; // 指令列表 + QTimer* timer; +}; + +#endif // SERIALPORT_H diff --git a/TcpConnectionHandler.cpp b/TcpConnectionHandler.cpp new file mode 100644 index 0000000..e938d9b --- /dev/null +++ b/TcpConnectionHandler.cpp @@ -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(); +} diff --git a/TcpConnectionHandler.h b/TcpConnectionHandler.h new file mode 100644 index 0000000..56213a2 --- /dev/null +++ b/TcpConnectionHandler.h @@ -0,0 +1,29 @@ +#ifndef TCPCONNECTIONHANDLER_H +#define TCPCONNECTIONHANDLER_H + +#include "TcpRequest.h" +#include "TcpRequestHandler.h" +#include +#include + +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 \ No newline at end of file diff --git a/TcpController.h b/TcpController.h deleted file mode 100755 index 1811f11..0000000 --- a/TcpController.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef TCPCONTROLLER_H -#define TCPCONTROLLER_H - -#include -#include -#include -#include -#include - -using Routes = QMap>; - -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 diff --git a/TcpRequest.cpp b/TcpRequest.cpp new file mode 100644 index 0000000..d566acb --- /dev/null +++ b/TcpRequest.cpp @@ -0,0 +1,73 @@ +#include "TcpRequest.h" +#include "Log.h" +#include + +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; +} diff --git a/TcpRequest.h b/TcpRequest.h new file mode 100644 index 0000000..e423f49 --- /dev/null +++ b/TcpRequest.h @@ -0,0 +1,42 @@ +#ifndef TCPREQUEST_H +#define TCPREQUEST_H + +#include +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 diff --git a/TcpController.cpp b/TcpRequestHandler.cpp similarity index 58% rename from TcpController.cpp rename to TcpRequestHandler.cpp index 217760a..4284602 100755 --- a/TcpController.cpp +++ b/TcpRequestHandler.cpp @@ -1,4 +1,4 @@ -#include "TcpController.h" +#include "TcpRequestHandler.h" #include "Channel.h" #include "Constant.h" #include "Json.h" @@ -9,34 +9,49 @@ extern const QList channelList; -TcpController::TcpController(QObject* parent) +TcpRequestHandler::TcpRequestHandler(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 * @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(); if (params.contains("ip")) @@ -47,7 +62,7 @@ void TcpController::setIpaddr(QTcpSocket* socket) netMap["gateway"] = params.value("gateway"); Json::saveFile(netMap, Constant::NetConfigPath); - socket->write("url=set_ipaddr\r\nstatus=success"); + reponse->success(); Tool::writeCom(Constant::NetScriptPath); } @@ -55,41 +70,41 @@ void TcpController::setIpaddr(QTcpSocket* socket) * @brief 设置盒子的名称 * @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")) { Log::error("setName params error, missing param \"name\""); - socket->write("url=set_name\r\nstatus=error"); + reponse->error("名称不能为空"); return; } QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); cfg["name"] = params.value("name"); Json::saveFile(cfg, Constant::ConfigurationPath); - socket->write("url=set_name\r\nstatus=success"); + reponse->success(); } /** * @brief 获取盒子名称 */ -void TcpController::getName(QTcpSocket* socket) +void TcpRequestHandler::getName(TcpRequest* request, TcpResponse* reponse) { QVariantMap config = Json::loadFile(Constant::ConfigurationPath).toMap(); QString name = config["name"].toString(); QString data = QString("url=get_name\r\nstatus=success\r\nname=%1").arg(name); - socket->write(data.toStdString().data()); + reponse->success("", name); } /** * @brief 设置视频录制参数 * @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")) { Log::error("setVideoEncode params error, missing param \"interface\""); - socket->write("url=set_video_enc\r\nstatus=error"); + reponse->error("缺少接口参数"); return; } @@ -101,7 +116,7 @@ void TcpController::setVideoEnc(QTcpSocket* socket) } if (chn) { // 修改分辨率和帧率 - chn->videoEncoder->setData(params); + // chn->videoEncoder->setData(params); // 更新配置文件 QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); QVariantList interfaces = cfg.value("interface").toList(); @@ -121,10 +136,10 @@ void TcpController::setVideoEnc(QTcpSocket* socket) } cfg["interface"] = interfaces; Json::saveFile(cfg, Constant::ConfigurationPath); - socket->write("url=set_video_enc\r\nstatus=success"); + reponse->success("修改录制参数成功,重启后生效"); } else { 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 获取视频录制参数 * @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")) { QString interface = params.value("interface").toString(); @@ -149,21 +164,11 @@ void TcpController::getVideoEnc(QTcpSocket* socket) targetInterfaceCfg = item; } } - QString width; - QString height; - QString framerate; QVariantMap encodeV = targetInterfaceCfg.value("encV").toMap(); - width = encodeV.value("width").toString(); - 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()); + reponse->success("", encodeV); } else { Log::error("getVideoEnc params error, missing param \"interface\""); - socket->write("url=get_video_enc\r\nstatus=error"); + reponse->error("缺少接口参数"); return; } } @@ -172,12 +177,12 @@ void TcpController::getVideoEnc(QTcpSocket* socket) * @brief 获取文件列表 * @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")) { Log::error("getFileList params error, missing param \"interface\""); - socket->write("url=get_file_list\r\nstatus=error"); + reponse->error("缺少接口参数"); return; } @@ -186,26 +191,24 @@ void TcpController::getFileList(QTcpSocket* socket) QStringList videoList = Tool::getFileList(videoPath); - if (videoList.isEmpty()) { - Log::error("getFileList error, video dir is empty "); - socket->write("url=get_file_list\r\nstatus=error"); - return; - } - QString data; - data = "url=get_file_list\r\nstatus=success\r\n" + videoList.join("\r\n"); - socket->write(data.toStdString().data()); + // 复制两份测试用 + QStringList copy = videoList; + QStringList copy1 = videoList; + videoList.append(copy); + videoList.append(copy1); + reponse->success("获取文件成功", videoList); } /** * @brief 删除文件 * @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")) { Log::error("deleteFile params error, missing params \"interface\" or \"filename\""); - socket->write("url=delete_file\r\nstatus=error"); + reponse->error("缺少端口或文件名"); return; } @@ -219,10 +222,10 @@ void TcpController::deleteFile(QTcpSocket* socket) if (video.exists()) { video.remove(); Log::info("remove video {}", filePath.toStdString()); - socket->write("url=delete_file\r\nstatus=success"); + reponse->success("删除成功"); } else { 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 设置录制模式,一路录制/两路录制,只修改配置文件,重启之后生效 * @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")) { Log::error("setRecordMode params error, missing param \"recordMode\""); - socket->write("url=set_record_mode\r\nstatus=error"); + reponse->error("缺少录制模式参数"); return; } int recordMode = params.value("recordMode").toInt(); @@ -243,60 +246,93 @@ void TcpController::setRecordMode(QTcpSocket* socket) && recordMode != Constant::TwoChannelRecord && recordMode != Constant::NoChannelRecord) { Log::error("setRecordMode params error, unkown param \"recordMode\": {}", recordMode); - socket->write("url=set_record_mode\r\nstatus=error"); + reponse->error("录制参数错误"); return; } + QVariantMap cfg = Json::loadFile(Constant::ConfigurationPath).toMap(); int mode = cfg["recordMode"].toInt(); if (recordMode == mode) { 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; } // 更新配置文件 cfg["recordMode"] = recordMode; Json::saveFile(cfg, Constant::ConfigurationPath); - socket->write("url=set_record_mode\r\nstatus=success"); + reponse->success("设置录制参数成功, 重启后生效"); } /** * @brief 设置回放模式, 一路回放/两路同时回放,只修改配置文件,重启之后生效 * @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")) { Log::error("setPlaybackMode params error, missing param \"playbackmode\""); - socket->write("url=set_playback_mode\r\nstatus=error"); + reponse->error("缺少回放模式参数"); return; } int playbackMode = params.value("playbackMode").toInt(); if (playbackMode != Constant::OneChannelPlayback && playbackMode != Constant::TwoChannelPlayback) { 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(); + 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; Json::saveFile(cfg, Constant::ConfigurationPath); - socket->write("url=set_playback_mode\r\nstatus=success"); + reponse->success("修改回放模式成功,重启后生效"); } /** * @brief 重启程序 * @param socket */ -void TcpController::reboot(QTcpSocket* socket) +void TcpRequestHandler::reboot(TcpRequest* request, TcpResponse* reponse) { // 先停止所有的录制 for (Channel* chn : channelList) { chn->stopRecord(); } - socket->write("url=reboot\r\nstatus=success"); - socket->flush(); + reponse->success("重启成功"); // 退出程序,等待后台运行的shell脚本重启程序 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("时间同步成功"); + } } \ No newline at end of file diff --git a/TcpRequestHandler.h b/TcpRequestHandler.h new file mode 100755 index 0000000..fdb2000 --- /dev/null +++ b/TcpRequestHandler.h @@ -0,0 +1,35 @@ +#ifndef TCPCONTROLLER_H +#define TCPCONTROLLER_H + +#include "TcpRequest.h" +#include "TcpResponse.h" +#include +#include + +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 diff --git a/TcpResponse.cpp b/TcpResponse.cpp new file mode 100644 index 0000000..38e89fb --- /dev/null +++ b/TcpResponse.cpp @@ -0,0 +1,67 @@ +#include "TcpResponse.h" +#include + +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(); +} \ No newline at end of file diff --git a/TcpResponse.h b/TcpResponse.h new file mode 100644 index 0000000..ec25512 --- /dev/null +++ b/TcpResponse.h @@ -0,0 +1,20 @@ +#ifndef TCPRESPONSE_H +#define TCPRESPONSE_H + +#include + +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 \ No newline at end of file diff --git a/TcpServer.cpp b/TcpServer.cpp index ebe400c..2247abd 100755 --- a/TcpServer.cpp +++ b/TcpServer.cpp @@ -5,10 +5,46 @@ TcpServer::TcpServer(QObject* parent) : QTcpServer(parent) { - controller = new TcpController(); + handler = new TcpRequestHandler(); + 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 服务器有新的连接 */ @@ -17,33 +53,13 @@ void TcpServer::onNewConnect() if (this->hasPendingConnections()) { QTcpSocket* socket = this->nextPendingConnection(); Log::info("new client connected, ip: {}", socket->peerAddress().toString().toStdString()); - clients.push_back(socket); - connect(socket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead); - connect(socket, &QTcpSocket::disconnected, [=] { - QTcpSocket* sk = static_cast(sender()); - Log::info(" one client disconnect..."); - clients.removeOne(sk); + TcpConnectionHandler* connection = new TcpConnectionHandler(socket, handler); + clients.push_back(connection); + + connect(connection, &TcpConnectionHandler::disconnected, [=] { + // 从列表中移除该指针 + TcpConnectionHandler* conn = static_cast(sender()); + clients.removeOne(conn); }); } } - -/** - * @brief 从缓冲区读取数据 - */ -void TcpServer::onReadyRead() -{ - QTcpSocket* socket = static_cast(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()); - } - } -} diff --git a/TcpServer.h b/TcpServer.h index 96e5706..7133176 100755 --- a/TcpServer.h +++ b/TcpServer.h @@ -1,7 +1,8 @@ #ifndef TCPSERVER_H #define TCPSERVER_H -#include "TcpController.h" +#include "TcpConnectionHandler.h" +#include "TcpRequestHandler.h" #include class TcpServer : public QTcpServer { @@ -9,14 +10,16 @@ class TcpServer : public QTcpServer { public: TcpServer(QObject* parent = nullptr); + ~TcpServer(); + void listen(); + void close(); private slots: - void onReadyRead(); void onNewConnect(); private: - TcpController* controller; - QList clients; + QList clients; + TcpRequestHandler* handler; }; #endif // TCPSERVER_H diff --git a/Tool.cpp b/Tool.cpp index 2c37bf5..b8a5302 100755 --- a/Tool.cpp +++ b/Tool.cpp @@ -29,6 +29,7 @@ QStringList Tool::getFileList(QString path) nameFilters << "*.mp4"; dir.setNameFilters(nameFilters); fileList = dir.entryList(); + return fileList; } diff --git a/Widget.cpp b/Widget.cpp index 0671c25..814491c 100755 --- a/Widget.cpp +++ b/Widget.cpp @@ -3,6 +3,7 @@ #include "Constant.h" #include "Json.h" #include "Log.h" +#include "SerialPortTool.h" #include "TcpServer.h" #include "Tool.h" #include "ui_Widget.h" @@ -18,6 +19,7 @@ QString curFilename; extern QList channelList; extern Constant::PlaybackMode playbackMode; +extern SerialPortTool* serialPortTool; Widget::Widget(QWidget* parent) : QWidget(parent) @@ -46,12 +48,6 @@ Widget::Widget(QWidget* parent) ui->listWidget->verticalScrollBar()->hide(); ui->listWidget->hide(); - // 初始化串口 - initSerialPort(); - - // 初始化指令列表 - initCommandMap(); - // 每5秒更新一次进度条 progressTimer = new QTimer(); progressTimer->setInterval(500); @@ -60,47 +56,51 @@ Widget::Widget(QWidget* parent) for (Channel* chn : channelList) { connect(chn, SIGNAL(playEnd()), this, SLOT(onPlayEnd())); 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() { - serialPort->close(); delete ui; - delete timer; delete progressTimer; delete menu; delete progressBar; 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 获取视频文件列表并显示 */ @@ -118,21 +118,6 @@ void Widget::showPlayList() 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 更新进度条 */ @@ -143,80 +128,10 @@ void Widget::onProgressTimeout() 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 收到回放指令 */ -void Widget::receivePlayback() +void Widget::onBtnPlaybackClicked() { Log::info("receive command playback, playback mode: {}", playbackMode); // 如果是一路回放,显示选择菜单 @@ -234,7 +149,7 @@ void Widget::receivePlayback() /** * @brief 收到上段指令 */ -void Widget::receivePrevious() +void Widget::onBtnPreviousClicked() { // 菜单显示则切换菜单选中 if (menu->isVisible()) { @@ -249,14 +164,12 @@ void Widget::receivePrevious() ui->listWidget->setCurrentRow(curRow - 1); return; } - if (isPlayback) { - } } /** * @brief 收到下段指令 */ -void Widget::receiveNext() +void Widget::onBtnNextClicked() { if (menu->isVisible()) { menu->setCurChannel(Constant::SecondaryChannel); @@ -270,14 +183,12 @@ void Widget::receiveNext() ui->listWidget->setCurrentRow(curRow + 1); return; } - if (isPlayback) { - } } /** * @brief 收到暂停指令 */ -void Widget::receivePasue() +void Widget::onBtnPasueClicked() { if (!isPlayback) return; @@ -288,8 +199,12 @@ void Widget::receivePasue() progressBar->showMax(); if (chn->state == Channel::Pause) { progressBar->setState(ProgressBar::Pause); + // 打开暂停灯 + serialPortTool->onPlayPause(); } else { progressBar->setState(ProgressBar::Play); + // 关闭暂停灯 + serialPortTool->onPlayPause(); } } } @@ -299,7 +214,7 @@ void Widget::receivePasue() /** * @brief 收到确定指令 */ -void Widget::receiveEnter() +void Widget::onBtnEnterClicked() { // 菜单显示,则关闭菜单显示列表 if (menu->isVisible()) { @@ -315,6 +230,7 @@ void Widget::receiveEnter() playOneChannel(); else if (playbackMode == Constant::TwoChannelPlayback) playTwoChannels(); + serialPortTool->onPlaybackStart(); return; } } @@ -323,7 +239,7 @@ void Widget::receiveEnter() * @brief 收到返回指令 * 处理优先级: 菜单 ==> 列表 ==> 回放 */ -void Widget::receiveReturn() +void Widget::onBtnReturnClicked() { if (menu->isVisible()) { menu->hide(); @@ -336,8 +252,10 @@ void Widget::receiveReturn() if (isPlayback) { // 停止回放 for (Channel* chn : channelList) { - if (chn->state != Channel::Stop) + if (chn->state != Channel::Stop) { chn->startPlayLive(); + serialPortTool->onPlaybackEnd(); + } } progressBar->hide(); ui->lblA->show(); @@ -354,16 +272,36 @@ void Widget::seek(QString type) return; for (Channel* chn : channelList) { if (chn->state == Channel::Pause || chn->state == Channel::Playback) { - if (type == "forward") + if (type == "forward") { chn->forward(); - else + serialPortTool->onFoward(); + } else { chn->back(); + serialPortTool->onBack(); + } + if (chn->channelName == Constant::MainChannel) progressBar->showMax(); } } } +/** + * @brief 快进 + */ +void Widget::onBtnForwardClicked() +{ + seek("forward"); +} + +/** + * @brief 快退 + */ +void Widget::onBtnBackClicked() +{ + seek("back"); +} + /** * @brief 一路回放,将当前选中播放的视频从HDMI-OUT0口输出 */ @@ -478,7 +416,17 @@ void Widget::onPlayEnd() QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curPlayChannel); fileList = Tool::getFileList(dirPath); 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; } Log::info("refresh current play file list"); diff --git a/Widget.h b/Widget.h index 8ca6e38..c2fae94 100755 --- a/Widget.h +++ b/Widget.h @@ -5,11 +5,8 @@ #include "Menu.h" #include "Message.h" #include "ProgressBar.h" -#include #include #include -#include -#include #include #include @@ -19,17 +16,6 @@ class Widget; class Widget : public QWidget { Q_OBJECT - enum Command { - CmdPlayback, - CmdNext, - CmdPrevious, - CmdForward, - CmdBack, - CmdPause, - CmdReturn, - CmdEnter - }; - public: explicit Widget(QWidget* parent = 0); ~Widget(); @@ -38,21 +24,23 @@ public slots: void showPlayList(); private slots: - void onSerialError(QSerialPort::SerialPortError error); - void onTimeout(); - void onReadyRead(); void onProgressTimeout(); void onPlayEnd(); void onShowRecordLabel(bool show); + void onBtnPlaybackClicked(); + void onBtnPreviousClicked(); + void onBtnNextClicked(); + void onBtnPasueClicked(); + void onBtnEnterClicked(); + void onBtnReturnClicked(); + void onBtnForwardClicked(); + void onBtnBackClicked(); + private: Ui::Widget* ui; QStringList fileList; - QTimer* timer; - QSerialPort* serialPort; - QMap commandMap; - QTimer* progressTimer; bool isPlayback = false; QString curPlayChannel; @@ -64,20 +52,9 @@ private: Message* message; private: - void initSerialPort(); - void initCommandMap(); - - void receivePlayback(); - void receivePrevious(); - void receiveNext(); void seek(QString type); - void receivePasue(); - void receiveEnter(); - void receiveReturn(); - void playOneChannel(); void playTwoChannels(); - Channel* findChannelByName(QString name); }; diff --git a/main.cpp b/main.cpp index 3615fe5..923bb8a 100755 --- a/main.cpp +++ b/main.cpp @@ -4,13 +4,18 @@ #include "Json.h" #include "Link.h" #include "Log.h" +#include "SerialPortTool.h" #include "TcpServer.h" +#include "Tool.h" #include "Widget.h" #include -#include #include +// Tcp服务器 TcpServer* server; +// 串口工具 +SerialPortTool* serialPortTool; +// 视频输出通道 LinkObject* vo; LinkObject* vo1; @@ -60,11 +65,21 @@ bool loadConfiguration(QString path) */ 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通道 if (recordMode == Constant::OneChannelRecord) { for (Channel* chn : channelList) { if (chn->channelName == Constant::MainChannel) { chn->startRecord(); + // 打开录制灯,关闭环路灯 + serialPortTool->onRecordStart(); + serialPortTool->onLoopOff(); } } } @@ -72,8 +87,16 @@ void startRecord() else if (recordMode == Constant::TwoChannelRecord) { for (Channel* chn : channelList) { chn->startRecord(); + // 打开录制灯,关闭环路灯 + serialPortTool->onRecordStart(); + serialPortTool->onLoopOff(); } } + // 无录制 + else { + // 打开环路灯 + serialPortTool->onLoopOn(); + } } int main(int argc, char* argv[]) @@ -140,6 +163,12 @@ int main(int argc, char* argv[]) return 0; } + // 串口 + serialPortTool = new SerialPortTool(); + serialPortTool->open(); + // 打开电源灯 + serialPortTool->onPowerOn(); + // qt界面 Widget w; w.show(); @@ -149,12 +178,13 @@ int main(int argc, char* argv[]) // 开启Tcp服务 server = new TcpServer(); - server->listen(QHostAddress::Any, 8080); - Log::info("start tcp server, listen port 8080..."); + server->listen(); // 硬盘检测线程,检测硬盘容量 CheckStorageThread* thread = new CheckStorageThread(); thread->start(); + QObject::connect(thread, SIGNAL(diskWillFull()), serialPortTool, SLOT(onDiskWillFull())); + QObject::connect(thread, SIGNAL(diskNotFull()), serialPortTool, SLOT(onDiskNotFull())); Log::info("start storage check thread..."); return a.exec();