Files
RkApp/ApCreate/src/main.cpp

565 lines
16 KiB
C++
Raw Normal View History

2025-09-28 16:03:54 +08:00
/*
:
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;
}
}
2025-09-28 16:31:50 +08:00
//开启服务
2025-09-28 16:03:54 +08:00
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()
{
2025-09-28 16:31:50 +08:00
string commnd = "echo 'orangepi' | sudo -S ../../StartService/bin/start start";
2025-09-28 16:03:54 +08:00
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';
}
}