diff --git a/src/public/libcmd/inc/myCmd.h b/src/public/libcmd/inc/myCmd.h index 98f6618..5237a36 100644 --- a/src/public/libcmd/inc/myCmd.h +++ b/src/public/libcmd/inc/myCmd.h @@ -1,8 +1,9 @@ /** * @file myCmd.h * @brief 命令处理框架 — 纯 C 接口 - * @details 命令注册/分发/补全,内置 linenoise 行编辑。 + * @details 命令注册/分发/补全。 * 通过 `__attribute__((constructor))` 实现模块自动注册。 + * 调试命令通过 WebSocket 在前端执行,不占用终端 stdin。 */ #ifndef _MY_CMD_H_ #define _MY_CMD_H_ @@ -42,21 +43,6 @@ void cmd_complete(const char *buf, char ***completions, int *ncomp); void cmd_sub_complete(const char *buf, char ***completions, int *ncomp, const char **subs, int sub_count); -/** 行编辑交互式输入 */ -char *linenoise(const char *prompt); - -/** 释放 linenoise 返回的内存 */ -void linenoiseFree(void *ptr); - -/** 添加历史记录 */ -int linenoiseHistoryAdd(const char *line); - -/** 释放所有历史 */ -void linenoiseHistoryFree(void); - -/** 设置自动补全回调 */ -void lineniseSetCompletionCallback(void (*cb)(const char *, char ***, int *)); - /** 注册命令宏(无补全) */ #define CMD_REGISTER(cmd_name, func_ptr, cmd_desc) \ __attribute__((constructor)) static void register_##func_ptr(void) \ diff --git a/src/public/libcmd/src/myCmd.cpp b/src/public/libcmd/src/myCmd.cpp index bd9d17e..514bf75 100644 --- a/src/public/libcmd/src/myCmd.cpp +++ b/src/public/libcmd/src/myCmd.cpp @@ -14,6 +14,9 @@ extern "C" { +/* ===== 内置命令 ===== */ +CMD_REGISTER("help", cmd_help, "显示所有可用命令") + /* ===== 命令注册表 ===== */ static std::vector &get_cmd_list(void) @@ -182,6 +185,8 @@ void cmd_help(int argc, char *argv[]) { printf("%-24s [%s]\n", cmd.name, cmd.desc); } + + fflush(stdout); } /* ===== 内置 linenoise ===== */ @@ -199,6 +204,7 @@ static void (*g_completion_cb)(const char *, char ***, int *) = NULL; static struct termios g_orig_termios; static int g_raw_enabled = 0; +static void (*g_old_sigint)(int) = NULL; static void disable_raw(void) { @@ -209,24 +215,31 @@ static void disable_raw(void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_orig_termios); g_raw_enabled = 0; + + /* restore SIGINT handler */ + if (g_old_sigint) + { + signal(SIGINT, g_old_sigint); + g_old_sigint = NULL; + } } static void sigint_handler(int sig) { - (void)sig; + void (*old)(int) = g_old_sigint; - if (g_raw_enabled) + disable_raw(); + + /* 先恢复 raw mode 再调用原始 handler(main.c 的 sig_handler 设 g_running=0) */ + if (old) { - disable_raw(); + old(sig); } - - printf("\n"); - exit(0); } static int enable_raw(void) { - signal(SIGINT, sigint_handler); + g_old_sigint = signal(SIGINT, sigint_handler); tcgetattr(STDIN_FILENO, &g_orig_termios); atexit(disable_raw); @@ -424,24 +437,48 @@ static int linenoise_edit(char *buf, int bufsize, const char *prompt) return 0; } +void linenoiseSetCompletionCallback(void (*cb)(const char *, char ***, int *)) +{ + g_completion_cb = cb; +} + char *linenoise(const char *prompt) { char buf[1024]; + /* 默认补全回调(首次调用或外部未设置时生效) */ + if (!g_completion_cb) + { + g_completion_cb = cmd_complete; + } + if (0 != enable_raw()) { - return NULL; + /* stdin is not a TTY (pipe/redirect) — fallback to fgets */ + if (prompt) SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt)); + if (!fgets(buf, (int)sizeof(buf), stdin)) + { + return NULL; /* EOF */ + } + /* strip trailing newline */ + size_t len = strlen(buf); + while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) + { + buf[--len] = '\0'; + } + return strdup(buf); } int n = linenoise_edit(buf, (int)sizeof(buf), prompt); disable_raw(); - if (n <= 0) + if (n < 0) { - return NULL; + return NULL; /* read error or EOF */ } + /* empty line: return allocated empty string (not NULL) */ char *copy = strdup(buf); return copy; @@ -471,6 +508,8 @@ void linenoiseHistoryFree(void) { free(g_history[j]); } + + g_history_len = 0; } void lineniseSetCompletionCallback(void (*cb)(const char *, char ***, int *)) diff --git a/src/system/RTU/src/main.c b/src/system/RTU/src/main.c index 1a2ec7d..ec6270a 100644 --- a/src/system/RTU/src/main.c +++ b/src/system/RTU/src/main.c @@ -14,46 +14,21 @@ #include "mySystem.h" #include "myLog.h" #include "myDatacenter.h" -#include "myModbus.h" #include "myFunc.h" -#include "myCmd.h" #include #include +#include #include -#include static volatile int g_running = 1; static void sig_handler(int sig) { - (void)sig; g_running = 0; LOG_I("signal %d received, shutting down...", sig); } -static void cmd_version(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - - printf("RTU_ALL_AI v1.0\n"); - printf(" libmodbus v%d.%d.%d\n", - libmodbus_version_major, libmodbus_version_minor, - libmodbus_version_micro); -} - -static void cmd_uptime(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - - printf("RTU uptime: (running)\n"); -} - -CMD_REGISTER("version", cmd_version, "show firmware version"); -CMD_REGISTER("uptime", cmd_uptime, "show uptime"); - int main(int argc, char *argv[]) { (void)argc; @@ -65,11 +40,13 @@ int main(int argc, char *argv[]) signal(SIGTERM, sig_handler); signal(SIGPIPE, SIG_IGN); + /* 日志初始化:控制台输出(调试可见),运行时可开文件输出 */ log_init(NULL); - LOG_I("RTU booting..."); + /* 启动数据中心 */ dc_init(NULL); + /* X-Macro 驱动启动所有模块 */ if (app_sys_init() != 0) { LOG_E("app_sys_init failed, aborting"); @@ -79,38 +56,25 @@ int main(int argc, char *argv[]) LOG_I("RTU running"); + /* 导出 out 信号 XML 供前端 PLC Config 页面使用(所有模块 init1/init2 之后) */ + { + char proc_dir[256] = {0}; + + if (0 == func_proc_self_dir(proc_dir, sizeof(proc_dir))) + { + char xml_path[512]; + + snprintf(xml_path, sizeof(xml_path), "%s/config/SYSTEM/datacenter_out.xml", + proc_dir); + dc_save_out_signals_xml(xml_path); + LOG_I("datacenter_out.xml exported to %s", xml_path); + } + } + + /* 纯事件等待,命令交互走 Web 前端 */ while (g_running) { - char *line = linenoise("RTU> "); - - if (!line) - { - break; - } - - if (line[0] != '\0') - { - linenoiseHistoryAdd(line); - - char *argv_buf[16]; - uint8_t argc = 0; - - if (func_str2argv(line, &argc, argv_buf, 16) == 0 && argc > 0) - { - stru_cmd *cmd = cmd_find(argv_buf[0]); - - if (cmd) - { - cmd->func((int)argc, argv_buf); - } - else - { - printf("unknown command: %s (type 'help')\n", argv_buf[0]); - } - } - } - - linenoiseFree(line); + sleep(1); } LOG_I("shutting down...");