Files
front_linux/LFtid1056/cloudfront/code/worker.cpp

977 lines
39 KiB
C++
Raw Normal View History

2025-06-24 17:55:34 +08:00
////////////////////////////////////////////////////////////////////////////////////////////////////
2025-09-15 16:36:21 +08:00
#include <fstream>
2025-06-24 17:55:34 +08:00
#include <iostream>
#include <string>
#include <vector>
#include <array>
#include <list>
#include <map>
#include <thread>
#include <mutex>
#include <atomic>
#include <cstdio>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <fnmatch.h>
#include <chrono>
#include <cstring>
#include <cstdlib>
#include <ctime>
// socket 网络编程
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/stat.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
#include "worker.h"
#include "interface.h"
#include "rocketmq.h"
2025-09-23 10:19:53 +08:00
#include "log4.h"
2025-06-24 17:55:34 +08:00
#include "front.h"
/////////////////////////////////////////////////////////////////////////////////////////////////////
//shell日志打印开关
bool showinshellflag =false;
////////////////////////////////////////////////////////////////////////////////////////////////////
extern std::list<std::string> errorList, warnList, normalList;
extern std::mutex errorListMutex, warnListMutex, normalListMutex;
extern int IED_COUNT;
extern int INITFLAG;
extern int g_front_seg_index;
extern std::string subdir;
extern int G_TEST_NUM;
extern int G_TEST_TYPE;
2025-10-22 15:57:50 +08:00
extern int LEDGER_MAX_ITEMS;
2025-06-24 17:55:34 +08:00
extern bool errorOutputEnabled;
extern bool warnOutputEnabled;
extern bool normalOutputEnabled;
////////////////////////////////////////////////////////////////////////////////////////////////////测试登录类
Worker::Worker(Front* front)
: m_front(front), listenFD(-1), running(false), historyIndex(-1), stopViewLog(true), g_stopTelnetTest(true) {}
Worker::~Worker() {
stopServer();
}
// 启动 Telnet 服务(监听端口)
bool Worker::startServer(int port) {
if (running) return false;
listenFD = socket(AF_INET, SOCK_STREAM, 0);
if (listenFD < 0) return false;
int opt = 1;
setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(listenFD, (sockaddr*)&addr, sizeof(addr)) < 0) return false;
if (listen(listenFD, 5) < 0) return false;
running = true;
serverThread = std::thread(&Worker::acceptLoop, this);
std::thread periodicThread(&Worker::doPeriodicTask, this);
periodicThread.detach();
return true;
}
// 停止 Telnet 服务
void Worker::stopServer() {
if (!running) return;
running = false;
shutdown(listenFD, SHUT_RDWR);
close(listenFD);
if (serverThread.joinable()) serverThread.join();
}
// 发送字符串
void Worker::sendStr(int fd, const std::string& s) {
send(fd, s.c_str(), s.size(), 0);
}
// 接收客户端连接主循环
void Worker::acceptLoop() {
while (running) {
sockaddr_in caddr;
socklen_t clen = sizeof(caddr);
int cfd = accept(listenFD, (sockaddr*)&caddr, &clen);
if (cfd >= 0) std::thread(&Worker::sessionThread, this, cfd).detach();
}
}
//打印提示符
void Worker::printPrompt(int clientFD)
{
sendStr(clientFD, "\r\n>");
}
// 客户端会话线程处理函数
void Worker::sessionThread(int clientFD) {
sendTelnetNegotiation(clientFD);
sendStr(clientFD, "\r\nWelcome to the test shell. Type 'help'.\r\n> ");
while (true) {
char buf[512];
int n = recv(clientFD, buf, sizeof(buf), 0);
if (n <= 0) break;
for (int i = 0; i < n; ++i) {
unsigned char c = (unsigned char)buf[i];
// 如果不是可打印字符,且也不是 上/下箭头、退格、回车等,就忽略
bool isPrintable = (c >= 32 && c <= 126); //可见字符范围
bool isArrowOrEsc = (c == 0x1b); //ESC
bool isEnter = (c == '\r' || c == '\n'); //换行
bool isBackspace = (c == 0x7f || c == 0x08);//退格
bool isTab = (c == '\t'); //tab
if( !isPrintable && !isArrowOrEsc && !isEnter && !isBackspace && !isTab )
{
// 跳过不认识的控制字符:如 '\0'
continue;
}
// Telnet 协议协商处理
if (c == IAC) {
if (i + 1 < n && (buf[i+1] == TELDO || buf[i+1] == DONT || buf[i+1] == WILL || buf[i+1] == WONT)) i += 2;
else i++;
continue;
}
// 退出 viewlog / telnet 流程
if (c == '`') {
stopViewLog = true; // [MOD] 支持停止日志查看
g_stopTelnetTest = true; // [MOD] 停止 telnet 测试
sendStr(clientFD, "\r\n[Log view stopped]\r\n> ");
continue;
}
// 2) 回车/换行 => 执行命令
if (c == '\r' || c == '\n')
{
// 如果当前是 '\r' 并且下一个是 '\n',就跳过 '\n'
if (c == '\r') {
if (i + 1 < n && buf[i+1] == '\n') {
i++;
}
// 这里再加一步,如果紧跟着是 '\0',也跳过
if (i + 1 < n && buf[i+1] == '\0') {
i++;
}
}
// 如果当前是 '\n',也可以检查一下下一个是不是 '\0'(有些客户端可能发 '\n\0'
else {
if (i + 1 < n && buf[i+1] == '\0') {
i++;
}
}
// 现在把 当前输入的指令 前后空白去掉
std::string cmdtrim = trim(currentCommand);
if (!cmdtrim.empty()) { //输入指令非空则记录到历史
if (commandHistory.empty() || commandHistory.back() != cmdtrim) { //防止连续重复的历史记录
commandHistory.push_back(cmdtrim);
}
historyIndex = commandHistory.size(); //更新历史指令下标
processCommand(cmdtrim, clientFD); //处理当前指令
}
// 处理后清空并打印新的提示符
currentCommand.clear();
printPrompt(clientFD);
// 回车后处理新的命令行
continue;
}
// 上/下箭头处理
if (c == 0x1b && i + 2 < n && buf[i+1] == '[') {
char arrow = buf[i+2];
if (arrow == 'A') handleUpArrow(clientFD);
else if (arrow == 'B') handleDownArrow(clientFD);
i += 2;
continue;
}
// 退格处理
if (c == 0x7f || c == 0x08) {
if (!currentCommand.empty()) {
currentCommand.pop_back();
sendStr(clientFD, "\b \b");
}
continue;
}
// 普通字符输入
currentCommand.push_back((char)c);
sendBytes(clientFD, (const char*)&c, 1);
}
}
close(clientFD);
}
// 发送 Telnet 协商指令
void Worker::sendTelnetNegotiation(int clientFD) {
unsigned char will_echo[3] = { IAC, WILL, TELOPT_ECHO };
unsigned char will_sga[3] = { IAC, WILL, TELOPT_SUPPRESS_GO_AHEAD };
unsigned char dont_lin[3] = { IAC, DONT, TELOPT_LINEMODE };
sendBytes(clientFD, (const char*)will_echo, 3);
sendBytes(clientFD, (const char*)will_sga, 3);
sendBytes(clientFD, (const char*)dont_lin, 3);
}
// 发送字节数组
void Worker::sendBytes(int fd, const char* buf, int len) {
send(fd, buf, len, 0);
}
void Worker::setTestNum(int num) {
std::lock_guard<std::mutex> locker(testMutex);
G_TEST_NUM = num;
}
void Worker::setTestType(int type) {
std::lock_guard<std::mutex> locker(testMutex);
G_TEST_TYPE = type;
}
2025-10-22 15:57:50 +08:00
void Worker::setMaxItems(int items) {
std::lock_guard<std::mutex> locker(testMutex);
LEDGER_MAX_ITEMS = items;
}
2025-06-24 17:55:34 +08:00
// 日志控制
void Worker::setTestlog(bool flag) {
redirectErrorOutput(flag);
redirectWarnOutput(flag);
redirectNormalOutput(flag);
}
void Worker::doPeriodicTask() {
while (running) {
{
std::lock_guard<std::mutex> locker(testMutex);
std::cout << "[PeriodicTask] G_TEST_NUM = " << G_TEST_NUM
<< ", G_TEST_TYPE = " << G_TEST_TYPE << std::endl;
if (G_TEST_NUM != 0) {
std::cout << "[PeriodicTask] Executing rocketmq_test_300()\n";
rocketmq_test_300(G_TEST_NUM, g_front_seg_index, G_TEST_TYPE,m_front);
2025-06-26 16:44:21 +08:00
//upload_data_test();
2025-06-24 17:55:34 +08:00
}
}
std::this_thread::sleep_for(std::chrono::seconds(60)); // 每 60 秒执行一次
}
}
// 命令处理逻辑扩展
void Worker::processCommand(const std::string &cmd, int clientFD) {
std::cout << "Received command: " << cmd << std::endl;
if (cmd == "help") {
std::string helpText =
"Available commands:\r\n"
"G_TEST_NUM=<num> - Set the G_TEST_NUM\r\n"
"G_TEST_TYPE=<num> - Set the G_TEST_TYPE 0:use ledger,1:use number\r\n"
"LOG=<bool> - Set the LOG\r\n"
2025-09-02 14:58:19 +08:00
"dir - Execute rocketmq_test_getdir\r\n"
2025-06-24 17:55:34 +08:00
"rc - Execute rocketmq_test_rc\r\n"
"rt - Execute rocketmq_test_rt\r\n"
"ud - Execute rocketmq_test_ud\r\n"
"set - Execute rocketmq_test_set\r\n"
"log - Execute rocketmq_test_log\r\n"
"upload - Execute upload file\r\n"
"qvvr - Execute http_test_qvvr\r\n"
"ledger <id> - Execute ledger with optional terminal_id\r\n"
"viewlog <level> - View logs (ERROR, WARN, NORMAL, DEBUG)\r\n"
"value <valuename> - Execute value print with valuename\r\n"
"exit - Exit the shell\r\n"
"help - Show this help message\r\n";
sendStr(clientFD, "\r\x1B[K" + helpText);
} else if (cmd.find("viewlog") == 0) {
showinshellflag = true;
handleViewLogCommand(cmd, clientFD);
} else if (cmd.find("G_TEST_NUM=") == 0) {
int num = std::atoi(cmd.substr(9).c_str());
setTestNum(num);
sendStr(clientFD, "\r\x1B[KTEST_NUM updated\r\n");
} else if (cmd.find("G_TEST_TYPE=") == 0) {
int type = std::atoi(cmd.substr(10).c_str());
setTestType(type);
sendStr(clientFD, "\r\x1B[KTEST_TYPE updated\r\n");
} else if (cmd.find("LOG=") == 0) {
int flag = std::atoi(cmd.substr(4).c_str());
setTestlog(flag);
sendStr(clientFD, "\r\x1B[KLOG updated\r\n");
2025-10-22 15:57:50 +08:00
}else if (cmd.find("MAX=") == 0) {
int flag = std::atoi(cmd.substr(4).c_str());
setMaxItems(flag);
sendStr(clientFD, "\r\x1B[KMAX_ITEMS updated\r\n");
}
else if (cmd == "rc") {
2025-06-24 17:55:34 +08:00
rocketmq_test_rc(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_rc\r\n");
2025-09-02 14:58:19 +08:00
} else if (cmd == "getdir") {
rocketmq_test_getdir(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_getdir\r\n");
2025-06-24 17:55:34 +08:00
} else if (cmd == "rt") {
rocketmq_test_rt(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_rt\r\n");
} else if (cmd == "ud") {
rocketmq_test_ud(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_ud\r\n");
} else if (cmd == "set") {
rocketmq_test_set(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_set\r\n");
} else if (cmd == "upload") {
Fileupload_test();
sendStr(clientFD, "\r\x1B[KExecuted upload file\r\n");
} else if (cmd == "qvvr") {
qvvr_test();
sendStr(clientFD, "\r\x1B[KExecuted http_test_qvvr\r\n");
} else if (cmd.find("ledger") == 0) {
size_t pos = cmd.find(' ');
if (pos != std::string::npos) {
std::string terminalId = cmd.substr(pos + 1);
ledger(terminalId.c_str(), clientFD);
sendStr(clientFD, "\r\x1B[KExecuted ledger with terminal_id\r\n");
} else {
ledger("", clientFD);
sendStr(clientFD, "\r\x1B[KExecuted ledger without parameters\r\n");
}
} else if (cmd.find("value") == 0) {
size_t pos = cmd.find(' ');
if (pos != std::string::npos) {
std::string var = cmd.substr(pos + 1);
sendStr(clientFD, "\r\x1B[KExecuted value with variable name: " + var + "\r\n");
value_print(var, clientFD);
} else {
sendStr(clientFD, "\r\x1B[KPlease provide a variable name\r\n");
}
} else if (cmd == "exit") {
sendStr(clientFD, "\r\x1B[KGoodbye! Exiting shell...\r\n");
shutdown(clientFD, SHUT_RDWR);
close(clientFD);
return;
} else {
sendStr(clientFD, "\r\x1B[KUnknown command\r\n");
}
// 打印提示符
sendStr(clientFD, "> ");
}
// 上箭头历史回溯
void Worker::handleUpArrow(int fd) {
if (!commandHistory.empty() && historyIndex > 0) {
historyIndex--;
currentCommand = commandHistory[historyIndex];
sendStr(fd, "\r\x1B[K> " + currentCommand);
}
}
// 下箭头历史前进
void Worker::handleDownArrow(int fd) {
if (!commandHistory.empty() && historyIndex < (int)commandHistory.size() - 1) {
historyIndex++;
currentCommand = commandHistory[historyIndex];
sendStr(fd, "\r\x1B[K> " + currentCommand);
} else {
currentCommand.clear();
sendStr(fd, "\r\x1B[K> ");
}
}
// 字符串 trim
std::string Worker::trim(const std::string& s) {
auto start = s.begin();
while (start != s.end() && std::isspace(*start)) ++start;
auto end = s.end();
do { --end; } while (std::distance(start, end) > 0 && std::isspace(*end));
return (start <= end) ? std::string(start, end + 1) : "";
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////测试shell用的函数
void Worker::printLedgerinshell(const terminal_dev& dev, int fd) {
2025-09-05 16:28:26 +08:00
// —— 显示控制:最多打印的元素数量(防止过长)——
2025-10-22 15:57:50 +08:00
const size_t MAX_ITEMS = static_cast<size_t>(LEDGER_MAX_ITEMS); // 非 constexpr
2025-09-05 16:28:26 +08:00
2025-06-24 17:55:34 +08:00
std::ostringstream os;
os << "\r\x1B[K------------------------------------\n";
2025-10-24 17:07:51 +08:00
os << "\r\x1B[K|-- guid : " << dev.guid << "\n";
os << "\r\x1B[K|-- busytype : " << dev.busytype << "\n";
os << "\r\x1B[K|-- isbusy : " << dev.isbusy << "\n";
os << "\r\x1B[K|-- busytimecount : " << dev.busytimecount << "\n";
2025-09-25 16:36:04 +08:00
//os << "\r\x1B[K|-- dev_index : " << dev.dev_index << "\n";
2025-09-05 16:28:26 +08:00
os << "\r\x1B[K|-- terminal_id : " << dev.terminal_id << "\n";
os << "\r\x1B[K|-- terminal_name : " << dev.terminal_name << "\n";
os << "\r\x1B[K|-- dev_ip : " << dev.addr_str << "\n";
os << "\r\x1B[K|-- dev_port : " << dev.port << "\n";
os << "\r\x1B[K|-- dev_type : " << dev.dev_type << "\n";
os << "\r\x1B[K|-- dev_key : " << dev.dev_key << "\n";
os << "\r\x1B[K|-- dev_series : " << dev.dev_series << "\n";
os << "\r\x1B[K|-- dev_processNo : " << dev.processNo << "\n";
os << "\r\x1B[K|-- maxProcessNum : " << dev.maxProcessNum << "\n";
os << "\r\x1B[K|-- org_name : " << dev.org_name << "\n";
os << "\r\x1B[K|-- maint_name : " << dev.maint_name << "\n";
os << "\r\x1B[K|-- station_name : " << dev.station_name << "\n";
os << "\r\x1B[K|-- tmnl_factory : " << dev.tmnl_factory << "\n";
os << "\r\x1B[K|-- tmnl_status : " << dev.tmnl_status << "\n";
os << "\r\x1B[K|-- timestamp : " << dev.timestamp << "\n";
2025-09-24 14:07:09 +08:00
os << "\r\x1B[K|-- Righttime : " << dev.Righttime << "\n";
2025-09-05 16:28:26 +08:00
os << "\r\x1B[K|-- mac : " << dev.mac << "\n";
// ========================= 终端级 · 内部定值 =========================
// internal_valuesushort 列表)与 dz_internal_info_list 一一对应,仅展示前 MAX_ITEMS 条
os << "\r\x1B[K|-- Internal Values (" << dev.internal_values.size() << "):\n";
{
size_t idx = 0;
for (auto itv = dev.internal_values.begin();
itv != dev.internal_values.end() && idx < MAX_ITEMS;
++itv, ++idx) {
os << "\r\x1B[K |-- [" << idx << "] value: " << static_cast<unsigned>(*itv) << "\n";
}
if (dev.internal_values.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (dev.internal_values.size() - MAX_ITEMS) << " more)\n";
}
}
// dz_internal_info_listNameFixValue 描述,和 internal_values 对应)
os << "\r\x1B[K|-- Internal Value Descriptions (" << dev.dz_internal_info_list.size() << "):\n";
{
const size_t n = dev.dz_internal_info_list.size();
for (size_t i = 0; i < n && i < MAX_ITEMS; ++i) {
const auto& nf = dev.dz_internal_info_list[i];
os << "\r\x1B[K |-- [" << i << "] "
<< "Name=" << trim_cstr(nf.sFixValueName, sizeof(nf.sFixValueName))
<< ", Type=" << nf.DataType
<< ", Unit=" << trim_cstr(nf.sDimension, sizeof(nf.sDimension))
<< ", Min=" << nf.MinValue
<< ", Max=" << nf.MaxValue
<< ", Default=" << nf.DefaultValue
<< "\n";
}
if (n > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (n - MAX_ITEMS) << " more)\n";
}
}
2025-06-26 14:39:34 +08:00
2025-09-05 16:28:26 +08:00
// control_words控制字描述
os << "\r\x1B[K|-- Control Words (" << dev.control_words.size() << "):\n";
{
const size_t n = dev.control_words.size();
for (size_t i = 0; i < n && i < MAX_ITEMS; ++i) {
const DZ_kzz_bit& b = dev.control_words[i];
os << "\r\x1B[K |-- [" << i << "] "
<< "name=\"" << trim_cstr(b.kzz_bit, sizeof(b.kzz_bit)) << "\""
<< ", enable=" << static_cast<int>(b.bit_enable) << "\n";
}
if (n > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (n - MAX_ITEMS) << " more)\n";
}
}
// ========================= 监测点级 · line =========================
2025-06-24 17:55:34 +08:00
for (size_t i = 0; i < dev.line.size(); ++i) {
const auto& ld = dev.line[i];
if (ld.monitor_id.empty()) continue;
2025-09-05 16:28:26 +08:00
2025-06-24 17:55:34 +08:00
os << "\r\x1B[K|-- line[" << i << "]:\n";
2025-09-05 16:28:26 +08:00
os << "\r\x1B[K |-- monitor_id : " << ld.monitor_id << "\n";
os << "\r\x1B[K |-- monitor_name : " << ld.monitor_name << "\n";
os << "\r\x1B[K |-- logical_device_seq : " << ld.logical_device_seq << "\n";
os << "\r\x1B[K |-- terminal_id : " << ld.terminal_id << "\n";
os << "\r\x1B[K |-- voltage_level : " << ld.voltage_level << "\n";
os << "\r\x1B[K |-- terminal_connect : " << ld.terminal_connect << "\n";
os << "\r\x1B[K |-- status : " << ld.status << "\n";
os << "\r\x1B[K |-- timestamp : " << ld.timestamp << "\n";
os << "\r\x1B[K |-- CT1=" << ld.CT1 << ", CT2=" << ld.CT2
<< ", PT1=" << ld.PT1 << ", PT2=" << ld.PT2 << "\n";
// --- 监测点 · 定值set_values ---
os << "\r\x1B[K |-- Set Values (" << ld.set_values.size() << "):\n";
{
size_t idx = 0;
for (auto it = ld.set_values.begin();
it != ld.set_values.end() && idx < MAX_ITEMS;
++it, ++idx) {
os << "\r\x1B[K |-- [" << idx << "] " << *it << "\n";
}
if (ld.set_values.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (ld.set_values.size() - MAX_ITEMS) << " more)\n";
}
}
2025-06-26 14:39:34 +08:00
2025-09-05 16:28:26 +08:00
// --- 监测点 · 定值描述文件dz_info_list ---
os << "\r\x1B[K |-- DZ Table (" << ld.dz_info_list.size() << "):\n";
{
const size_t n = ld.dz_info_list.size();
for (size_t j = 0; j < n && j < MAX_ITEMS; ++j) {
const DZ_TAB_STRUCT& z = ld.dz_info_list[j];
os << "\r\x1B[K |-- [" << j << "] "
<< "LN=" << z.LN_Num
<< ", DZ=" << z.DZ_Num
<< ", Name=" << trim_cstr(z.DZ_Name, sizeof(z.DZ_Name))
<< ", Type=" << z.DZ_Type
<< ", Min=" << z.DZ_Min
<< ", Max=" << z.DZ_Max
<< ", Def=" << z.DZ_Default
<< ", Unit=" << trim_cstr(z.DZ_UNIT, sizeof(z.DZ_UNIT))
<< "\n";
}
if (n > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (n - MAX_ITEMS) << " more)\n";
}
}
// --- 监测点 · 暂态事件qvvrevent ---
os << "\r\x1B[K |-- QVVR Event:\n";
// 1) 暂态事件列表
os << "\r\x1B[K |-- Events (" << ld.qvvrevent.qvvrdata.size() << "):\n";
{
const size_t n = ld.qvvrevent.qvvrdata.size();
for (size_t k = 0; k < n && k < MAX_ITEMS; ++k) {
const qvvr_data& e = ld.qvvrevent.qvvrdata[k];
os << "\r\x1B[K |-- [" << k << "] "
<< "used=" << e.used_status
<< ", type=" << e.QVVR_type
<< ", time=" << static_cast<unsigned long long>(e.QVVR_time)
<< ", per=" << e.QVVR_PerTime
<< ", amp=" << e.QVVR_Amg
<< ", phase="<< e.phase
<< "\n";
}
if (n > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (n - MAX_ITEMS) << " more)\n";
}
}
// 2) 文件组列表
os << "\r\x1B[K |-- File Groups (" << ld.qvvrevent.qvvrfile.size() << "):\n";
{
const size_t n = ld.qvvrevent.qvvrfile.size();
for (size_t g = 0; g < n && g < MAX_ITEMS; ++g) {
const qvvr_file& fg = ld.qvvrevent.qvvrfile[g];
os << "\r\x1B[K |-- [" << g << "] "
<< "used=" << fg.used_status
<< ", tcount=" << fg.file_time_count
<< ", downloaded=" << fg.is_download
<< ", paired=" << fg.is_pair
<< "\n";
// 文件名列表file_name
os << "\r\x1B[K |-- file_name (" << fg.file_name.size() << "):\n";
{
size_t c = 0;
for (const auto& fn : fg.file_name) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- " << fn << "\n";
}
if (fg.file_name.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (fg.file_name.size() - MAX_ITEMS) << " more)\n";
}
}
// 已下载列表file_download
os << "\r\x1B[K |-- file_download (" << fg.file_download.size() << "):\n";
{
size_t c = 0;
for (const auto& fn : fg.file_download) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- " << fn << "\n";
}
if (fg.file_download.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (fg.file_download.size() - MAX_ITEMS) << " more)\n";
}
}
}
if (n > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (n - MAX_ITEMS) << " more)\n";
}
}
2025-10-22 15:57:50 +08:00
// ========================= ★新增:补招打印 =========================
// ★新增:小工具—把状态/阶段枚举转成可读字符串
auto recallStatusStr = [](int st) -> const char* {
switch (st) {
case 0: return "NOT_STARTED(0)";
case 1: return "RUNNING(1)";
case 2: return "DONE(2)";
case 3: return "FAILED(3)";
2025-10-24 08:50:54 +08:00
case 4: return "EMPTY(4)";
2025-10-22 15:57:50 +08:00
default: return "UNKNOWN";
}
};
auto phaseStr = [](RecallPhase p) -> const char* {
switch (p) {
case RecallPhase::IDLE: return "IDLE";
case RecallPhase::LISTING: return "LISTING";
case RecallPhase::DOWNLOADING: return "DOWNLOADING";
}
return "UNKNOWN";
};
auto resultStr = [](ActionResult r) -> const char* {
switch (r) {
case ActionResult::PENDING: return "PENDING";
case ActionResult::FAIL: return "FAIL";
case ActionResult::OK: return "OK";
}
return "UNKNOWN";
};
// --- ★新增事件补招RecallMonitor ---
os << "\r\x1B[K |-- Recall(Event) (" << ld.recall_list.size() << "):\n";
{
size_t idx = 0;
for (const auto& r : ld.recall_list) {
if (idx++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- [" << (idx-1) << "] "
<< "status=" << recallStatusStr(r.recall_status)
<< ", StartTime=" << r.StartTime
<< ", EndTime=" << r.EndTime
<< ", STEADY=" << r.STEADY
<< ", VOLTAGE=" << r.VOLTAGE
<< "\n";
}
if (ld.recall_list.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (ld.recall_list.size() - MAX_ITEMS) << " more)\n";
}
}
// --- ★新增稳态补招RecallFile+ 状态机信息 ---
os << "\r\x1B[K |-- Recall(Static Files) (" << ld.recall_list_static.size() << "):\n";
{
size_t idx = 0;
for (const auto& rf : ld.recall_list_static) {
if (idx++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- [" << (idx-1) << "] "
<< "status=" << recallStatusStr(rf.recall_status)
<< ", StartTime=" << rf.StartTime
<< ", EndTime=" << rf.EndTime
<< ", STEADY=" << rf.STEADY
<< ", VOLTAGE=" << rf.VOLTAGE
<< "\n";
// ★新增:直下模式与目标时间列表
os << "\r\x1B[K |-- direct_mode=" << (rf.direct_mode ? "true" : "false")
<< ", target_filetimes(" << rf.target_filetimes.size() << ")\n";
{
size_t c = 0;
for (const auto& t : rf.target_filetimes) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- " << t << "\n";
}
if (rf.target_filetimes.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (rf.target_filetimes.size() - MAX_ITEMS) << " more)\n";
}
}
// ★新增:状态机运行态
os << "\r\x1B[K |-- phase=" << phaseStr(rf.phase)
<< ", cur_dir_index=" << rf.cur_dir_index
<< ", cur_dir=" << rf.cur_dir << "\n";
os << "\r\x1B[K |-- list_result=" << resultStr(rf.list_result)
<< ", download_result=" << resultStr(rf.download_result) << "\n";
// ★新增:候选目录
os << "\r\x1B[K |-- dir_candidates(" << rf.dir_candidates.size() << ")\n";
{
size_t c = 0;
for (const auto& d : rf.dir_candidates) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- " << d << "\n";
}
if (rf.dir_candidates.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (rf.dir_candidates.size() - MAX_ITEMS) << " more)\n";
}
}
// ★新增:目录 -> 文件名列表(仅概要)
os << "\r\x1B[K |-- dir_files(" << rf.dir_files.size() << " dirs)\n";
{
size_t c = 0;
for (const auto& kv : rf.dir_files) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- [" << (c-1) << "] dir=" << kv.first
<< " files=" << kv.second.size() << "\n";
}
if (rf.dir_files.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (rf.dir_files.size() - MAX_ITEMS) << " more)\n";
}
}
// ★新增:下载队列(概要)
os << "\r\x1B[K |-- download_queue(" << rf.download_queue.size() << ")\n";
{
size_t c = 0;
for (const auto& path : rf.download_queue) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- " << path << "\n";
}
if (rf.download_queue.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (rf.download_queue.size() - MAX_ITEMS) << " more)\n";
}
}
// ★新增:当前下载中文件
if (!rf.downloading_file.empty()) {
os << "\r\x1B[K |-- downloading: " << rf.downloading_file << "\n";
}
// ★新增:已下载/待上报的完整路径file_paths
os << "\r\x1B[K |-- file_paths(" << rf.file_paths.size() << ")\n";
{
size_t c = 0;
for (const auto& p : rf.file_paths) {
if (c++ >= MAX_ITEMS) break;
os << "\r\x1B[K |-- " << p << "\n";
}
if (rf.file_paths.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (rf.file_paths.size() - MAX_ITEMS) << " more)\n";
}
}
}
if (ld.recall_list_static.size() > MAX_ITEMS) {
os << "\r\x1B[K |.. (+" << (ld.recall_list_static.size() - MAX_ITEMS) << " more)\n";
}
}
// ======================= ★新增:补招打印结束 =======================
2025-06-24 17:55:34 +08:00
}
os << "\r\x1B[K------------------------------------\n";
sendStr(fd, os.str());
}
2025-09-05 16:28:26 +08:00
////////////////////////////////////////////////////////////////////////////////////////////////////////////////打印所有的终端列表
2025-06-24 17:55:34 +08:00
void Worker::ledger(const std::string& terminal_id, int fd) {
sendStr(fd, "\r\x1B[Kprint ledger in shell\n");
std::lock_guard<std::mutex> lock(ledgermtx);
bool found = false;
if (terminal_id.empty()) {
for (const auto& dev : terminal_devlist) {
printLedgerinshell(dev, fd);
}
} else {
for (const auto& dev : terminal_devlist) {
if (dev.terminal_id == terminal_id) {
printLedgerinshell(dev, fd);
found = true;
break;
}
}
if (!found) {
std::ostringstream msg;
msg << "\r\x1B[Kterminal not exist: " << terminal_id << "\n";
sendStr(fd, msg.str());
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////打印指定的变量名
void Worker::value_print(const std::string& variableName, int clientFD) {
std::string output;
{
std::lock_guard<std::mutex> lock(ledgermtx);
std::cout << "value_print hold lock !!!!!!!!!!!" << std::endl;
if (variableName == "frontindex") {
output = "frontindex = " + std::to_string(g_front_seg_index);
} else if (variableName == "iedcount") {
output = "ledger list = " + std::to_string(terminal_devlist.size()) +
", ied config count = " + std::to_string(IED_COUNT);
} else if (variableName == "frontfun") {
output = "frontfun = " + subdir;
} else if (variableName == "log") {
output = "showinshellflag = " + std::to_string(showinshellflag) +
", normalOutputEnabled = " + std::to_string(normalOutputEnabled) +
", warnOutputEnabled = " + std::to_string(warnOutputEnabled) +
", errorOutputEnabled = " + std::to_string(errorOutputEnabled);
} else if (variableName == "init") {
output = "INITFLAG = " + std::to_string(INITFLAG);
} else {
output = "Unknown variable name: " + variableName;
}
std::cout << "value_print free lock !!!!!!!!!!!" << std::endl;
}
sendStr(clientFD, "\r\x1B[K" + output + "\r\n");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////日志开关控制
//日志消息列表
std::list<std::string>* getLogList(const std::string& level) {
if (level == "ERROR") return &errorList;
if (level == "WARN") return &warnList;
if (level == "NORMAL") return &normalList;
return nullptr;
}
//日志锁
std::mutex* getLogMutex(const std::string& level) {
if (level == "ERROR") return &errorListMutex;
if (level == "WARN") return &warnListMutex;
if (level == "NORMAL") return &normalListMutex;
return nullptr;
}
void Worker::handleViewLogCommand(const std::string& command, int clientFD) {
std::istringstream iss(command);
std::string cmd, level;
iss >> cmd >> level;
std::transform(level.begin(), level.end(), level.begin(), ::toupper);
if (level.empty()) {
sendStr(clientFD, "\r\x1B[KUsage: viewlog [ERROR|WARN|NORMAL]\r\n> ");
return;
}
std::list<std::string>* logList = getLogList(level);
std::mutex* logMutex = getLogMutex(level);
if (!logList || !logMutex) {
sendStr(clientFD, "\r\x1B[KInvalid log level! Use ERROR, WARN, NORMAL.\r\n> ");
return;
}
stopViewLog = false;
showinshellflag = true;
2025-09-15 16:36:21 +08:00
sendStr(clientFD, std::string("\r\x1B[KViewing logs for level: ") + level + " (Press '`' to exit)\r\n> ");
2025-06-24 17:55:34 +08:00
char inputBuf[16];
2025-09-15 16:36:21 +08:00
// --- 新增 begin: 目录创建 + 唯一文件名生成 + 打开文件 ---
// 递归创建目录的小工具(最小实现,按‘/’逐级创建)
auto ensure_dir = [](const std::string& path) -> bool {
if (path.empty()) return false;
std::string cur;
cur.reserve(path.size());
for (size_t i = 0; i < path.size(); ++i) {
cur.push_back(path[i]);
if (path[i] == '/' && cur.size() > 1) {
if (::access(cur.c_str(), F_OK) != 0) {
if (::mkdir(cur.c_str(), 0755) != 0 && errno != EEXIST) return false;
}
}
}
// 末级(若不以 / 结尾)
if (cur.back() != '/') {
if (::access(cur.c_str(), F_OK) != 0) {
if (::mkdir(cur.c_str(), 0755) != 0 && errno != EEXIST) return false;
}
}
return true;
};
const std::string logDir = "/FeProject/dat/log";
if (!ensure_dir(logDir)) {
sendStr(clientFD, "\r\x1B[KFailed to create log directory: /FeProject/dat/log\r\n> ");
return;
}
std::string filePath = logDir + "/temp.log";
int index = 1;
while (::access(filePath.c_str(), F_OK) == 0) {
filePath = logDir + "/temp_" + std::to_string(index++) + ".log";
}
std::ofstream logFile(filePath.c_str(), std::ios::out | std::ios::trunc);
if (!logFile.is_open()) {
sendStr(clientFD, "\r\x1B[KFailed to open log file for writing.\r\n> ");
return;
}
// --- 新增 end ---
2025-06-24 17:55:34 +08:00
while (!stopViewLog) {
2025-09-15 16:36:21 +08:00
// 1) 监听 shell 输入退出符号 `
2025-06-24 17:55:34 +08:00
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(clientFD, &read_fds);
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 500000; // 500ms
int activity = select(clientFD + 1, &read_fds, nullptr, nullptr, &timeout);
if (activity > 0 && FD_ISSET(clientFD, &read_fds)) {
int n = recv(clientFD, inputBuf, sizeof(inputBuf), 0);
2025-09-15 16:36:21 +08:00
if (n > 0 && std::memchr(inputBuf, '`', static_cast<size_t>(n))) {
2025-06-24 17:55:34 +08:00
stopViewLog = true;
showinshellflag = false;
break;
}
}
2025-09-15 16:36:21 +08:00
// --- 修改 begin: 批量获取日志swap 全取,减少加锁时间) ---
std::list<std::string> tempLogs;
2025-06-24 17:55:34 +08:00
{
std::lock_guard<std::mutex> lock(*logMutex);
if (!logList->empty()) {
2025-09-15 16:36:21 +08:00
tempLogs.swap(*logList); // 把 logList 中的内容全取出
2025-06-24 17:55:34 +08:00
}
}
2025-09-15 16:36:21 +08:00
// --- 修改 end ---
2025-06-24 17:55:34 +08:00
2025-09-15 16:36:21 +08:00
if (!tempLogs.empty()) {
for (const auto& logEntry : tempLogs) {
if (!logEntry.empty()) {
sendStr(clientFD, std::string("\r\x1B[K") + logEntry + "\r\n");
// --- 新增 begin: 写入文件 + 及时落盘 ---
logFile << logEntry << '\n';
// --- 新增 end ---
}
}
// --- 新增 begin: 刷新文件缓冲,保证实时可见 ---
logFile.flush();
// --- 新增 end ---
2025-06-24 17:55:34 +08:00
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
2025-09-15 16:36:21 +08:00
// 3) 打印退出提示
2025-06-24 17:55:34 +08:00
sendStr(clientFD, "\r\x1B[K\nLog view stopped. Returning to shell.\r\n> ");
2025-09-15 16:36:21 +08:00
// --- 新增 begin: 关闭文件 ---
logFile.close();
// --- 新增 end ---
2025-06-24 17:55:34 +08:00
}
2025-09-15 16:36:21 +08:00