“工欲善其事,必先利其器!”
本文创建了一个在命令窗口下的Nebula命令控制台.可以直接执行脚本指令,察看当前Nebula的系统状态,察看NOH树及当前工作对象信息,对于调试和控制Nebula2程序非常实用方便!
本来Nebula2在nGui系统中已经实现了一个叫做nGuiCmdEntry的控件,这个控件就具备即时执行脚本的功能,但我们在实际开发中可能会抛弃nGui而使用其他如CEGUI的界面系统进行替换,所以我们需要一个通用的解决方案:适用Windows控制台作为Nebula控制台的载体,于是就产生了本文的nConConServer。
在nConConServer中借鉴了nGuiCmdEntry中对用户输入脚本指令的处理方式,并加入了几条实用的全局指令,使用”-help”指令查看全局指令帮助,代码如下:
/****************************************************************************/
/* Nebula2 - Tutorial Utils */
/* nConConServer - 控制台下的Nebula2指令控制台 */
/* author: happykevins */
/****************************************************************************/
#pragma once
#include "kernel/nkernelserver.h"
#include "kernel/nscriptserver.h"
#include "kernel/nautoref.h"
#define KS_USE_STDOUT
#include "nkernelinfo.h"
#undef KS_USE_STDOUT
#define LINE_BUF_MAX_SIZE 512
#define GLOBAL_CMD_TOKEN '-'
class nConConServer : public nRoot
{
public:
/// constructor
nConConServer();
/// destructor
virtual ~nConConServer();
/// Toggle Mode
void ToggleMode();
/// Poll Commands
void PollCmd();
/// Execute String
void ExecuteCommand(const char* szCmdStr);
protected:
/// set edit line buffer
void SetEditLine(const char* szCmdBuf);
/// execute the current command
void ExecuteEditLine();
/// checking the char effect
bool CheckingChar(char ch);
/// global function check
void ExecuteGlobal();
/// add current command to history
void AddCommandToHistory();
/// recall next command in history
void RecallNextCommand();
/// recall previous command in history
void RecallPrevCommand();
/// set local cwd
void SetCwd(nRoot* cwd);
/// get local cwd (can return 0)
nRoot* GetCwd();
/// update the tab completion stuff
void UpdateTabComplete();
/// perform a tab completion
void DoTabCompletion();
private:
nKernelInfoHelper m_info;
bool m_bRecvCmd;
size_t m_nLineBufPtr;
char m_szLineBuf[LINE_BUF_MAX_SIZE];
nString editLine;
nAutoRef<nScriptServer> refScriptServer;
nRef<nRoot> refCwd;
};
// nconconserver.cpp
#pragma warning(push)
#pragma warning(disable: 4267 4244)
#include "nconconserver.h"
#include "nutildefs.h"
///----------------------------------------------------------------------------
/// constructor
nConConServer::nConConServer() :
m_bRecvCmd(false),
refScriptServer("/sys/servers/script")
{
}
/// destructor
nConConServer::~nConConServer()
{
}
/// Toggle Mode
void nConConServer::ToggleMode()
{
m_bRecvCmd = !m_bRecvCmd;
if ( m_bRecvCmd )
{
// Welcome Text
printf(" ");
printf("%s ", "=========================");
printf("%s ", " ConConServer Welcome :)" );
printf("%s ", "=========================");
}
}
/// Poll Commands
void nConConServer::PollCmd()
{
while ( m_bRecvCmd )
{
printf(" :>");
memset(m_szLineBuf, 0, LINE_BUF_MAX_SIZE);
m_nLineBufPtr = 0;
char ch;
while ( (ch = getchar()) != ' ' )
{
if ( CheckingChar(ch) )
{
continue;
}
m_szLineBuf[m_nLineBufPtr] = ch;
m_nLineBufPtr++;
}
m_szLineBuf[m_nLineBufPtr] = '\0';
ExecuteCommand(m_szLineBuf);
}
}
/// Execute String
void nConConServer::ExecuteCommand(const char* szCmdStr)
{
SetEditLine(m_szLineBuf);
ExecuteEditLine();
}
/// set edit line buffer
void nConConServer::SetEditLine(const char* szCmdBuf)
{
editLine.Set(szCmdBuf);
}
/// execute the current command
void nConConServer::ExecuteEditLine()
{
// check and run global function
if ( GLOBAL_CMD_TOKEN == editLine[0] )
{
ExecuteGlobal();
return;
}
// check if script server is ok
if ( !refScriptServer.isvalid() )
{
printf("Error: Script Server is not Startup! ");
return;
}
nScriptServer* scriptServer = this->refScriptServer.get();
// make local cwd global
nKernelServer::Instance()->PushCwd(this->GetCwd());
// and run the command through the script server
nString result = 0;
bool failOnError = scriptServer->GetFailOnError();
scriptServer->SetFailOnError(false);
scriptServer->Run(this->editLine.Get(), result);
scriptServer->SetFailOnError(failOnError);
if (result.IsValid())
{
printf("%s ", result.Get());
}
this->AddCommandToHistory();
this->editLine.Clear();
// set new local cwd
nRoot* newCwd = nKernelServer::Instance()->GetCwd();
if (newCwd != this->GetCwd())
{
this->SetCwd(nKernelServer::Instance()->GetCwd());
this->UpdateTabComplete();
}
// restore previous cwd
nKernelServer::Instance()->PopCwd();
}
/// checking the char effect
bool nConConServer::CheckingChar(char ch)
{
return false;
}
/// global function check
void nConConServer::ExecuteGlobal()
{
char* szRealCmd = m_szLineBuf + 1;
if ( 0 == strcmp("help", szRealCmd) )
{
printf("%s ", "=========================");
printf("%s ", "ConConServer Help List :)");
printf("%s ", "=========================");
printf("%s ", "*Command List: ");
printf("%s ", " help: Show this Text!");
printf("%s ", " exit: Exit ConConServer Mode.");
printf("%s ", " noh : Display a Tree View of Nebula NOH.");
printf("%s ", " cls : Show Current Registered Class List.");
printf("%s ", " cwd : Show Current Working Object Info.");
printf("%s ", " mtd : Show the Methods that Current Working Object Support.");
}
else if ( 0 == strcmp("exit", szRealCmd) )
{
ToggleMode();
}
else if ( 0 == strcmp("noh", szRealCmd) )
{
m_info.LogNOH(this->GetCwd()->GetFullName().Get());
}
else if ( 0 == strcmp("cls", szRealCmd) )
{
m_info.LogCLS();
}
else if ( 0 == strcmp("cwd", szRealCmd) )
{
printf("CWD : %s ", this->GetCwd()->GetFullName().Get() );
printf("Class : %s ", this->GetCwd()->GetClass()->GetProperName() );
printf("Parent: %s ", this->GetCwd()->GetClass()->GetSuperClass()->GetProperName() );
}
else if ( 0 == strcmp("mtd", szRealCmd) )
{
nHashList* obj = this->GetCwd()->GetClass()->GetCmdList();
nCmdProto* node = (nCmdProto*)obj->GetHead();
do
{
printf("%s ", node->GetProtoDef());
} while ( (node = (nCmdProto*)node->GetSucc()) );
}
else
{
printf("%s ", "Error: '-' follows Unkown Globle Command!");
}
}
/// add current command to history
void nConConServer::AddCommandToHistory()
{
// empty.
}
/// recall next command in history
void nConConServer::RecallNextCommand()
{
// empty.
}
/// recall previous command in history
void nConConServer::RecallPrevCommand()
{
// empty.
}
/// set local cwd
void nConConServer::SetCwd(nRoot* cwd)
{
this->refCwd = cwd;
}
/// get local cwd (can return 0)
nRoot* nConConServer::GetCwd()
{
if ( !this->refCwd.isvalid() )
{
this->SetCwd(nKernelServer::Instance()->GetCwd());
}
return this->refCwd.get();
}
/// update the tab completion stuff
void nConConServer::UpdateTabComplete()
{
// ...
}
/// perform a tab completion
void nConConServer::DoTabCompletion()
{
// ...
}
///----------------------------------------------------------------------------
/// 声明为Nebula2脚本支持类
nNebulaScriptModule(nConConServer, nconconserver, "nroot");
/// ConConServer开关
static void n_toggle(void* slf, nCmd* cmd);
/// 执行控制台指令
static void n_exec(void* slf, nCmd* cmd);
/// Regist Commands
void nNebulaScriptInitCmds(nconconserver) (nClass* cl)
{
cl->BeginCmds();
cl->AddCmd("v_toggle_v", 'TOGL', n_toggle);
cl->AddCmd("v_exec_s", 'EXEC', n_exec);
cl->EndCmds();
}
/// ToggleMode的脚本支持
static void n_toggle(void* slf, nCmd* cmd)
{
nConConServer* self = (nConConServer*) slf;
self->ToggleMode();
}
/// ExecCommand的脚本支持
static void n_exec(void* slf, nCmd* cmd)
{
nConConServer* self = (nConConServer*) slf;
const char* szCmdStr = cmd->In()->GetS();
self->ExecuteCommand(szCmdStr);
}
///----------------------------------------------------------------------------
#pragma warning(pop)
其中ToggleMode()方法是切换到控制台模式的开关,已经加入了对脚本的支持,可以直接通过InputServer映射到一个按键上。
PollCmd()命令用于阻塞接受用户输入的指令。由于是以Windows控制台实现,所以只能以阻塞模式接受用户输入,在执行PollCmd()函数后应用程序会停止运行,直到用户在控制台中输入”-exit”指令,nConConServer才会把控制权释放掉。
下面给出nConConServer的使用事例代码:
/****************************************************************************/
/* Nebula2 - Tutorial 12 */
/* A Console Server for Console */
/* author: happykevins */
/****************************************************************************/
///----------------------------------------------------------------------------
/// +必要头文件
// nebula2 includes
#include "kernel/nkernelserver.h"
#include "script/ntclserver.h"
// ConConServer头文件
#include "../NebulaUtils/nConConServer.h"
// Tutorial工具库:一些通用的宏定义
#include "../NebulaUtils/nutildefs.h"
/// -必要头文件
///----------------------------------------------------------------------------
///----------------------------------------------------------------------------
/// +链接库
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "d_nkernel.lib")
#pragma comment(lib, "d_nnebula.lib")
#pragma comment(lib, "d_microtcl.lib")
/// -链接库
///----------------------------------------------------------------------------
///----------------------------------------------------------------------------
/// +声明使用的Nebula2 Package&Module
nNebulaUseModule(ntclserver);
/// -声明使用的Nebula2 Package&Module
///----------------------------------------------------------------------------
nKernelServer* ks = NULL;
///----------------------------------------------------------------------------
/// +初始化环境,创建需要的Server
///
bool InitApp()
{
/// 创建KernelServer
ks = n_new(nKernelServer);
///----------------------------------------------------------------------------
/// +向KernelServer中添加Package&Module
nNebulaAddModule(ntclserver);
/// +向KernelServer中添加Package&Module
///----------------------------------------------------------------------------
ks->New("ntclserver", "/sys/servers/script");
return true;
}
///
/// -初始化环境,创建需要的Server
///----------------------------------------------------------------------------
///----------------------------------------------------------------------------
/// +退出程序,清理资源
///
bool CloseApp()
{
/// 销毁KernelServer
n_delete(ks);
return true;
}
///
/// -退出程序,清理资源
///----------------------------------------------------------------------------
///----------------------------------------------------------------------------
/// +Application
int main(int argc, const char** argv)
{
/// 初始化Application
if ( !InitApp() )
{
n_error("程序初始化失败! ");
return 0;
}
/// ConConServer
nConConServer console;
/// 切出ConConServer
console.ToggleMode();
/// 轮训控制台指令,(阻塞模式)
console.PollCmd();
/// 释放资源
if ( !CloseApp() )
{
n_error("释放资源失败! ");
return 0;
}
return 0;
}
/// -Application
///----------------------------------------------------------------------------