<修改> 1、在webserver增加自定义命令行调试页面
This commit is contained in:
parent
18ee3d8acd
commit
6ef960525c
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#include "myBase.h"
|
#include "myBase.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
#include "myFunc.h"
|
||||||
|
|
@ -40,6 +42,8 @@ int app_cfg_parse()
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
#ifdef RK356x
|
#ifdef RK356x
|
||||||
MY_LOG_I("RK356x");
|
MY_LOG_I("RK356x");
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -2228,8 +2228,12 @@ LOCAL void dc_show_signals(stru_signal_map &dc_signal_map)
|
||||||
<< pad_left(val_str, COL_VAL)
|
<< pad_left(val_str, COL_VAL)
|
||||||
<< pad_left(link_str, COL_LINK)
|
<< pad_left(link_str, COL_LINK)
|
||||||
<< endl;
|
<< endl;
|
||||||
|
|
||||||
|
if (!cout.good()) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cout.good()) return;
|
||||||
|
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
cout << "signal_id: " << dc_signal_map.signal_id << endl;
|
cout << "signal_id: " << dc_signal_map.signal_id << endl;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
struct mg_connection;
|
struct mg_connection;
|
||||||
|
|
||||||
void ws_recv(struct mg_connection *c, const char *p_rx, uint16_t rx_len);
|
void ws_recv(struct mg_connection *c, const char *p_rx, uint16_t rx_len);
|
||||||
void ws_send_all(const char *p_tx, uint16_t tx_len);
|
void ws_send_all(const char *p_tx, size_t tx_len);
|
||||||
void ws_send_one(unsigned long conn_id, const char *p_tx, uint16_t tx_len);
|
bool ws_send_one(unsigned long conn_id, const char *p_tx, size_t tx_len);
|
||||||
|
bool ws_send_binary(unsigned long conn_id, const void *data, size_t len);
|
||||||
void ws_session_destroy(struct mg_connection *c);
|
void ws_session_destroy(struct mg_connection *c);
|
||||||
void ws_task(void);
|
void ws_task(void);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,11 +18,11 @@ LOCAL pthread_mutex_t g_ws_conns_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static pthread_t thread_id;
|
static pthread_t thread_id;
|
||||||
|
|
||||||
|
|
||||||
void ws_send_all(const char *p_tx, uint16_t tx_len)
|
void ws_send_all(const char *p_tx, size_t tx_len)
|
||||||
{
|
{
|
||||||
if(NULL == p_tx || 0 == tx_len)
|
if(NULL == p_tx || 0 == tx_len)
|
||||||
{
|
{
|
||||||
LOG_E("ws_send_all arg not valid, p_tx:%p, tx_len:%d", p_tx, tx_len);
|
LOG_E("ws_send_all arg not valid, p_tx:%p, tx_len:%zu", p_tx, tx_len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,25 +39,48 @@ void ws_send_all(const char *p_tx, uint16_t tx_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ws_send_one(unsigned long conn_id, const char *p_tx, uint16_t tx_len)
|
bool ws_send_one(unsigned long conn_id, const char *p_tx, size_t tx_len)
|
||||||
{
|
{
|
||||||
if(NULL == p_tx || 0 == tx_len)
|
if(NULL == p_tx || 0 == tx_len)
|
||||||
{
|
{
|
||||||
LOG_E("ws_send_one arg not valid, p_tx:%p, tx_len:%d", p_tx, tx_len);
|
LOG_E("ws_send_one arg not valid, p_tx:%p, tx_len:%zu", p_tx, tx_len);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
pthread_mutex_lock(&g_ws_conns_mutex);
|
pthread_mutex_lock(&g_ws_conns_mutex);
|
||||||
for(uint32_t i = 0; i < g_ws_conns.size(); i++)
|
for(uint32_t i = 0; i < g_ws_conns.size(); i++)
|
||||||
{
|
{
|
||||||
struct mg_connection *c = g_ws_conns[i];
|
struct mg_connection *c = g_ws_conns[i];
|
||||||
if(c->id == conn_id && c->is_websocket && !c->is_draining)
|
if(c->id == conn_id && c->is_websocket && !c->is_draining)
|
||||||
{
|
{
|
||||||
mg_ws_send(c, p_tx, tx_len, WEBSOCKET_OP_TEXT);
|
size_t sent = mg_ws_send(c, p_tx, tx_len, WEBSOCKET_OP_TEXT);
|
||||||
|
ok = (sent >= tx_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&g_ws_conns_mutex);
|
pthread_mutex_unlock(&g_ws_conns_mutex);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_send_binary(unsigned long conn_id, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
if(NULL == data || 0 == len) return false;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
pthread_mutex_lock(&g_ws_conns_mutex);
|
||||||
|
for(uint32_t i = 0; i < g_ws_conns.size(); i++)
|
||||||
|
{
|
||||||
|
struct mg_connection *c = g_ws_conns[i];
|
||||||
|
if(c->id == conn_id && c->is_websocket && !c->is_draining)
|
||||||
|
{
|
||||||
|
size_t sent = mg_ws_send(c, data, len, WEBSOCKET_OP_BINARY);
|
||||||
|
ok = (sent >= len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_ws_conns_mutex);
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
#include "mySystem.h"
|
#include "mySystem.h"
|
||||||
#include "myDatacenter.h"
|
#include "myDatacenter.h"
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
#include "myCmd.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -499,6 +503,148 @@ LOCAL void set_param_signal_data(stru_ws_session &s, const std::string& saddr, c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LOCAL void ws_send_large_text(unsigned long conn_id, const char *type, const std::string &data)
|
||||||
|
{
|
||||||
|
const size_t CHUNK_MAX = 35000;
|
||||||
|
|
||||||
|
if(data.size() <= CHUNK_MAX)
|
||||||
|
{
|
||||||
|
cJSON *resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "type", type);
|
||||||
|
cJSON_AddStringToObject(resp, "data", data.c_str());
|
||||||
|
char *p_tx = cJSON_PrintUnformatted(resp);
|
||||||
|
if(p_tx)
|
||||||
|
{
|
||||||
|
ws_send_one(conn_id, p_tx, strlen(p_tx));
|
||||||
|
free(p_tx);
|
||||||
|
}
|
||||||
|
cJSON_Delete(resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> cuts;
|
||||||
|
size_t pos = 0;
|
||||||
|
while(pos < data.size())
|
||||||
|
{
|
||||||
|
size_t end = pos + CHUNK_MAX;
|
||||||
|
if(end >= data.size()) end = data.size();
|
||||||
|
|
||||||
|
while(end < data.size() && (data[end] & 0xC0) == 0x80) end++;
|
||||||
|
|
||||||
|
cuts.push_back(end);
|
||||||
|
pos = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_chunks = cuts.size();
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
MY_LOG_I("ws_send_large_text: %zu chunks, %zu bytes total", total_chunks, data.size());
|
||||||
|
|
||||||
|
for(size_t seq = 0; seq < total_chunks; seq++)
|
||||||
|
{
|
||||||
|
size_t end = cuts[seq];
|
||||||
|
std::string chunk = data.substr(pos, end - pos);
|
||||||
|
pos = end;
|
||||||
|
|
||||||
|
cJSON *resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "type", "cmd_rsp_chunk");
|
||||||
|
cJSON_AddNumberToObject(resp, "seq", (double)seq);
|
||||||
|
cJSON_AddNumberToObject(resp, "total", (double)total_chunks);
|
||||||
|
cJSON_AddStringToObject(resp, "data", chunk.c_str());
|
||||||
|
char *p_tx = cJSON_PrintUnformatted(resp);
|
||||||
|
if(p_tx)
|
||||||
|
{
|
||||||
|
size_t tx_len = strlen(p_tx);
|
||||||
|
bool ok = ws_send_one(conn_id, p_tx, tx_len);
|
||||||
|
free(p_tx);
|
||||||
|
if(!ok)
|
||||||
|
{
|
||||||
|
MY_LOG_E("ws_send_large_text: chunk %zu/%zu failed (len=%zu)", seq, total_chunks, tx_len);
|
||||||
|
cJSON_Delete(resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usleep(5000);
|
||||||
|
}
|
||||||
|
cJSON_Delete(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LOCAL std::string ws_cmd_exec(const char *cmd_line)
|
||||||
|
{
|
||||||
|
if(NULL == cmd_line || 0 == strlen(cmd_line))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[1024];
|
||||||
|
strncpy(line, cmd_line, sizeof(line) - 1);
|
||||||
|
line[sizeof(line) - 1] = '\0';
|
||||||
|
|
||||||
|
char *argv[16];
|
||||||
|
int argc = 0;
|
||||||
|
char *p = strtok(line, " \t");
|
||||||
|
while(NULL != p && argc < 16)
|
||||||
|
{
|
||||||
|
argv[argc++] = p;
|
||||||
|
p = strtok(NULL, " \t");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 == argc)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
stru_cmd *p_cmd = cmd_find(argv[0]);
|
||||||
|
if(NULL == p_cmd)
|
||||||
|
{
|
||||||
|
return std::string("[unknown] ") + argv[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
int pipefd[2];
|
||||||
|
if(0 != pipe(pipefd))
|
||||||
|
{
|
||||||
|
return "pipe failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
pthread_t reader_thread;
|
||||||
|
auto *reader_ctx = new std::pair<int, std::string*>(pipefd[0], &result);
|
||||||
|
|
||||||
|
pthread_create(&reader_thread, NULL,
|
||||||
|
[](void *arg) -> void* {
|
||||||
|
auto *ctx = (std::pair<int, std::string*>*)arg;
|
||||||
|
char rbuf[4096];
|
||||||
|
ssize_t nr;
|
||||||
|
while((nr = read(ctx->first, rbuf, sizeof(rbuf))) > 0)
|
||||||
|
{
|
||||||
|
ctx->second->append(rbuf, nr);
|
||||||
|
}
|
||||||
|
close(ctx->first);
|
||||||
|
delete ctx;
|
||||||
|
return NULL;
|
||||||
|
},
|
||||||
|
reader_ctx);
|
||||||
|
|
||||||
|
int saved_stdout = dup(STDOUT_FILENO);
|
||||||
|
dup2(pipefd[1], STDOUT_FILENO);
|
||||||
|
close(pipefd[1]);
|
||||||
|
|
||||||
|
p_cmd->func(argc, argv);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
dup2(saved_stdout, STDOUT_FILENO);
|
||||||
|
close(saved_stdout);
|
||||||
|
|
||||||
|
pthread_join(reader_thread, NULL);
|
||||||
|
|
||||||
|
if(!result.empty())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return "(no output)";
|
||||||
|
}
|
||||||
|
|
||||||
void ws_recv(struct mg_connection *c, const char* p_rx, uint16_t rx_len)
|
void ws_recv(struct mg_connection *c, const char* p_rx, uint16_t rx_len)
|
||||||
{
|
{
|
||||||
|
|
@ -526,6 +672,22 @@ void ws_recv(struct mg_connection *c, const char* p_rx, uint16_t rx_len)
|
||||||
cJSON *signal_type = cJSON_GetObjectItem(root, "signal_type");
|
cJSON *signal_type = cJSON_GetObjectItem(root, "signal_type");
|
||||||
cJSON *signal_data = cJSON_GetObjectItem(root, "signal_data");
|
cJSON *signal_data = cJSON_GetObjectItem(root, "signal_data");
|
||||||
cJSON *setting_zone = cJSON_GetObjectItem(root, "setting_zone");
|
cJSON *setting_zone = cJSON_GetObjectItem(root, "setting_zone");
|
||||||
|
cJSON *type = cJSON_GetObjectItem(root, "type");
|
||||||
|
if(type && cJSON_IsString(type) && type->valuestring)
|
||||||
|
{
|
||||||
|
if(0 == strcmp(type->valuestring, "cmd"))
|
||||||
|
{
|
||||||
|
cJSON *data = cJSON_GetObjectItem(root, "data");
|
||||||
|
if(data && cJSON_IsString(data) && data->valuestring)
|
||||||
|
{
|
||||||
|
std::string result = ws_cmd_exec(data->valuestring);
|
||||||
|
ws_send_binary(c->id, result.data(), result.size());
|
||||||
|
}
|
||||||
|
cJSON_Delete(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cJSON *curd = cJSON_GetObjectItem(root, "curd");
|
cJSON *curd = cJSON_GetObjectItem(root, "curd");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -538,3 +538,54 @@ body.login-page #main-content { margin-left: 0; display: flex; align-items: cent
|
||||||
.signal-table { font-size: 12px; }
|
.signal-table { font-size: 12px; }
|
||||||
.signal-table thead th, .signal-table tbody td { padding: 6px 8px; }
|
.signal-table thead th, .signal-table tbody td { padding: 6px 8px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== 命令终端 ===== */
|
||||||
|
.terminal-wrap {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
.terminal-output {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #d4d4d4;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
background: #1e1e1e;
|
||||||
|
}
|
||||||
|
.terminal-output div {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.terminal-input-line {
|
||||||
|
display: flex;
|
||||||
|
background: #2d2d2d;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-top: 1px solid #3c3c3c;
|
||||||
|
}
|
||||||
|
.terminal-prompt {
|
||||||
|
color: #6a9955;
|
||||||
|
padding: 4px 6px 4px 0;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.terminal-input {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #d4d4d4;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.term-sent { color: #569cd6; }
|
||||||
|
.term-resp { color: #d4d4d4; }
|
||||||
|
.term-err { color: #f44747; }
|
||||||
|
.term-info { color: #6a9955; }
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ var App = (function() {
|
||||||
{ hash: '#yk', label: '⚡ 遥控 (yk)' },
|
{ hash: '#yk', label: '⚡ 遥控 (yk)' },
|
||||||
{ hash: '#ao', label: '🔧 参数 (ao)' },
|
{ hash: '#ao', label: '🔧 参数 (ao)' },
|
||||||
{ hash: '#param', label: '⚙ 定值 (param)' },
|
{ hash: '#param', label: '⚙ 定值 (param)' },
|
||||||
{ hash: '#monitor', label: '📋 数据监控' }
|
{ hash: '#monitor', label: '📋 数据监控' },
|
||||||
|
{ hash: '#terminal', label: '💻 命令终端' }
|
||||||
];
|
];
|
||||||
|
|
||||||
function buildNav() {
|
function buildNav() {
|
||||||
|
|
@ -137,8 +138,18 @@ var App = (function() {
|
||||||
|
|
||||||
/* ---- WS 下行数据分发 ---- */
|
/* ---- WS 下行数据分发 ---- */
|
||||||
function onWsMessage(data, raw) {
|
function onWsMessage(data, raw) {
|
||||||
|
if (raw instanceof ArrayBuffer && currentPage && currentPage.onBinary) {
|
||||||
|
currentPage.onBinary(raw);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!data || typeof data !== 'object') return;
|
if (!data || typeof data !== 'object') return;
|
||||||
|
|
||||||
|
if (data.type === 'cmd_rsp' && currentPage && currentPage.onData) {
|
||||||
|
currentPage.onData(data.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var signalTypes = ['out', 'in', 'yk', 'ao', 'param'];
|
var signalTypes = ['out', 'in', 'yk', 'ao', 'param'];
|
||||||
for (var i = 0; i < signalTypes.length; i++) {
|
for (var i = 0; i < signalTypes.length; i++) {
|
||||||
var st = signalTypes[i];
|
var st = signalTypes[i];
|
||||||
|
|
@ -173,6 +184,7 @@ var App = (function() {
|
||||||
registerPage('#ao', AoPage);
|
registerPage('#ao', AoPage);
|
||||||
registerPage('#param', ParamPage);
|
registerPage('#param', ParamPage);
|
||||||
registerPage('#monitor', MonitorPage);
|
registerPage('#monitor', MonitorPage);
|
||||||
|
registerPage('#terminal', TerminalPage);
|
||||||
|
|
||||||
// 构建导航
|
// 构建导航
|
||||||
buildNav();
|
buildNav();
|
||||||
|
|
|
||||||
|
|
@ -684,3 +684,91 @@ var MonitorPage = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
* 命令终端页面
|
||||||
|
* ======================================================================== */
|
||||||
|
var TerminalPage = (function() {
|
||||||
|
var history = [];
|
||||||
|
var historyIdx = 0;
|
||||||
|
|
||||||
|
function append(text, cls) {
|
||||||
|
var el = document.getElementById('term-output');
|
||||||
|
if (!el) return;
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.className = cls || '';
|
||||||
|
div.textContent = text;
|
||||||
|
el.appendChild(div);
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
render: function(container) {
|
||||||
|
history = [];
|
||||||
|
historyIdx = 0;
|
||||||
|
container.innerHTML = ''
|
||||||
|
+ '<div class="page-header"><h2>命令终端</h2><p>输入 RTU 命令,Enter 发送,↑↓ 历史记录</p></div>'
|
||||||
|
+ '<div class="terminal-wrap">'
|
||||||
|
+ '<div class="terminal-output" id="term-output"></div>'
|
||||||
|
+ '<div class="terminal-input-line">'
|
||||||
|
+ '<span class="terminal-prompt">cmd > </span>'
|
||||||
|
+ '<input type="text" class="terminal-input" id="term-input" autofocus spellcheck="false">'
|
||||||
|
+ '</div>'
|
||||||
|
+ '</div>';
|
||||||
|
|
||||||
|
var input = document.getElementById('term-input');
|
||||||
|
if (!input) return;
|
||||||
|
|
||||||
|
input.onkeydown = function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
var cmd = input.value.trim();
|
||||||
|
if (cmd) {
|
||||||
|
history.push(cmd);
|
||||||
|
historyIdx = history.length;
|
||||||
|
append('cmd > ' + cmd, 'term-sent');
|
||||||
|
if (!WsClient.send({type:'cmd', data:cmd})) {
|
||||||
|
append('[未连接]', 'term-err');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.value = '';
|
||||||
|
} else if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (history.length > 0) {
|
||||||
|
historyIdx = Math.max(0, historyIdx - 1);
|
||||||
|
input.value = history[historyIdx];
|
||||||
|
}
|
||||||
|
} else if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (historyIdx < history.length - 1) {
|
||||||
|
historyIdx++;
|
||||||
|
input.value = history[historyIdx];
|
||||||
|
} else {
|
||||||
|
historyIdx = history.length;
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
append('输入 help 查看所有命令', 'term-info');
|
||||||
|
},
|
||||||
|
|
||||||
|
onData: function(data) {
|
||||||
|
if (data) append(data, 'term-resp');
|
||||||
|
},
|
||||||
|
|
||||||
|
onBinary: function(ab) {
|
||||||
|
var decoder = new TextDecoder('utf-8');
|
||||||
|
var text = decoder.decode(ab);
|
||||||
|
if (text) append(text, 'term-resp');
|
||||||
|
},
|
||||||
|
|
||||||
|
onEnter: function() {
|
||||||
|
var input = document.getElementById('term-input');
|
||||||
|
if (input) setTimeout(function() { input.focus(); }, 100);
|
||||||
|
},
|
||||||
|
|
||||||
|
onLeave: function() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ var WsClient = (function() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ws = new WebSocket(url);
|
ws = new WebSocket(url);
|
||||||
|
ws.binaryType = 'arraybuffer';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setStatus('disconnected');
|
setStatus('disconnected');
|
||||||
scheduleReconnect();
|
scheduleReconnect();
|
||||||
|
|
@ -57,6 +58,16 @@ var WsClient = (function() {
|
||||||
ws.onmessage = function(e) {
|
ws.onmessage = function(e) {
|
||||||
var raw = e.data;
|
var raw = e.data;
|
||||||
|
|
||||||
|
if (raw instanceof ArrayBuffer) {
|
||||||
|
if (typeof MonitorRing !== 'undefined') {
|
||||||
|
MonitorRing.push('↓', '[binary ' + raw.byteLength + 'B]');
|
||||||
|
}
|
||||||
|
for (var i = 0; i < msgCallbacks.length; i++) {
|
||||||
|
try { msgCallbacks[i](null, raw); } catch (ex) {}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 写入监控日志
|
// 写入监控日志
|
||||||
if (typeof MonitorRing !== 'undefined') {
|
if (typeof MonitorRing !== 'undefined') {
|
||||||
MonitorRing.push('↓', raw);
|
MonitorRing.push('↓', raw);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue