This commit is contained in:
2026-01-09 13:59:10 +08:00
commit 336a19762a
378 changed files with 99177 additions and 0 deletions

364
DeviceActivate/src/main.cpp Normal file
View File

@@ -0,0 +1,364 @@
/*
本程序用于设备激活
当检测配置文件发现设备未激活时,则系统阻塞
进行激活,并保存密文
当检测到设备已被激活后,启动验证程序,并退出本程序
*/
#include <iostream>
#include <mqtt/async_client.h> //和App进行MQTT数据交互
#include <boost/process.hpp>
#include <mutex>
#include <condition_variable>
#include <nlohmann/json.hpp> //用于操作JSON文件
#include "Netra.hpp"
using namespace std;
using namespace QCL;
using namespace chrono_literals;
// 配置相对路径
const string envPath = "/home/orangepi/RKApp/InitAuth/conf/.env";
// MQTT相关配置
const string mqtt_url = "tcp://192.168.12.1:1883";
const string clientId = "RK3588_SubTest";
const string TopicRecv = "/bsd_camera/cmd"; // 接收手机传来的信息
const string TopicSend = "/bsd_camera/init"; // 发送的话题
const int Qos = 1;
atomic<bool> isRunning(true); // 是否需要激活
atomic<bool> Deactivate(false); // 激活标志
mutex envMutex; // 配置锁
mutex runMutex;
condition_variable runCv; // 条件变量,判断是否可以启动验证程序
mqtt::async_client client(mqtt_url, clientId);
atomic<pid_t> g_soft_pid{0};
// 获取SIM卡号
// 通过串口发送 AT+CCID 指令读取并解析返回的ICCID号
string GetSimICCID(const string &tty = "/dev/ttyUSB2");
// 获取唯一标识码
string GetqrCode();
// MQTT初始化
void mqttInit();
// 连接成功的回调
void connectCallback(const string &cause);
// 接受消息的回调
void messageCallback(mqtt::const_message_ptr msg);
// 检测设备是否已进行初始化
bool checkUUID();
// 退出程序,开始进行设备验证
void StartCheck();
void forwardSigint(int);
int main()
{
if (checkUUID())
{
// 设备已被激活,调用验证程序,退出本程序
cout << "设备已激活,开始验证" << endl;
StartCheck();
return 0;
}
// 初始化MQTT
mqttInit();
unique_lock<mutex> lk(runMutex);
runCv.wait(lk, []()
{ return !isRunning.load(); });
return 0;
}
void forwardSigint(int)
{
pid_t gpid = g_soft_pid.load();
if (gpid > 0)
{
kill(-gpid, SIGINT);
}
}
// 获取唯一标识码
string GetqrCode()
{
lock_guard<mutex> lk(envMutex);
string UUID = "";
ReadFile rf(envPath);
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();
return UUID;
}
// 退出程序,开始进行设备验证
void StartCheck()
{
namespace bp = boost::process;
string cmd = "../../softWareInit/bin/verification ";
cout << cmd << endl;
try
{
bp::child c(cmd);
g_soft_pid = c.id();
signal(SIGINT, forwardSigint);
c.wait(); // 等待子进程结束
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
string sysCmd = cmd + " &";
system(sysCmd.c_str());
}
// 退出程序
isRunning = false;
runCv.notify_one();
}
// 检测设备是否已经激活
bool checkUUID()
{
bool flag = false;
// 读取文件
ReadFile rf(envPath);
if (rf.Open() == false)
{
cerr << "文件打开失败" << endl;
}
// 读取文本每一行
auto lines = rf.ReadLines();
for (auto &ii : lines)
{
if (ii.find("ServerPwd:null") != string::npos)
{
flag = false;
break;
}
else
flag = true;
}
rf.Close();
return flag;
}
// 接受消息的回调
void messageCallback(mqtt::const_message_ptr msg)
{
// 接受App传来的密文,并保存在配置文件中
auto buffer = msg->to_string();
cout << "Topic:" << msg->get_topic() << ",msg:" << buffer << endl;
if (buffer.find("Activate") != string::npos)
{
// 接受请求,发送SIM卡号
string ICCID = GetSimICCID();
string qrcode = GetqrCode();
string deviceId = format(R"({"cardNo":"{}","qrcode":"{}"})", ICCID, qrcode);
cout << "deviceId:" << deviceId << "" << endl;
client.publish(TopicSend, deviceId, Qos, false); // 不保存
}
else if (buffer.find("ServerPwd") != string::npos)
{
// 接受UUID,保存至配置文件中,退出程序,调用设备验证程序
auto res = nlohmann::json::parse(buffer); // 准备解析接受到的秘钥
auto pwd = res["Data"];
cout << "pass = " << pwd << endl;
// 写入文件
ReadFile rf(envPath);
rf.Open();
auto lines = rf.ReadLines();
for (auto &ii : lines)
{
if (ii.find("ServerPwd:null") != string::npos)
ii = format("ServerPwd:{}", pwd);
}
rf.Close();
thread([lines]()
{
lock_guard<mutex> lk(envMutex);
WriteFile wf(envPath);
string out;
out.reserve(1024);
for (size_t i = 0; i < lines.size(); ++i)
{
out += lines[i];
if (i + 1 < lines.size())
out += "\n";
}
wf.overwriteText(out);
StartCheck(); })
.detach();
}
}
// 连接成功的回调
void connectCallback(const string &cause)
{
cout << "连接成功" << endl;
}
void mqttInit()
{
client.set_message_callback(messageCallback);
client.set_connected_handler(connectCallback);
client.connect()->wait(); // 进行连接
client.subscribe(TopicRecv, Qos)->wait(); // 订阅话题
}
// 获取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 "";
}

View File

@@ -0,0 +1,8 @@
all:init
init:main.cpp
g++ -g -o init main.cpp -lpaho-mqttpp3 -lpaho-mqtt3a -lpthread /home/orangepi/RKApp/DeviceActivate/NetraLib/src/Netra.cpp -I/home/orangepi/RKApp/DeviceActivate/NetraLib/include
mv init ../bin/
clean:
rm -rf ../bin/init