Files
RkApp/Identification/src/main.cpp
2026-01-09 13:59:10 +08:00

430 lines
12 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.

/*
本程序用于 设备出厂获取 唯一标识符进行 wifi设置和修改
向云端发送访问请求(ICCID号),并显示请求次数
接收云端传传来的标识符,并保存至本地,根据表示符进行wifi设置
第二次启动时,自动检测是否已进行初始化,若是,直接从配置文件中获取唯一标识符
*/
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <nlohmann/json.hpp> //用于解析JSON字符串
#include <chrono>
#include <boost/process.hpp>
#include "NetRequest.hpp"
#include "Netra.hpp"
#include <boost/process.hpp>
#include <signal.h>
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<pid_t> 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<mutex> 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<unsigned char>(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<unsigned char>(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<unsigned char>(s[pos])))
pos++;
size_t start = pos;
while (pos < s.size() && std::isalnum(static_cast<unsigned char>(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<mutex> 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<mutex> 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<mutex> 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";
}
}