Files
RkApp/ApCreate/src/main.cpp
2025-09-28 16:31:50 +08:00

565 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
本程序提供以下功能:
1.开启4G模块,允许上网
2.进行RTSP推流
3.初始化验证->启动系统服务
*/
#include <iostream>
#include <cstdlib>
#include <string>
#include <thread>
#include <boost/process.hpp>
#include <nlohmann/json.hpp>
#include "Netra.hpp"
#include "NetRequest.hpp"
#include "encrypt.hpp"
using namespace std;
using namespace QCL;
using namespace ntq;
using namespace encrypt;
namespace bp = boost::process;
bp::child rtsp_proc; // 服务器进程
bp::child video_proc; // 视频推流进程
bp::child ap_proc; // 创建热点进程
bp::child net_proc; // 开启网络进程
// 文件设置路径
string filepath = "/home/orangepi/InitAuth/conf/.env";
string cameraPath = "/opt/rknn-yolov11/.env";
string passwd = "/home/orangepi/InitAuth/pwd/.env";
string url = "http://116.147.36.110:8095/device/validateDevice";
std::atomic<bool> isRunning(true); // 全局运行标志
std::atomic<bool> confirm(true); // 发送卡和ID
// 媒体对象
struct Media
{
bool mirror; // 镜像
bool flip; // 翻转
bool occlusion; // 遮挡
} media{};
// 输入输出对象
struct OutSignal
{
bool outPutMode; // 警报输出电平 true--高电平,false -- 低电平
bool inPutMode; // 触发输入模式 true--高电平,false -- 低电平
} algor{};
// 确认是否已经进行过认证
bool ConfirmInit();
// 获取cpu序列号和SIM卡号
string GetCardInfo();
// 获取SIM卡号
string GetSimICCID(const string &tty = "/dev/ttyUSB2");
// 发送cpu序列号和SIM卡号
void SendCardInfo(TcpServer *MyServer);
// 进行验证
bool verification();
// 解析接收的数据
template <class T>
void CalculateInfo(T &conf, const string &json);
// 创建服务器
TcpServer *MyServer;
// 打开RSTP服务器
void OpenRTSP();
// 视频推流
void VideoStream();
// 接收数据
void ReceiveData();
// 退出信号捕捉
void Exit(int sig);
// 开启网络(4G模块)
void StartNet();
// 开启服务
void StartService();
/*
parames:
argv[1] - SSID of the WiFi network to connect to
argv[2] - Password for the WiFi network (if applicable)
*/
int main(int argc, char *argv[])
{
// 开启4G模块
StartNet();
this_thread::sleep_for(chrono::seconds(2));
// 关闭全部信号
blockAllSignals();
signal(SIGINT, Exit); // 捕获Ctrl+C信号
// 开启服务器
MyServer = new TcpServer(8848);
MyServer->start(); // 不会阻塞
// 如果没有进行过初始化,发送物联网卡信息进行认证
if (ConfirmInit() == false)
{
SendCardInfo(MyServer);
}
else
{
if (verification() == false)
{
cerr << "验证失败" << endl;
return 0;
}
}
//开启服务
thread(StartService).detach();
thread Rtsp(OpenRTSP);
std::this_thread::sleep_for(chrono::seconds(2)); // 等待RTSP服务器启动
// 进行RTSP视频推流
thread video(VideoStream);
// 作为服务端进行数据的收发
thread Processing(ReceiveData);
// 等待热点创建线程结束
Processing.join();
video.join();
Rtsp.join();
return 0;
}
// 开启服务
void StartService()
{
string commnd = "echo 'orangepi' | sudo -S ../../StartService/bin/start start";
system(commnd.c_str());
}
void StartNet()
{
string commd = "../../GetNet/bin/setNet";
net_proc = bp::child("/bin/bash", bp::args = {"-c", commd});
}
// 进行验证
bool verification()
{
// 读取文件密文
ReadFile *rf = new ReadFile(passwd);
bool flag = false;
auto pass = rf->ReadLines();
// 若为空:改写初始化标志位false,重认证
if (pass.empty())
{
WriteFile *wf = new WriteFile(filepath);
wf->overwriteAtPos("no", wf->countBytesPattern("InitOrNot:", true), 3);
return 0;
}
for (auto &ii : pass)
{
// 不为空,线上发送密文开始认证
auto pos = format(R"({{"cardNo":"{}"}})", ii);
auto res = NetRequest::QuickPostJson(url, pos);
int code = -1;
if (res)
{
try
{
code = nlohmann::json::parse(res->body).value("code", -1);
}
catch (const std::exception &e)
{
std::cerr << "JSON解析失败: " << e.what() << std::endl;
}
}
if (res && code == 200)
{
cout << "线上:认证成功" << endl;
flag = true;
}
else
{
try
{
// 进行线下认证
// 获取cpu序列号和SIM卡号
string CardID = GetCardInfo();
string SIMID = GetSimICCID();
string info = CardID + SIMID;
// 进行MD5加密
string md5 = MD5(info);
cout << md5 << endl;
if (md5.compare(ii) == 0)
{
cout << "线下:认证成功" << endl;
flag = true;
}
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
}
}
}
cout << "结束" << endl;
return flag;
}
// 退出信号
void Exit(int sig)
{
cout << "Exiting..." << endl;
isRunning = false;
confirm = false;
MyServer->stop();
system("sudo killall create_ap");
// 杀死后台 RTSP 和 ffmpeg 进程
if (rtsp_proc.running())
rtsp_proc.terminate();
if (video_proc.running())
video_proc.terminate();
if (ap_proc.running())
ap_proc.terminate();
if (net_proc.running())
net_proc.terminate();
}
// 接收数据
void ReceiveData()
{
string buffer = "";
while (isRunning)
{
std::vector<int> client = MyServer->getClientSockets();
if (client.empty())
{
this_thread::sleep_for(chrono::milliseconds(100));
continue;
}
int index = client.size();
for (int ii = 0; ii < index; ii++)
{
buffer = MyServer->receiveFromClient(client[ii], false); // 非阻塞模式接受数据
if (buffer.empty() == false)
{
cout << "已收到" << buffer << endl;
// 处理数据
if (buffer.find("Pass:") != string::npos)
{
cout << "已收到" << buffer << endl;
// 验证秘钥
WriteFile wf(filepath);
string str = "yse\n";
wf.overwriteAtPos(str, wf.countBytesPattern("InitOrNot:", true), str.size());
cout << "已更新" << endl;
// 将秘钥写入配置文件:
WriteFile wf2(passwd);
wf2.overwriteText(buffer.substr(5));
}
else if (buffer.find("SET_DISTANCES") != string::npos)
{ // 接收距离参数
auto res = nlohmann::json::parse(buffer);
// 读取值并兼容 number/string
auto &danger_json = res["params"]["danger_distance"];
auto &warning_json = res["params"]["warning_distance"];
auto &safe_json = res["params"]["safe_distance"];
auto toDouble = [](const nlohmann::json &jv) -> double
{
if (jv.is_number())
return jv.get<double>();
if (jv.is_string())
{
try
{
return std::stod(jv.get<string>());
}
catch (...)
{
return 0.0;
}
}
return 0.0;
};
double danger = toDouble(danger_json);
double warn = toDouble(warning_json);
double safe = toDouble(safe_json);
// 整文件读取,逐行替换,保持注释不变
ReadFile rf(cameraPath);
if (!rf.Open())
{
cerr << "文件打开失败: " << cameraPath << "\n";
}
else
{
auto lines = rf.ReadLines();
for (auto &line : lines)
{
if (line.rfind("NEAR_THRESHOLD=", 0) == 0)
{
line = format("NEAR_THRESHOLD={}", danger);
}
else if (line.rfind("MID_THRESHOLD=", 0) == 0)
{
line = format("MID_THRESHOLD={}", warn);
}
else if (line.rfind("MAX_DISTANCE=", 0) == 0)
{
line = format("MAX_DISTANCE={}", safe);
}
}
// 重新拼接并整体写回
string out;
out.reserve(4096);
for (size_t i = 0; i < lines.size(); ++i)
{
out += lines[i];
if (i + 1 < lines.size())
out += "\n";
}
WriteFile wf(cameraPath);
wf.overwriteText(out);
}
}
else
{
// 写摄像头画面设置
string conf = "";
if (buffer.find("media") != string::npos)
{ // 写摄像头参数
cout << buffer << endl;
WriteFile cam(cameraPath);
CalculateInfo(media, buffer);
conf = format("\nMEDIA_MIRROR={}\nMEDIA_FLIP={}\nMEDIA_OCCLUSION={}\n", media.mirror == 1 ? "true" : "false", media.flip == 1 ? "true" : "false", media.occlusion == 1 ? "true" : "false");
cout << conf;
cam.writeAfterPatternOrAppend("***---***", conf);
}
if (buffer.find("algorithm") != string::npos)
{
// 写输入输出参数
WriteFile mode(filepath);
int pos = mode.countBytesPattern("***---***", true);
CalculateInfo(algor, buffer);
conf = format("\noutPutMode:{}\ninPutMode:{}", algor.outPutMode == 1 ? "true" : "false", algor.inPutMode == 1 ? "true" : "false");
mode.writeAfterPatternOrAppend("***---***", conf);
}
}
}
}
this_thread::sleep_for(chrono::milliseconds(100)); // 避免过于频繁的循环
}
}
// 打开RSTP服务器
void OpenRTSP()
{
string commnd = "../RTSPServer/mediamtx ../RTSPServer/mediamtx.yml";
rtsp_proc = bp::child("/bin/bash", bp::args = {"-c", commnd});
}
// 视频推流
void VideoStream()
{
string commnd = "ffmpeg -f v4l2 -i /dev/video0 -c:v h264_rkmpp -rtsp_transport tcp -f rtsp rtsp://192.168.12.1:8554/stream";
video_proc = bp::child("/bin/bash", bp::args = {"-c", commnd});
}
// 确认是否已经进行过认证
bool ConfirmInit()
{
ReadFile *rf = new ReadFile(filepath);
if (rf->Open() == false)
{
cerr << "文件打开失败\n";
return false;
}
auto vct = rf->ReadLines();
for (auto &ii : vct)
{
if (ii.find("InitOrNot:no") != string::npos)
return false;
}
return true;
}
// 获取cpu序列号和SIM卡号
string GetCardInfo()
{
ReadFile *rf = new ReadFile("/proc/cpuinfo");
if (rf->Open() == false)
{
cerr << "文件打开失败\n";
return "";
}
auto lines = rf->ReadLines();
string res = "";
for (auto &ii : lines)
{
if (ii.find("Serial") != string::npos)
{
auto pos = ii.find(":");
if (pos != string::npos)
{
res = ii.substr(pos + 1);
break;
}
}
}
return LRtrim(res);
}
// 获取SIM卡号
// 通过串口发送 AT+CCID 指令读取并解析返回的ICCID号
string GetSimICCID(const string &tty)
{
int retry = 0; // 重试次数
while (retry < 5) // 最多重试5次
{
int fd = open(tty.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // 打开串口
if (fd < 0)
{
std::cerr << "无法打开串口: " << tty << std::endl;
return "";
}
// 配置串口参数
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag |= (CLOCAL | CREAD);
tcsetattr(fd, TCSANOW, &options);
// 发送 AT+CCID 指令
write(fd, "AT+CCID\r\n", 9);
std::string result;
char buf[256] = {0};
// 循环多次读取,拼接内容,防止数据分包
for (int i = 0; i < 10; ++i)
{
usleep(100000); // 每次等待100ms
int n = read(fd, buf, sizeof(buf) - 1);
if (n > 0)
result.append(buf, n);
}
close(fd); // 关闭串口
// 检查是否有ERROR若有则重试
if (result.find("ERROR") != std::string::npos)
{
retry++;
usleep(200000); // 等待200ms再重试
continue;
}
// 用正则提取ICCID+CCID: 后面的数字)
std::smatch m;
std::regex reg(R"(\+CCID:\s*([0-9]+))");
if (std::regex_search(result, m, reg))
return m[1];
// 兜底直接找19~22位数字兼容不同长度的ICCID
std::regex reg2(R"((\d{19,22}))");
if (std::regex_search(result, m, reg2))
return m[1];
// 没有ERROR但也没读到ICCID直接退出循环
break;
}
return ""; // 多次重试失败,返回空
}
// 发送cpu序列号和SIM卡号
void SendCardInfo(TcpServer *MyServer)
{
string CardID = GetCardInfo();
string SIMID = GetSimICCID();
string info = CardID + SIMID;
while (confirm)
{
std::vector<int> client = MyServer->getClientSockets();
if (client.empty())
continue;
cout << "有了客户端连接" << endl;
size_t index = client.size();
for (int ii = 0; ii < index; ii++)
{
if (MyServer->receiveFromClient(client[ii]) == "GET_ID")
{
cout << info << endl;
MyServer->sendToClient(client[ii], info + " ");
}
}
break;
}
}
// 解析接收的数据
template <class T>
void CalculateInfo(T &conf, const string &json)
{
try
{
/* code */
auto j = nlohmann::json::parse(json);
if (j.contains("params") && j["params"].contains("media"))
{
auto conf = j["params"]["media"];
if (conf.contains("mirror"))
media.mirror = conf["mirror"].get<bool>();
if (conf.contains("flip"))
media.flip = conf["mirror"].get<bool>();
if (conf.contains("occlusion"))
media.occlusion = conf["occlusion"].get<bool>();
}
else if (j.contains("params") && j["params"].contains("algorithm"))
{
auto conf = j["params"]["algorithm"];
if (conf.contains("alarm_output_mode"))
algor.outPutMode = (conf["alarm_output_mode"].get<string>().find("高电平") != string::npos) ? true : false;
if (conf.contains("trigger_input_mode"))
algor.inPutMode = (conf["trigger_input_mode"].get<string>().find("高电平") != string::npos) ? true : false;
}
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
}
}