|
|
@ -48,6 +48,15 @@ void ExtAPIService::initialize(string can_if_name, int baudrate, bool enablLoopb |
|
|
|
m_zmodule_device_manager.reset(new ZModuleDeviceManager()); |
|
|
|
m_zmodule_device_manager->initialize(m_zcan_receiver_master.get()); |
|
|
|
|
|
|
|
// m_zmodule_device_manager->registerModule();
|
|
|
|
for (size_t i = 1; i < 1000; i++) { |
|
|
|
m_proxys[i].initialize(i, m_zcan_receiver_master.get()); |
|
|
|
m_zmodule_device_manager->registerModule(&m_proxys[i]); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief CAN通用指令解析器 |
|
|
|
*/ |
|
|
|
m_zmodule_device_script_cmder_paser.reset(new ZModuleDeviceScriptCmderPaser()); |
|
|
|
m_zmodule_device_script_cmder_paser->initialize(this, m_zmodule_device_manager.get()); |
|
|
|
|
|
|
@ -66,80 +75,173 @@ void ExtAPIService::initialize(string can_if_name, int baudrate, bool enablLoopb |
|
|
|
m_iflytopwsService->startListen(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
* 脚本服务 * |
|
|
|
*******************************************************************************/ |
|
|
|
/**
|
|
|
|
* @brief 脚本服务 |
|
|
|
* |
|
|
|
* |
|
|
|
* protocol: websocket |
|
|
|
* port : 19004 |
|
|
|
* descript: 命令行脚本服务支持,回执以日志的形式返回 |
|
|
|
*/ |
|
|
|
m_script_server.reset(new WebSocketServer(19004, "0.0.0.0")); |
|
|
|
m_script_server->setOnConnectionCallback([this](weak_ptr<WebSocket> webSocket, shared_ptr<ConnectionState> connectionState) { |
|
|
|
logger->info("Remote ip: {}", connectionState->getRemoteIp()); |
|
|
|
// on new client connected
|
|
|
|
logger->info("remote ip: {} connect to 19004", connectionState->getRemoteIp()); |
|
|
|
auto ws = webSocket.lock(); |
|
|
|
if (!ws) return; |
|
|
|
ws->setOnMessageCallback([this, webSocket, connectionState](const ix::WebSocketMessagePtr &msg) { |
|
|
|
processcmds(msg->str, [this, webSocket, connectionState](string cmd, ICmdParserACK *ack) { //
|
|
|
|
string ackstr; |
|
|
|
if (ack->acktype == ICmdParserACK::kAckType_none) { |
|
|
|
ackstr = fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
} else if (ack->acktype == ICmdParserACK::kAckType_int32) { |
|
|
|
ackstr = fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
for (int32_t i = 0; i < ack->getAckInt32Num(); i++) { |
|
|
|
ackstr += fmt::format(" ack[{}]{}\n", i, ack->getAckInt32Val(i)); |
|
|
|
} |
|
|
|
} else if (ack->acktype == ICmdParserACK::kAckType_buf) { |
|
|
|
ackstr = fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
ackstr = fmt::format(" ackbuf :{}", StringUtils().bytesToString(ack->rawdata, ack->rawlen)); |
|
|
|
ackstr = fmt::format(" ackbufstr:{}", string((const char *)ack->rawdata)); |
|
|
|
ws->setOnMessageCallback([this, webSocket](const ix::WebSocketMessagePtr &msg) { |
|
|
|
if (msg->type == ix::WebSocketMessageType::Message) { |
|
|
|
script_server_on_message(webSocket, msg); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
m_script_server->listenAndStart(); |
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
* CAN透传服务 * |
|
|
|
*******************************************************************************/ |
|
|
|
/**
|
|
|
|
* @brief CAN透传服务 |
|
|
|
* |
|
|
|
* protocol: websocket |
|
|
|
* port : 19005 |
|
|
|
* descript: 直接透传CAN数据,不做任何处理 |
|
|
|
* |
|
|
|
*/ |
|
|
|
m_can_passthrough_server.reset(new WebSocketServer(19005, "0.0.0.0")); |
|
|
|
m_can_passthrough_server->setOnConnectionCallback([this](weak_ptr<WebSocket> webSocket, shared_ptr<ConnectionState> connectionState) { |
|
|
|
logger->info("m_can_passthrough_server on connect remote ip: {}", connectionState->getRemoteIp()); |
|
|
|
auto ws = webSocket.lock(); |
|
|
|
if (!ws) return; |
|
|
|
ws->setOnMessageCallback([this, webSocket, connectionState](const ix::WebSocketMessagePtr &msg) { |
|
|
|
// msg->binary;
|
|
|
|
if (msg->binary) { |
|
|
|
logger->info("rx:{}({})", StringUtils().bytesToString(msg->str.data(), msg->wireSize), msg->wireSize); |
|
|
|
m_zcan_receiver_master->sendraw((uint8_t *)msg->str.data(), msg->str.size()); |
|
|
|
} else { |
|
|
|
logger->info("rx:{}({})", msg->str, msg->wireSize); |
|
|
|
|
|
|
|
ws->setOnMessageCallback([this, webSocket](const ix::WebSocketMessagePtr &msg) { |
|
|
|
if (msg->type == ix::WebSocketMessageType::Message) { |
|
|
|
logger->info("down can bin -> {}({})", msg->str, msg->wireSize); |
|
|
|
vector<uint8_t> rxbyte; |
|
|
|
StringUtils().hexStringToBytes(msg->str, "", rxbyte); |
|
|
|
m_zcan_receiver_master->sendraw(rxbyte.data(), rxbyte.size()); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
m_can_passthrough_server->listenAndStart(); |
|
|
|
|
|
|
|
m_zcan_receiver_master->regPacketListener([this](int32_t fromboard, zcr_cmd_header_t *packet, int32_t datalen) { |
|
|
|
auto clients = m_can_passthrough_server->getClients(); |
|
|
|
string rx = StringUtils().bytesToString((uint8_t *)packet, datalen); |
|
|
|
logger->info("tx:{}({})", rx, datalen); |
|
|
|
logger->info("report can bin -< {}({})", rx, datalen); |
|
|
|
for (auto &each : clients) { |
|
|
|
if (each) each->sendText(rx); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
m_zmodule_device_manager->regOnRegValChangeEvent([this](int32_t moduleid, int32_t regadd, int32_t toval) { //
|
|
|
|
logger->info("on reg change: moduleid:{} REG({}) to {}", moduleid, regadd, toval); |
|
|
|
/*******************************************************************************
|
|
|
|
* 事件监听器 * |
|
|
|
*******************************************************************************/ |
|
|
|
/**
|
|
|
|
* @brief 监听事件,并打印 |
|
|
|
*/ |
|
|
|
m_zmodule_device_manager->regOnRegValChangeEvent([this](int32_t moduleid, int32_t regindex, int32_t oldval, int32_t toval) { //
|
|
|
|
logger->info("on reg change: moduleid:{} REG({}) {} to {}", moduleid, regindex, oldval, toval); |
|
|
|
|
|
|
|
// m_script_server 上报事件
|
|
|
|
{ |
|
|
|
auto clients = m_script_server->getClients(); |
|
|
|
for (auto &each : clients) { |
|
|
|
if (each) each->sendText(fmt::format("on reg change: moduleid:{} REG({}) {} to {}", moduleid, regindex, oldval, toval)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
* m_script_server * |
|
|
|
*******************************************************************************/ |
|
|
|
|
|
|
|
void ExtAPIService::script_server_on_message(weak_ptr<WebSocket> webSocket, const ix::WebSocketMessagePtr &msg) { |
|
|
|
logger->info("script_server_on_message:\n{}", msg->str); |
|
|
|
script_processer_processcmds( |
|
|
|
msg->str, |
|
|
|
/*******************************************************************************
|
|
|
|
* PRIVATE_FN_PROCESSER * |
|
|
|
*******************************************************************************/ |
|
|
|
[this, webSocket](string cmd, int32_t paramN, const char **paraV, ICmdParserACK *ack) { |
|
|
|
auto ws = webSocket.lock(); |
|
|
|
if (!ws) return; |
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
* HELP * |
|
|
|
*******************************************************************************/ |
|
|
|
if (cmd == "help") { |
|
|
|
ack->ecode = 0; |
|
|
|
string helpinfo; |
|
|
|
helpinfo += fmt::format("cmdlist:\n"); |
|
|
|
for (auto it = m_cmdmap.begin(); it != m_cmdmap.end(); it++) { |
|
|
|
// ws->sendText(fmt::format("\t{} {}", it->first, it->second.helpinfo));
|
|
|
|
helpinfo += fmt::format("\t{} {}\n", it->first, it->second.helpinfo); |
|
|
|
} |
|
|
|
ws->sendText(helpinfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (cmd == "app_scan_moudle") { |
|
|
|
ack->ecode = 0; |
|
|
|
string helpinfo; |
|
|
|
helpinfo += fmt::format("cmdlist:\n"); |
|
|
|
for (auto it = m_cmdmap.begin(); it != m_cmdmap.end(); it++) { |
|
|
|
// ws->sendText(fmt::format("\t{} {}", it->first, it->second.helpinfo));
|
|
|
|
helpinfo += fmt::format("\t{} {}\n", it->first, it->second.helpinfo); |
|
|
|
} |
|
|
|
ws->sendText(helpinfo); |
|
|
|
return; |
|
|
|
} |
|
|
|
}, |
|
|
|
/*******************************************************************************
|
|
|
|
* ACK_PROCESSER * |
|
|
|
*******************************************************************************/ |
|
|
|
[this, webSocket](string cmd, ICmdParserACK *ack) { //
|
|
|
|
logger->info("do {},{}({})", cmd, ack->ecode, err::error2str(ack->ecode)); |
|
|
|
string ackstr; |
|
|
|
if (ack->acktype == ICmdParserACK::kAckType_none) { |
|
|
|
ackstr += fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
} else if (ack->acktype == ICmdParserACK::kAckType_int32) { |
|
|
|
ackstr += fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
for (int32_t i = 0; i < ack->getAckInt32Num(); i++) { |
|
|
|
ackstr += fmt::format(" ack[{}]{}\n", i, ack->getAckInt32Val(i)); |
|
|
|
} |
|
|
|
} else if (ack->acktype == ICmdParserACK::kAckType_buf) { |
|
|
|
ackstr += fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
ackstr += tools_dumpbuffer(ack->rawdata, ack->rawlen); |
|
|
|
} else if (ack->acktype == ICmdParserACK::kAckType_str) { |
|
|
|
ackstr += fmt::format("{} -> ecode:{}({})\n", cmd, err::error2str(ack->ecode), ack->ecode); |
|
|
|
ackstr = fmt::format(" ackbufstr:{}", string((const char *)ack->rawdata)); |
|
|
|
} |
|
|
|
auto ws = webSocket.lock(); |
|
|
|
if (ws) ws->sendText(ackstr); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
void ExtAPIService::regfns() { |
|
|
|
this->regCMD("", "", 2, [](int32_t paramN, const char **paraV, ICmdParserACK *ack) { |
|
|
|
/**
|
|
|
|
* @brief who call me? |
|
|
|
*/ |
|
|
|
}); |
|
|
|
} |
|
|
|
/*******************************************************************************
|
|
|
|
* 脚本处理方法 * |
|
|
|
*******************************************************************************/ |
|
|
|
void ExtAPIService::regCMD(const char *cmdname, const char *helpinfo, int paraNum, ICmdFunction_t cmdimpl) { |
|
|
|
cmd_container_t cmd_container; |
|
|
|
cmd_container.cmdname = cmdname; |
|
|
|
cmd_container.helpinfo = helpinfo; |
|
|
|
cmd_container.paraNum = paraNum; |
|
|
|
cmd_container.cmdimpl = cmdimpl; |
|
|
|
m_cmdmap[cmdname] = cmd_container; |
|
|
|
} |
|
|
|
|
|
|
|
void ExtAPIService::processcmds(string script, function<void(string cmd, ICmdParserACK *ack)> ackprocesser) { |
|
|
|
void ExtAPIService::script_processer_processcmds(string script, private_fn_processer_t private_fn_processer, ackprocesser_t ackprocesser) { |
|
|
|
/**
|
|
|
|
* @brief |
|
|
|
* script: |
|
|
@ -161,67 +263,138 @@ void ExtAPIService::processcmds(string script, function<void(string cmd, ICmdPar |
|
|
|
for (int32_t i = 0; i < stringlen; i++) { |
|
|
|
if (currentstr == nullptr) { |
|
|
|
if (strcache[i] != '\0') { |
|
|
|
// logger->info("new cmd:{}", &strcache[i]);
|
|
|
|
currentstr = &strcache[i]; |
|
|
|
cmdlines.push_back(currentstr); |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (strcache[i] == '\0') { |
|
|
|
cmdlines.push_back(currentstr); |
|
|
|
currentstr = nullptr; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for (auto &cmdline : cmdlines) { |
|
|
|
ICmdParserACK ack = {0}; |
|
|
|
bool isnote = false; |
|
|
|
callcmd(cmdline, &ack, isnote); |
|
|
|
if (isnote) continue; |
|
|
|
if (ackprocesser) ackprocesser(cmdline, &ack); |
|
|
|
// bool isnote = false;
|
|
|
|
FnProcessContext cxt; |
|
|
|
cxt.private_fn_processer = private_fn_processer; |
|
|
|
cxt.ackprocesser = ackprocesser; |
|
|
|
|
|
|
|
// logger->info("do {}", cmdline);
|
|
|
|
script_processer_callcmd(cmdline, cxt); |
|
|
|
|
|
|
|
if (cxt.breakflag) break; |
|
|
|
} |
|
|
|
free(strcache); |
|
|
|
} |
|
|
|
|
|
|
|
void removenote(char *buf); |
|
|
|
|
|
|
|
void ExtAPIService::callcmd(string cmd, ICmdParserACK *ack, bool &isnote) { |
|
|
|
char *argv[10]; |
|
|
|
void ExtAPIService::script_processer_callcmd(string cmd, FnProcessContext &cxt) { |
|
|
|
char *argv[30] = {0}; |
|
|
|
int32_t argc = 0; |
|
|
|
|
|
|
|
char cmdcache[1024] = {0}; |
|
|
|
strncmp(cmdcache, cmd.c_str(), 1023); |
|
|
|
strncpy(cmdcache, cmd.c_str(), 1023); |
|
|
|
|
|
|
|
// remove note
|
|
|
|
for (size_t i = 0; i < cmd.length(); i++) { |
|
|
|
if (cmdcache[i] == '#') { |
|
|
|
cmdcache[i] = '\0'; |
|
|
|
if (i == 0) { |
|
|
|
isnote = true; |
|
|
|
// is note return;
|
|
|
|
return; |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
char *p = strtok(cmdcache, " "); |
|
|
|
while (p != NULL) { |
|
|
|
argv[argc] = p; |
|
|
|
argc++; |
|
|
|
p = strtok(NULL, " "); |
|
|
|
// logger->info("cmdcache:{}", cmdcache);
|
|
|
|
|
|
|
|
for (int32_t i = 0; i < cmd.length(); i++) { |
|
|
|
if (cmdcache[i] == '\n' || cmdcache[i] == '\r' || cmdcache[i] == ' ' || cmdcache[i] == 127) { |
|
|
|
cmdcache[i] = '\0'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int j = 0; |
|
|
|
for (int i = 0; cmdcache[i] == 0 || i < cmd.length(); i++) { |
|
|
|
if (cmdcache[i] != 0 && j == 0) { |
|
|
|
argv[argc++] = &cmdcache[i]; |
|
|
|
j = 1; |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
callcmd(argv[0], argc - 1, (const char **)(argv + 1), ack); |
|
|
|
if (cmdcache[i] == 0 && j == 1) { |
|
|
|
j = 0; |
|
|
|
continue; |
|
|
|
} |
|
|
|
void ExtAPIService::callcmd(string cmdname, int32_t paramN, const char **paraV, ICmdParserACK *ack) { |
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_cmdmaplock_); |
|
|
|
auto it = m_cmdmap.find(cmdname); |
|
|
|
if (it == m_cmdmap.end()) { |
|
|
|
ack->ecode = err::kcmd_not_found; |
|
|
|
} |
|
|
|
|
|
|
|
logger->info("argc:{}", argc); |
|
|
|
for (int i = 0; i < argc; i++) { |
|
|
|
logger->info("argv[{}]:{}", i, argv[i]); |
|
|
|
} |
|
|
|
|
|
|
|
if (argc == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
script_processer_callcmd(cxt, argv[0], argc - 1, (const char **)(argv + 1)); |
|
|
|
} |
|
|
|
void ExtAPIService::script_processer_callcmd(FnProcessContext &cxt, string cmdname, int32_t paramN, const char **paraV) { |
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_cmdmaplock_); |
|
|
|
auto it = m_cmdmap.find(cmdname); |
|
|
|
|
|
|
|
logger->info("call cmd:{} paramN:{}", cmdname, paramN); |
|
|
|
ICmdParserACK ack = {0}; |
|
|
|
if (it != m_cmdmap.end()) { |
|
|
|
cmd_container_t cmd_container = it->second; |
|
|
|
if (cmd_container.paraNum != paramN) { |
|
|
|
ack->ecode = err::kcmd_param_num_error; |
|
|
|
ack.ecode = err::kcmd_param_num_error; |
|
|
|
return; |
|
|
|
} |
|
|
|
cmd_container.cmdimpl(paramN, paraV, ack); |
|
|
|
if (cmd_container.cmdimpl) { |
|
|
|
cmd_container.cmdimpl(paramN, paraV, &ack); |
|
|
|
} else { |
|
|
|
logger->info("cmd:{} not impl", cmdname); |
|
|
|
} |
|
|
|
} else { |
|
|
|
ack.ecode = err::kcmd_not_found; |
|
|
|
if (cxt.private_fn_processer) cxt.private_fn_processer(cmdname, paramN, paraV, &ack); |
|
|
|
} |
|
|
|
|
|
|
|
if (cxt.ackprocesser) cxt.ackprocesser(cmdname, &ack); |
|
|
|
if (ack.ecode != err::ksucc) cxt.breakflag = true; |
|
|
|
} |
|
|
|
/*******************************************************************************
|
|
|
|
* TOOLS * |
|
|
|
*******************************************************************************/ |
|
|
|
string ExtAPIService::tools_dumpbuffer(uint8_t *data, int32_t len) { |
|
|
|
int32_t align = 32; |
|
|
|
string ret; |
|
|
|
|
|
|
|
for (int32_t i = 0; i < len;) { |
|
|
|
for (int32_t j = 0; j < align; j++) { |
|
|
|
if (i + j < len) { |
|
|
|
ret += fmt::format("{:02X} ", data[i + j]); |
|
|
|
} else { |
|
|
|
ret += " "; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ret += "|"; |
|
|
|
|
|
|
|
for (int32_t j = 0; j < align; j++) { |
|
|
|
if (i + j < len) { |
|
|
|
if (isprint(data[i + j])) { |
|
|
|
ret += fmt::format("{}", data[i + j]); |
|
|
|
} else { |
|
|
|
ret += "."; |
|
|
|
} |
|
|
|
} else { |
|
|
|
ret += " "; |
|
|
|
} |
|
|
|
} |
|
|
|
ret += "\n"; |
|
|
|
} |
|
|
|
return ret; |
|
|
|
} |