/* 本程序用于 设备出厂获取 唯一标识符进行 wifi设置和修改 向云端发送访问请求(ICCID号),并显示请求次数 接收云端传传来的标识符,并保存至本地,根据表示符进行wifi设置 第二次启动时,自动检测是否已进行初始化,若是,直接从配置文件中获取唯一标识符 */ #include #include #include #include //用于解析JSON字符串 #include #include #include "NetRequest.hpp" #include "Netra.hpp" #include #include using namespace std; using namespace QCL; using namespace ntq; using namespace chrono_literals; namespace bp = boost::process; const string filePath = "/home/orangepi/RKApp/InitAuth/conf/.env"; // 请求唯一标识符接口路径 const string UUIDPost = "http://116.147.36.110:8095/device/requestQrcode"; const size_t MAXSIZE = 1024 * 1024; // 保存ICCID string ICCID = ""; string UUID; // 保存唯一标识 int PostCount = 0; // 请求次数 mutex fileMutex; // 文件锁 mutex logMutex; // 日志锁 atomic g_Device_pid{0}; // 检测是否已进行初始化 bool checkInit(); // 获取标识符并保存至配置文件中 bool GetSignID(); // 设置wifi热点:名称,密码 void setApNet(const string &name, const string &pwd); // 读取配置文件,并获取表示符 void ReadUUID(); // 启动设备激活程序,等待用户激活 void startActiveWare(); // 退出信号处理 void forwardSigint(int); // 获取SIM卡号 string GetSimICCID(const string &tty = "/dev/ttyUSB2"); // 清理日志 void clearLog(); int main() { // 自检 if (checkInit()) { // 已进行初始化 ReadUUID(); setApNet(UUID, UUID); // 启动激活程序 startActiveWare(); return 0; } else { // 未进行初始化 if (GetSignID()) { // 获取标识符成功 setApNet(UUID, UUID); startActiveWare(); return 0; } cerr << "获取标识符失败,请重试" << endl; return -1; } return 0; } // 清理日志 void clearLog() { lock_guard lk(logMutex); string logpath = "/home/orangepi/RKApp/Identification/src/nohup.out"; size_t length = filesystem::file_size(logpath); if (length >= MAXSIZE) { // 清空日志 ofstream ofs(logpath, ios::trunc); } } // 退出信号处理 void forwardSigint(int) { pid_t pgid = g_Device_pid.load(); if (pgid > 0) { kill(-pgid, SIGINT); } } // 获取SIM卡号 // 通过串口发送 AT+CCID 指令,读取并解析返回的ICCID号 string GetSimICCID(const string &tty) { int retry = 0; while (retry < 5) { // 非阻塞打开,避免因 VMIN/VTIME 卡死 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); options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &= ~OPOST; options.c_iflag &= ~(IXON | IXOFF | IXANY); options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 5; // 0.5s tcsetattr(fd, TCSANOW, &options); auto send_and_read = [&](const char *cmd) -> std::string { // 清空缓冲并发送 tcflush(fd, TCIOFLUSH); write(fd, cmd, strlen(cmd)); std::string result; char buf[256] = {0}; // 轮询读取,累计约2秒 for (int i = 0; i < 20; ++i) { int n = read(fd, buf, sizeof(buf)); if (n > 0) result.append(buf, n); usleep(100000); } return result; }; // 先试 AT+QCCID,失败再试 AT+CCID std::string result = send_and_read("AT+QCCID\r\n"); if (result.find("+QCCID") == std::string::npos) { std::string r2 = send_and_read("AT+CCID\r\n"); if (!r2.empty()) result += r2; } close(fd); // 打印原始回应便于调试 std::string debug = result; // 清理换行 debug.erase(std::remove_if(debug.begin(), debug.end(), [](unsigned char c) { return c == '\r' || c == '\n'; }), debug.end()); // std::cout << "ICCID原始回应: " << debug << std::endl; // 错误重试 if (result.find("ERROR") != std::string::npos) { retry++; usleep(200000); continue; } // 解析(支持字母数字) std::smatch m; // +QCCID 或 +CCID 后取字母数字 std::regex reg(R"(\+(?:Q)?CCID:\s*([0-9A-Za-z]+))"); if (std::regex_search(debug, m, reg)) { std::string iccid = m[1]; // 去掉尾部OK或非字母数字 while (!iccid.empty() && !std::isalnum(static_cast(iccid.back()))) iccid.pop_back(); if (iccid.size() >= 2 && iccid.substr(iccid.size() - 2) == "OK") iccid.erase(iccid.size() - 2); return iccid; } // 兜底:19~22位的字母数字(如尾部含 D) std::regex reg2(R"(([0-9A-Za-z]{19,22}))"); if (std::regex_search(debug, m, reg2)) { std::string iccid = m[1]; while (!iccid.empty() && !std::isalnum(static_cast(iccid.back()))) iccid.pop_back(); if (iccid.size() >= 2 && iccid.substr(iccid.size() - 2) == "OK") iccid.erase(iccid.size() - 2); return iccid; } // 进一步兜底:手工截取 +QCCID: / +CCID: 后的连续字母数字 auto parse_after = [&](const std::string &s, const std::string &key) -> std::string { size_t pos = s.find(key); if (pos == std::string::npos) return ""; pos += key.size(); while (pos < s.size() && std::isspace(static_cast(s[pos]))) pos++; size_t start = pos; while (pos < s.size() && std::isalnum(static_cast(s[pos]))) pos++; std::string iccid = (pos > start) ? s.substr(start, pos - start) : ""; if (iccid.size() >= 2 && iccid.substr(iccid.size() - 2) == "OK") iccid.erase(iccid.size() - 2); return iccid; }; { std::string iccid = parse_after(debug, "+QCCID:"); if (iccid.empty()) iccid = parse_after(debug, "+CCID:"); if (!iccid.empty()) return iccid; } retry++; usleep(200000); } return ""; } // 检测是否已进行初始化 bool checkInit() { bool isInit = true; // 上锁,防止竞争 lock_guard lk(fileMutex); // 读取配置文件 ReadFile rf(filePath); if (rf.Open() == false) { cerr << "文件打开失败" << endl; isInit = false; } auto lines = rf.ReadLines(); for (auto &line : lines) { if (line.find("UUID:null") != string::npos) { // 未初始化 isInit = false; } } rf.Close(); return isInit; } // 获取标识符并保存至配置文件中 bool GetSignID() { // 获取成功状态 bool getState = true; bool isRunning = true; // ICCI获取 ICCID = GetSimICCID(); // cout << "ICCID = " << ICCID << endl; if (ICCID.empty()) { cerr << "获取ICCID失败,请检查SIM卡" << endl; return false; } int postCount = 0; int reTryTimes = 0; string RequestBody = format(R"({"cardNo":"{}"})", ICCID); // cout << "RequestBody = " << RequestBody << endl; while (isRunning) { // 发送请求 // cout << "RequestBody = " << RequestBody << endl; auto res = NetRequest::QuickPostJson(UUIDPost, RequestBody); int code = res->status; if (res->status == 200) { // 请求成功 ++postCount; auto json = nlohmann::json::parse(res->body); // cout << "json = " << json << endl; if (json["code"] == 601) { system("clear"); cout << "ICCID:" << ICCID << ",已发送请求次数 " << postCount << " 次" << endl; } else if (json["code"] == 200) { auto qrcode = json["data"]["qrcode"]; UUID = qrcode; // 保存至配置文件中 { lock_guard lk(fileMutex); ReadFile rf(filePath); if (!rf.Open()) return false; auto lines = rf.ReadLines(); for (auto &line : lines) { if (line.find("UUID:null") != string::npos) { line = format("UUID:{}", qrcode); cout << line << endl; } } string out; out.reserve(4096); for (size_t i = 0; i < lines.size(); ++i) { out += lines[i]; if (i + 1 < lines.size()) out += "\n"; } rf.Close(); WriteFile wf(filePath); wf.overwriteText(out); isRunning = false; } } else if (json["code"] == 500) { cout << "申请失败,请联系管理员,ICCID = " << ICCID << endl; } } else { cerr << "请检查网络连接" << endl; ++reTryTimes; if (reTryTimes > 5) { // 重试五次 getState = false; isRunning = false; } } // 间隔两秒 this_thread::sleep_for(2s); } return getState; } // 设置wifi热点:名称,密码 void setApNet(const string &name, const string &pwd) { // 清空nohup.out clearLog(); // 确保无线接口启用 system("sudo rfkill unblock all"); system("sudo ip link set wlan0 up"); // 清理占用与启用接口 system("sudo pkill -f create_ap; sudo pkill -f dnsmasq"); system("sudo ip link set wlan0 up"); // 组装SSID和密码 std::string ssid = name.empty() ? UUID : name; std::string pass = pwd.empty() ? UUID : pwd; if (pass.size() < 8) pass = "12345678"; // 上游接口优先默认路由 char buf[64]{0}; FILE *fp = popen("ip route show default | awk '{print $5}' | head -n1", "r"); std::string upstream = "enP4p65s0"; if (fp && fgets(buf, sizeof(buf), fp)) { upstream = buf; if (!upstream.empty() && upstream.back() == '\n') upstream.pop_back(); } if (fp) pclose(fp); string cmd = format("nohup sudo create_ap --no-virt wlan0 {} {} {} &", upstream,UUID, "12345678"); cout << "cmd = " << cmd << endl; system(cmd.c_str()); } // 读取配置文件,并获取标识符符 void ReadUUID() { lock_guard lk(fileMutex); ReadFile rf(filePath); rf.Open(); auto lines = rf.ReadLines(); for (auto &line : lines) { if (line.find("UUID:\"") != string::npos) { size_t start = sizeof("UUID:\"") - 1; size_t end = line.find_last_of("\"") - start; UUID = line.substr(start, end); } } rf.Close(); } // 启动设备激活程序,等待用户激活 void startActiveWare() { string cmd = "/home/orangepi/RKApp/DeviceActivate/bin/init"; try { // 启动为前台子进程,父进程等待它退出 bp::child c(cmd); g_Device_pid = c.id(); signal(SIGINT, forwardSigint); c.wait(); // 等待子进程结束 } catch (const std::exception &e) { cerr << e.what() << "\n"; } }