/* 本程序提供以下功能: 1.开启4G模块,允许上网 2.进行RTSP推流 3.初始化验证->启动系统服务 */ #include #include #include #include #include #include #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 isRunning(true); // 全局运行标志 std::atomic 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 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 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(); if (jv.is_string()) { try { return std::stod(jv.get()); } 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 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 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(); if (conf.contains("flip")) media.flip = conf["mirror"].get(); if (conf.contains("occlusion")) media.occlusion = conf["occlusion"].get(); } 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().find("高电平") != string::npos) ? true : false; if (conf.contains("trigger_input_mode")) algor.inPutMode = (conf["trigger_input_mode"].get().find("高电平") != string::npos) ? true : false; } } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } }