Files
front_linux/LFtid1056/cloudfront/code/worker.cpp
2025-10-22 15:57:50 +08:00

970 lines
39 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.

////////////////////////////////////////////////////////////////////////////////////////////////////
#include <fstream>
#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"
#include "log4.h"
#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;
extern int LEDGER_MAX_ITEMS;
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;
}
void Worker::setMaxItems(int items) {
std::lock_guard<std::mutex> locker(testMutex);
LEDGER_MAX_ITEMS = items;
}
// 日志控制
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);
//upload_data_test();
}
}
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"
"dir - Execute rocketmq_test_getdir\r\n"
"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");
}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") {
rocketmq_test_rc(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_rc\r\n");
} else if (cmd == "getdir") {
rocketmq_test_getdir(m_front);
sendStr(clientFD, "\r\x1B[KExecuted rocketmq_test_getdir\r\n");
} 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) {
// —— 显示控制:最多打印的元素数量(防止过长)——
const size_t MAX_ITEMS = static_cast<size_t>(LEDGER_MAX_ITEMS); // 非 constexpr
std::ostringstream os;
os << "\r\x1B[K------------------------------------\n";
//os << "\r\x1B[K|-- dev_index : " << dev.dev_index << "\n";
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";
os << "\r\x1B[K|-- Righttime : " << dev.Righttime << "\n";
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";
}
}
// 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 =========================
for (size_t i = 0; i < dev.line.size(); ++i) {
const auto& ld = dev.line[i];
if (ld.monitor_id.empty()) continue;
os << "\r\x1B[K|-- line[" << i << "]:\n";
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";
}
}
// --- 监测点 · 定值描述文件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";
}
}
// ========================= ★新增:补招打印 =========================
// ★新增:小工具—把状态/阶段枚举转成可读字符串
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)";
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";
}
}
// ======================= ★新增:补招打印结束 =======================
}
os << "\r\x1B[K------------------------------------\n";
sendStr(fd, os.str());
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////打印所有的终端列表
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;
sendStr(clientFD, std::string("\r\x1B[KViewing logs for level: ") + level + " (Press '`' to exit)\r\n> ");
char inputBuf[16];
// --- 新增 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 ---
while (!stopViewLog) {
// 1) 监听 shell 输入退出符号 `
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);
if (n > 0 && std::memchr(inputBuf, '`', static_cast<size_t>(n))) {
stopViewLog = true;
showinshellflag = false;
break;
}
}
// --- 修改 begin: 批量获取日志swap 全取,减少加锁时间) ---
std::list<std::string> tempLogs;
{
std::lock_guard<std::mutex> lock(*logMutex);
if (!logList->empty()) {
tempLogs.swap(*logList); // 把 logList 中的内容全取出
}
}
// --- 修改 end ---
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 ---
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
// 3) 打印退出提示
sendStr(clientFD, "\r\x1B[K\nLog view stopped. Returning to shell.\r\n> ");
// --- 新增 begin: 关闭文件 ---
logFile.close();
// --- 新增 end ---
}