cwUi.h/cpp,cwUiTest.h/cpp, cwUiDecls.h : Moved public variables to cwUiDecls.h. Added a version of create() which uses an arg_t record. Added parseArgs() and releaseArgs().

This commit is contained in:
kevin 2021-01-20 13:10:56 -05:00
parent 5154c9eb53
commit c929804d4c
5 changed files with 239 additions and 115 deletions

128
cwUi.cpp
View File

@ -2,6 +2,7 @@
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwFileSys.h"
#include "cwThread.h"
#include "cwObject.h"
#include "cwWebSock.h"
@ -37,13 +38,14 @@ namespace cw
typedef struct ui_str
{
unsigned eleAllocN; // size of eleA[]
unsigned eleN; // count of ele's in use
ele_t** eleA; // eleA[ eleAllocN ]
unsigned eleAllocN; // size of eleA[]
unsigned eleN; // count of ele's in use
ele_t** eleA; // eleA[ eleAllocN ]
uiCallback_t uiCbFunc; // app. cb func
void* uiCbArg; // app. cb func arg.
sendCallback_t sendCbFunc;
void* sendCbArg;
object_t* uiRsrc; // default ui resource object
appIdMapRecd_t* appIdMap; // map of application parent/child/js id's
char* buf; // buf[bufN] output message formatting buffer
unsigned bufN; //
@ -87,7 +89,8 @@ namespace cw
mem::release(p->sessA);
mem::release(p->eleA);
mem::release(p->buf);
mem::release(p->buf);
p->uiRsrc->free();
mem::release(p);
return rc;
@ -543,10 +546,10 @@ namespace cw
rc_t _createFromObj( ui_t* p, const object_t* o, unsigned wsSessId, unsigned parentUuId )
{
rc_t rc = kOkRC;
const object_t* po = nullptr;
ele_t* parentEle = nullptr;
char* eleName = nullptr;
rc_t rc = kOkRC;
const object_t* po = nullptr;
ele_t* parentEle = nullptr;
char* eleName = nullptr;
// locate the the 'parent' ele name value object
if((po = o->find("parent",kNoRecurseFl | kOptionalFl)) == nullptr )
@ -726,6 +729,15 @@ namespace cw
return rc;
}
// Instantiate any resource elements refered to by the ui cfg 'uiCfgFn'.
rc_t _initDefaultUiRsrc( ui_t* p, unsigned wsSessId )
{
rc_t rc = kOkRC;
if( p->uiRsrc != nullptr )
if((rc = _createFromObj( p, p->uiRsrc, wsSessId, kInvalidId )) != kOkRC )
rc = cwLogError(rc,"Default UI creation failed.");
return rc;
}
}
}
@ -736,6 +748,7 @@ cw::rc_t cw::ui::create(
void* sendCbArg,
uiCallback_t uiCbFunc,
void* uiCbArg,
const object_t* uiRsrc,
const appIdMap_t* appIdMapA,
unsigned appIdMapN,
unsigned fmtBufByteN )
@ -764,7 +777,8 @@ cw::rc_t cw::ui::create(
p->sendCbArg = sendCbArg;
p->buf = mem::allocZ<char>(fmtBufByteN);
p->bufN = fmtBufByteN;
p->uiRsrc = uiRsrc == nullptr ? nullptr : uiRsrc->duplicate();
// create the root element
if((ele = _createEle(p, nullptr, kRootAppId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId )
{
@ -870,6 +884,9 @@ cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* msg, unsi
switch( opId )
{
case kInitOpId:
// if the app cfg included a reference to a UI resource file then instantiate it here
_initDefaultUiRsrc( p, wsSessId );
// Pass on the 'init' msg to the app.
p->uiCbFunc( p->uiCbArg, wsSessId, opId, kInvalidId, kInvalidId, kInvalidId, nullptr );
break;
@ -1203,11 +1220,81 @@ namespace cw
}
}
cw::rc_t cw::ui::ws::parseArgs( const object_t& o, args_t& args, const char* object_label )
{
rc_t rc = kOkRC;
const object_t* op = &o;
char* uiCfgFn = nullptr;
memset(&args,0,sizeof(args));
// if no 'ui' cfg record was given then skip
if( object_label == nullptr )
op = &o;
else
if((op = o.find(object_label)) == nullptr )
return cwLogError(kLabelNotFoundRC,"The ui configuration label '%s' was not found.", cwStringNullGuard(object_label));
if((rc = op->getv(
"physRootDir", args.physRootDir, "dfltPageFn", args.dfltPageFn, "port", args.port, "rcvBufByteN", args.rcvBufByteN,
"xmtBufByteN", args.xmtBufByteN, "fmtBufByteN", args.fmtBufByteN, "websockTimeOutMs", args.wsTimeOutMs, "uiCfgFn", uiCfgFn )) != kOkRC )
{
rc = cwLogError(rc,"'ui' cfg. parse failed.");
}
// if a default UI resource script was given then convert it into an object
if( uiCfgFn != nullptr )
{
char* fn = filesys::makeFn( args.physRootDir, uiCfgFn, nullptr, nullptr );
if((rc = objectFromFile(fn,args.uiRsrc)) != kOkRC )
rc = cwLogError(rc,"An error occurred while parsing the UI resource script in '%s'.", cwStringNullGuard(uiCfgFn));
mem::release(fn);
}
return rc;
}
cw::rc_t cw::ui::ws::releaseArgs( args_t& args )
{
if( args.uiRsrc != nullptr )
args.uiRsrc->free();
return kOkRC;
}
cw::rc_t cw::ui::ws::create( handle_t& h,
const args_t& args,
void* cbArg,
uiCallback_t uiCbFunc,
const object_t* uiRsrc,
const appIdMap_t* appIdMapA,
unsigned appIdMapN,
websock::cbFunc_t wsCbFunc )
{
return create(h,
args.port,
args.physRootDir,
cbArg,
uiCbFunc,
uiRsrc,
appIdMapA,
appIdMapN,
wsCbFunc,
args.dfltPageFn,
args.wsTimeOutMs,
args.rcvBufByteN,
args.xmtBufByteN,
args.fmtBufByteN );
}
cw::rc_t cw::ui::ws::create( handle_t& h,
unsigned port,
const char* physRootDir,
void* cbArg,
uiCallback_t uiCbFunc,
const object_t* uiRsrc,
const appIdMap_t* appIdMapA,
unsigned appIdMapN,
websock::cbFunc_t wsCbFunc,
@ -1242,7 +1329,7 @@ cw::rc_t cw::ui::ws::create( handle_t& h,
}
// create the ui
if((rc = ui::create(p->uiH, _webSockSend, p, uiCbFunc, cbArg, appIdMapA, appIdMapN, fmtBufByteN )) != kOkRC )
if((rc = ui::create(p->uiH, _webSockSend, p, uiCbFunc, cbArg, uiRsrc, appIdMapA, appIdMapN, fmtBufByteN )) != kOkRC )
{
cwLogError(rc,"UI object create failed.");
goto errLabel;
@ -1281,7 +1368,7 @@ cw::rc_t cw::ui::ws::destroy( handle_t& h )
return rc;
}
cw::rc_t cw::ui::ws::exec( handle_t h, unsigned timeOutMs )
cw::rc_t cw::ui::ws::exec( handle_t h )
{
rc_t rc = kOkRC;
ui_ws_t* p = _handleToPtr(h);
@ -1327,7 +1414,6 @@ namespace cw
{
ws::handle_t wsUiH;
thread::handle_t thH;
unsigned wsTimeOutMs;
} ui_ws_srv_t;
ui_ws_srv_t* _handleToPtr(handle_t h )
@ -1352,7 +1438,7 @@ namespace cw
ui_ws_srv_t* p = static_cast<ui_ws_srv_t*>(arg);
rc_t rc;
if((rc = ws::exec(p->wsUiH,p->wsTimeOutMs)) != kOkRC )
if((rc = ws::exec(p->wsUiH)) != kOkRC )
{
cwLogError(rc,"Websocket UI exec failed.");
}
@ -1368,6 +1454,7 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
const char* physRootDir,
void* cbArg,
uiCallback_t uiCbFunc,
const object_t* uiRsrc,
const appIdMap_t* appIdMapA,
unsigned appIdMapN,
websock::cbFunc_t wsCbFunc,
@ -1383,7 +1470,7 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
ui_ws_srv_t* p = mem::allocZ<ui_ws_srv_t>();
if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN, fmtBufByteN )) != kOkRC )
if((rc = ws::create(p->wsUiH, port, physRootDir, cbArg, uiCbFunc, uiRsrc,appIdMapA, appIdMapN, wsCbFunc, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN, fmtBufByteN )) != kOkRC )
{
cwLogError(rc,"The websock UI creationg failed.");
goto errLabel;
@ -1395,8 +1482,6 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
goto errLabel;
}
p->wsTimeOutMs = websockTimeOutMs;
h.set(p);
errLabel:
@ -1407,7 +1492,7 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
}
cw::rc_t cw::ui::srv::create( handle_t& h,
const args_t& args,
const ws::args_t& args,
void* cbArg,
uiCallback_t uiCbFunc,
const appIdMap_t* appIdMapA,
@ -1419,13 +1504,14 @@ cw::rc_t cw::ui::srv::create( handle_t& h,
args.physRootDir,
cbArg,
uiCbFunc,
args.uiRsrc,
appIdMapA,
appIdMapN,
wsCbFunc,
args.dfltHtmlPageFn,
args.timeOutMs,
args.recvBufByteN,
args.xmitBufByteN,
args.dfltPageFn,
args.wsTimeOutMs,
args.rcvBufByteN,
args.xmtBufByteN,
args.fmtBufByteN );
}

108
cwUi.h
View File

@ -9,63 +9,14 @@ namespace cw
{
typedef handle<struct ui_str> handle_t;
enum
{
kHttpProtocolId = 1,
kUiProtocolId = 2
};
typedef enum
{
kInvalidOpId,
kConnectOpId,
kInitOpId,
kValueOpId,
kEchoOpId,
kIdleOpId,
kDisconnectOpId
} opId_t;
typedef enum
{
kInvalidTId,
kBoolTId,
kIntTId,
kUIntTId,
kFloatTId,
kDoubleTId,
kStringTId
} dtypeId_t;
enum
{
kRootUuId = 0,
kRootAppId,
};
typedef struct
{
dtypeId_t tid;
union
{
bool b;
int i;
unsigned u;
float f;
double d;
const char* s;
} u;
} value_t;
typedef struct appIdMap_str
{
unsigned parentAppId;
unsigned appId;
const char* eleName;
} appIdMap_t;
// Callback for application notification.
// (e.g. the GUI changed the value of a UI element)
typedef rc_t (*uiCallback_t)( void* cbArg, unsigned wsSessId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* value );
// Callback with messages for the GUI as JSON strings
// { "op":"create", "type":<>, "appId":<>, "parentUuId":<>, "name":<> (type specific fields) }
// { "op":""value", "uuid":<> "value":<> }
typedef rc_t (*sendCallback_t)( void* cbArg, unsigned wsSessId, const void* msg, unsigned msgByteN );
rc_t create(
@ -74,6 +25,7 @@ namespace cw
void* sendCbArg,
uiCallback_t uiCbFunc,
void* uiCbArg,
const object_t* uiRsrc = nullptr,
const appIdMap_t* appIdMapA = nullptr,
unsigned appIdMapN = 0,
unsigned fmtBufByteN = 4096 );
@ -87,7 +39,7 @@ namespace cw
rc_t onDisconnect( handle_t h, unsigned wsSessId );
rc_t onReceive( handle_t h, unsigned wsSessId, const void* msg, unsigned byteN );
unsigned findElementAppId( handle_t h, unsigned parentUuId, const char* eleName );
unsigned findElementAppId( handle_t h, unsigned parentUuId, const char* eleName );
unsigned findElementUuId( handle_t h, unsigned parentUuId, const char* eleName );
unsigned findElementUuId( handle_t h, unsigned parentUuId, unsigned appId );
const char* findElementName( handle_t h, unsigned uuId );
@ -124,6 +76,7 @@ namespace cw
// Register parent/child/name app id's
rc_t registerAppIdMap( handle_t h, const appIdMap_t* map, unsigned mapN );
// Send a value from the application to the UI via a JSON messages.
rc_t sendValueBool( handle_t h, unsigned wsSessId, unsigned uuId, bool value );
rc_t sendValueInt( handle_t h, unsigned wsSessId, unsigned uuId, int value );
rc_t sendValueUInt( handle_t h, unsigned wsSessId, unsigned uuId, unsigned value );
@ -135,11 +88,36 @@ namespace cw
{
typedef handle<struct ui_ws_str> handle_t;
typedef struct args_str
{
const char* physRootDir;
const char* dfltPageFn;
object_t* uiRsrc;
unsigned port;
unsigned rcvBufByteN;
unsigned xmtBufByteN;
unsigned fmtBufByteN;
unsigned wsTimeOutMs;
} args_t;
rc_t parseArgs( const object_t& o, args_t& args, const char* object_label=nullptr );
rc_t releaseArgs( args_t& args );
rc_t create( handle_t& h,
const args_t& args,
void* cbArg,
uiCallback_t uiCbFunc,
const object_t* uiRsrc = nullptr,
const appIdMap_t* appIdMapA = nullptr,
unsigned appIdMapN = 0,
websock::cbFunc_t wsCbFunc = nullptr );
rc_t create( handle_t& h,
unsigned port,
const char* physRootDir,
void* cbArg,
uiCallback_t uiCbFunc,
const object_t* uiRsrc = nullptr,
const appIdMap_t* appIdMapA = nullptr,
unsigned appIdMapN = 0,
websock::cbFunc_t wsCbFunc = nullptr,
@ -153,7 +131,7 @@ namespace cw
// This function should be called periodically to send and receive
// queued messages to and from the websocket.
rc_t exec( handle_t h, unsigned timeOutMs );
rc_t exec( handle_t h );
// This function executes the internal default websock callback function.
// It is useful if the user provides a custom websock callback function
@ -170,23 +148,12 @@ namespace cw
namespace srv
{
typedef struct args_str
{
const char* physRootDir;
const char* dfltHtmlPageFn;
unsigned port;
unsigned timeOutMs;
unsigned recvBufByteN;
unsigned xmitBufByteN;
unsigned fmtBufByteN;
} args_t;
typedef handle<struct ui_ws_srv_str> handle_t;
rc_t create( handle_t& h,
const args_t& args,
const ws::args_t& args,
void* cbArg,
uiCallback_t uiCbFunc,
uiCallback_t uiCbFunc,
const appIdMap_t* appIdMapA = nullptr,
unsigned appIdMapN = 0,
websock::cbFunc_t wsCbFunc = nullptr );
@ -196,6 +163,7 @@ namespace cw
const char* physRootDir,
void* cbArg,
uiCallback_t uiCbFunc,
const object_t* uiRsrc = nullptr,
const appIdMap_t* appIdMapA = nullptr,
unsigned appIdMapN = 0,
websock::cbFunc_t wsCbFunc = nullptr,

View File

@ -5,6 +5,61 @@ namespace cw
{
namespace ui
{
enum
{
kHttpProtocolId = 1,
kUiProtocolId = 2
};
typedef enum
{
kInvalidOpId,
kConnectOpId, // A new user interface instance was connected
kInitOpId, // A user interface instance was created and is available (new ui elements can now be added)
kValueOpId, // Used by the user interface instance to send a value of a ui element to the application.
kEchoOpId, // Used by the user interface instance to request the current value of a ui element from the application.
kIdleOpId, // The application is idle and waiting for the next event from the ui instance.
kDisconnectOpId // A user interface instance was disconnected
} opId_t;
typedef enum
{
kInvalidTId,
kBoolTId,
kIntTId,
kUIntTId,
kFloatTId,
kDoubleTId,
kStringTId
} dtypeId_t;
enum
{
kRootUuId = 0,
kRootAppId,
};
typedef struct
{
dtypeId_t tid;
union
{
bool b;
int i;
unsigned u;
float f;
double d;
const char* s;
} u;
} value_t;
typedef struct appIdMap_str
{
unsigned parentAppId;
unsigned appId;
const char* eleName;
} appIdMap_t;
}
}

View File

@ -15,11 +15,13 @@ namespace cw
{
typedef struct ui_test_str
{
const char* uiCfgFn;
srv::handle_t wsUiSrvH;
//const char* uiCfgFn; // Resource file name
srv::handle_t wsUiSrvH; //
std::atomic<bool> quitFl;
// Application values
bool appCheckFl;
unsigned appSelOptAppId;
int appInteger;
@ -33,8 +35,10 @@ namespace cw
} ui_test_t;
// Application Id's for UI elements
enum
{
// Programatically created UI elements
kDivId,
kBtnId,
kCheckId,
@ -48,6 +52,7 @@ namespace cw
kFloatId,
kProgressId,
// Resource Based elements
kPanelDivId,
kPanelBtn1Id,
kPanelCheck1Id,
@ -70,8 +75,7 @@ namespace cw
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
//registerAppIdMap(uiH, mapA, sizeof(mapA)/sizeof(mapA[0]));
// Create a UI elements programatically.
if((rc = createDiv( uiH, divUuId, wsSessId, kInvalidId, "myDivId", kDivId, "divClass", "My Panel" )) != kOkRC )
goto errLabel;
@ -106,8 +110,8 @@ namespace cw
if((rc = createProg( uiH, uuid, wsSessId, divUuId, "myProgressId", kProgressId, "progressClass", "Progress", 0, 10, 5 )) != kOkRC )
goto errLabel;
if((rc = createFromFile( uiH, p->uiCfgFn, wsSessId )) != kOkRC )
goto errLabel;
//if((rc = createFromFile( uiH, p->uiCfgFn, wsSessId )) != kOkRC )
// goto errLabel;
errLabel:
return rc;
@ -290,20 +294,12 @@ namespace cw
}
}
cw::rc_t cw::ui::test( )
cw::rc_t cw::ui::test( const object_t* cfg )
{
rc_t rc = kOkRC;
const char* physRootDir = "/home/kevin/src/cwtest/src/libcw/html/uiTest";
const char* dfltPageFn = "index.html";
int port = 5687;
unsigned rcvBufByteN = 2048;
unsigned xmtBufByteN = 2048;
unsigned fmtBufByteN = 4096;
unsigned websockTimeOutMs = 50;
ui_test_t* app = mem::allocZ<ui_test_t>();
app->quitFl.store(false);
rc_t rc = kOkRC;
ui::ws::args_t args = {};
// Application Id's for the resource based UI elements.
appIdMap_t mapA[] =
{
{ kRootAppId, kPanelDivId, "panelDivId" },
@ -316,11 +312,28 @@ cw::rc_t cw::ui::test( )
{ kSelId, kOpt1Id, "myOpt1" },
{ kSelId, kOpt2Id, "myOpt2" },
{ kSelId, kOpt3Id, "myOpt3" },
};
unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);
ui_test_t* app = mem::allocZ<ui_test_t>();
if( cfg == nullptr )
{
cwLogError(kInvalidArgRC,"ui::test() was not passed a valid cfg. object.");
goto errLabel;
}
if((rc = parseArgs(*cfg, args )) != kOkRC )
{
cwLogError(rc,"UI parse args failed in ui::test()");
goto errLabel;
}
app->quitFl.store(false);
// Initial values for the test applications
app->appCheckFl = true;
app->appSelOptAppId = kOption1Id;
app->appInteger = 5;
@ -333,10 +346,10 @@ cw::rc_t cw::ui::test( )
app->appSelId = kOpt3Id;
app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg";
//app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg";
// create the UI server
if((rc = srv::create(app->wsUiSrvH, port, physRootDir, app, _uiTestCallback, mapA, mapN, nullptr, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN, fmtBufByteN )) != kOkRC )
if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, nullptr )) != kOkRC )
return rc;
@ -354,6 +367,8 @@ cw::rc_t cw::ui::test( )
}
errLabel:
ui::ws::releaseArgs(args);
rc_t rc1 = kOkRC;
if( app->wsUiSrvH.isValid() )
rc1 = srv::destroy(app->wsUiSrvH);

View File

@ -2,6 +2,6 @@ namespace cw
{
namespace ui
{
rc_t test();
rc_t test(const object_t* cfg );
}
}