cwUI.h/cpp,cwUiTest.cpp,html/uiTest/ui.js : Implemented 'echo' protocol to send values to the browser. Implemented initial default HTML class attribute assignments.

This commit is contained in:
kevin.larke 2020-04-24 10:20:25 -04:00
parent 9288040004
commit ebfe4dd224
4 changed files with 310 additions and 38 deletions

114
cwUi.cpp
View File

@ -310,7 +310,7 @@ namespace cw
if( i >= p->bufN )
return cwLogError(kBufTooSmallRC,"The UI message formatting buffer is too small. (size:%i bytes)", p->bufN);
printf("%s\n",p->buf);
//printf("%s\n",p->buf);
// send the message
rc = _websockSend( p, wsSessId, p->buf );
@ -403,7 +403,7 @@ namespace cw
goto errLabel;
}
printf("%s\n",p->buf);
//printf("%s\n",p->buf);
if((rc = _websockSend( p, wsSessId, p->buf )) != kOkRC )
{
@ -565,6 +565,31 @@ namespace cw
return ele;
}
ele_t* _parse_echo_msg( ui_t* p, const char* msg )
{
unsigned eleUuId = kInvalidId;
ele_t* ele = nullptr;
if( sscanf(msg, "echo %i",&eleUuId) != 1 )
{
cwLogError(kSyntaxErrorRC,"Invalid message from UI: '%s'.", msg );
goto errLabel;
}
// locate the element record
if((ele = _uuIdToEle( p, eleUuId )) == nullptr )
{
cwLogError(kInvalidIdRC,"UI message elment not found.");
goto errLabel;
}
errLabel:
return ele;
}
opId_t _labelToOpId( const char* label )
{
typedef struct
@ -578,6 +603,7 @@ namespace cw
{ kConnectOpId, "connect" },
{ kInitOpId, "init" },
{ kValueOpId, "value" },
{ kEchoOpId, "echo" },
{ kDisconnectOpId, "disconnect" },
{ kInvalidOpId, "<invalid>" },
};
@ -588,6 +614,32 @@ namespace cw
return kInvalidOpId;
}
template< typename T >
rc_t _sendValue( ui_t* p, unsigned wsSessId, unsigned uuId, const char* vFmt, const T& value, int vbufN=32 )
{
rc_t rc = kOkRC;
if( p->sendCbFunc != nullptr )
{
const char* mFmt = "{ \"op\":\"value\", \"uuId\":%i, \"value\":%s }";
const int mbufN = 128;
char vbuf[vbufN];
char mbuf[mbufN];
if( snprintf(vbuf,vbufN,vFmt,value) >= vbufN-1 )
return cwLogError(kBufTooSmallRC,"The value msg buffer is too small.");
if( snprintf(mbuf,mbufN,mFmt,uuId,vbuf) >= mbufN-1 )
return cwLogError(kBufTooSmallRC,"The msg buffer is too small.");
p->sendCbFunc(p->sendCbArg,wsSessId,mbuf,strlen(mbuf));
}
return rc;
}
}
}
@ -662,11 +714,15 @@ cw::rc_t cw::ui::destroy( handle_t& h )
cw::rc_t cw::ui::onConnect( handle_t h, unsigned wsSessId )
{
ui_t* p = _handleToPtr(h);
p->uiCbFunc( p->uiCbArg, wsSessId, kConnectOpId, kInvalidId, kInvalidId, kInvalidId, nullptr );
return kOkRC;
}
cw::rc_t cw::ui::onDisconnect( handle_t h, unsigned wsSessId )
{
ui_t* p = _handleToPtr(h);
p->uiCbFunc( p->uiCbArg, wsSessId, kDisconnectOpId, kInvalidId, kInvalidId, kInvalidId, nullptr );
return kOkRC;
}
@ -698,6 +754,18 @@ cw::rc_t cw::ui::onReceive( handle_t h, unsigned wsSessId, const void* msg, unsi
}
break;
case kEchoOpId:
if((ele = _parse_echo_msg(p,(const char*)msg)) == nullptr )
cwLogError(kOpFailRC,"UI Echo message parse failed.");
else
{
unsigned parentEleAppId = ele->parent == nullptr ? kInvalidId : ele->parent->appId;
p->uiCbFunc( p->uiCbArg, wsSessId, opId, parentEleAppId, ele->uuId, ele->appId, nullptr );
}
break;
case kInvalidOpId:
cwLogError(kInvalidIdRC,"The UI received a NULL op. id.");
break;
@ -874,7 +942,43 @@ cw::rc_t cw::ui::registerAppIds( handle_t h, const appIdMap_t* map, unsigned ma
return rc;
}
cw::rc_t cw::ui::sendValueBool( handle_t h, unsigned wsSessId, unsigned uuId, bool value )
{
ui_t* p = _handleToPtr(h);
return _sendValue<int>(p,wsSessId,uuId,"%i",value?1:0);
}
cw::rc_t cw::ui::sendValueInt( handle_t h, unsigned wsSessId, unsigned uuId, int value )
{
ui_t* p = _handleToPtr(h);
return _sendValue<int>(p,wsSessId,uuId,"%i",value);
}
cw::rc_t cw::ui::sendValueUInt( handle_t h, unsigned wsSessId, unsigned uuId, unsigned value )
{
ui_t* p = _handleToPtr(h);
return _sendValue<unsigned>(p,wsSessId,uuId,"%i",value);
}
cw::rc_t cw::ui::sendValueFloat( handle_t h, unsigned wsSessId, unsigned uuId, float value )
{
ui_t* p = _handleToPtr(h);
return _sendValue<float>(p,wsSessId,uuId,"%f",value);
}
cw::rc_t cw::ui::sendValueDouble( handle_t h, unsigned wsSessId, unsigned uuId, double value )
{
ui_t* p = _handleToPtr(h);
return _sendValue<double>(p,wsSessId,uuId,"%f",value);
}
cw::rc_t cw::ui::sendValueString( handle_t h, unsigned wsSessId, unsigned uuId, const char* value )
{
ui_t* p = _handleToPtr(h);
// +10 allows for extra value buffer space for double quotes and slashed
return _sendValue<const char*>(p,wsSessId,uuId,"\"%s\"",value,strlen(value)+10);
}
namespace cw
{
@ -898,12 +1002,12 @@ namespace cw
rc_t _destroy( ui_ws_t* p )
{
rc_t rc;
if((rc = ui::destroy(p->uiH)) != kOkRC )
return rc;
if((rc = websock::destroy(p->wsH)) != kOkRC )
return rc;
if((rc = ui::destroy(p->uiH)) != kOkRC )
return rc;
mem::release(p);

11
cwUi.h
View File

@ -21,6 +21,7 @@ namespace cw
kConnectOpId,
kInitOpId,
kValueOpId,
kEchoOpId,
kDisconnectOpId
} opId_t;
@ -104,7 +105,13 @@ namespace cw
// Register parent/child/name app id's
rc_t registerAppIds( handle_t h, const appIdMap_t* map, unsigned mapN );
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 );
rc_t sendValueFloat( handle_t h, unsigned wsSessId, unsigned uuId, float value );
rc_t sendValueDouble( handle_t h, unsigned wsSessId, unsigned uuId, double value );
rc_t sendValueString( handle_t h, unsigned wsSessId, unsigned uuId, const char* value );
namespace ws
{
typedef handle<struct ui_ws_str> handle_t;
@ -134,6 +141,8 @@ namespace cw
websock::handle_t websockHandle( handle_t h );
ui::handle_t uiHandle( handle_t h );
}

View File

@ -18,6 +18,13 @@ namespace cw
const char* uiCfgFn;
srv::handle_t wsUiSrvH;
bool appCheckFl;
unsigned appSelectIndex;
int appInteger;
float appFloat;
int appProgress;
char* appString;
} ui_test_t;
enum
@ -31,7 +38,8 @@ namespace cw
kOption2Id,
kOption3Id,
kStringId,
kNumberId,
kIntegerId,
kFloatId,
kProgressId,
kPanelDivId,
@ -82,7 +90,10 @@ namespace cw
if((rc = createString( uiH, uuid, wsSessId, divUuId, "myStringId", kStringId, "stringClass", "String", "a string value" )) != kOkRC )
goto errLabel;
if((rc = createNumber( uiH, uuid, wsSessId, divUuId, "myNumberId", kNumberId, "numberClass", "Number", 10, 0, 100, 1, 0 )) != kOkRC )
if((rc = createNumber( uiH, uuid, wsSessId, divUuId, "myIntegerId", kIntegerId, "integerClass", "Integer", 10, 0, 100, 1, 0 )) != kOkRC )
goto errLabel;
if((rc = createNumber( uiH, uuid, wsSessId, divUuId, "myFloatId", kFloatId, "floatClass", "Float", 10.0, 0.53, 100.97, 1.0, 5 )) != kOkRC )
goto errLabel;
if((rc = createProgress( uiH, uuid, wsSessId, divUuId, "myProgressId", kProgressId, "progressClass", "Progress", 5, 0, 10 )) != kOkRC )
@ -107,27 +118,70 @@ namespace cw
case kCheckId:
printf("Check:%i\n", v->u.b);
p->appCheckFl = v->u.b;
break;
case kSelectId:
printf("Selected: optionId:%i\n", v->u.i);
p->appSelectIndex = v->u.i;
break;
case kStringId:
printf("String: %s\n",v->u.s);
case kNumberId:
if( v->tid == kIntTId )
printf("Number: %i\n",v->u.i);
else
printf("Number: %f\n",v->u.d);
mem::release(p->appString);
if( v->u.s != nullptr )
p->appString = mem::duplStr(v->u.s);
break;
case kIntegerId:
printf("Integer: %i\n",v->u.i);
p->appInteger = v->u.i;
break;
case kFloatId:
printf("Float: %f\n",v->u.f);
p->appFloat = v->u.f;
}
return rc;
}
rc_t _handleUiEchoMsg( ui_test_t* p, unsigned wsSessId, unsigned parentAppId, unsigned uuId, unsigned appId )
{
rc_t rc = kOkRC;
switch( appId )
{
case kCheckId:
sendValueBool( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appCheckFl );
break;
case kSelectId:
sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appSelectIndex );
break;
case kStringId:
sendValueString( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appString );
break;
case kIntegerId:
sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appInteger );
break;
case kFloatId:
sendValueFloat( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appFloat );
break;
case kProgressId:
sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appProgress );
break;
}
return rc;
}
// This function is called by the websocket with messages comring from a remote UI.
rc_t _uiTestCallback( void* cbArg, unsigned wsSessId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v )
{
@ -140,7 +194,7 @@ namespace cw
break;
case kDisconnectOpId:
cwLogInfo("Disconnect: wsSessId:%i.",wsSessId);
cwLogInfo("Disconnect: wsSessId:%i.",wsSessId);
break;
case kInitOpId:
@ -151,6 +205,10 @@ namespace cw
_handleUiValueMsg( p, wsSessId, parentAppId, uuId, appId, v );
break;
case kEchoOpId:
_handleUiEchoMsg( p, wsSessId, parentAppId, uuId, appId );
break;
case kInvalidOpId:
// fall through
default:
@ -176,6 +234,15 @@ cw::rc_t cw::ui::test( )
char sbuf[ sbufN+1 ];
ui_test_t* app = mem::allocZ<ui_test_t>();
app->appCheckFl = true;
app->appSelectIndex = 1;
app->appInteger = 5;
app->appFloat = 2.56;
app->appProgress = 7;
app->appString = mem::duplStr("fooz");
app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg";
// create the UI server
@ -207,7 +274,8 @@ cw::rc_t cw::ui::test( )
rc_t rc1 = kOkRC;
if( app->wsUiSrvH.isValid() )
rc1 = srv::destroy(app->wsUiSrvH);
mem::release(app->appString);
mem::release(app);
return rcSelect(rc,rc1);

View File

@ -232,8 +232,13 @@ function ui_send_value( ele, typeId, value )
function ui_send_bool_value( ele, value ) { ui_send_value(ele,'b',value); }
function ui_send_int_value( ele, value ) { ui_send_value(ele,'i',value); }
function ui_send_float_value( ele, value ) { ui_send_value(ele,'f',value); }
function ui_send_string_value( ele, value ) { ui_send_value(ele,'s',value); }
function ui_send_echo( ele )
{
ws_send("echo " + ele.id )
}
function ui_print_children( eleId )
{
@ -261,7 +266,7 @@ function ui_get_parent( parentId )
}
function ui_create_ele( parent_ele, ele_type, d )
function ui_create_ele( parent_ele, ele_type, d, dfltClassName )
{
// create the ctl object
var ele = dom_create_ele(ele_type);
@ -272,12 +277,17 @@ function ui_create_ele( parent_ele, ele_type, d )
{
ele.id = d.uuId;
if(d.hasOwnProperty('className') )
ele.className = d.className;
else
ele.className = dfltClassName;
if(d.hasOwnProperty('appId'))
ele.appId = d.appId;
else
ele.appId = null;
console.log("Created: " + ele_type + " parent:" + d.parentUuId + " id:" + ele.id + " appId:" + ele.appId)
//console.log("Created: " + ele_type + " parent:" + d.parentUuId + " id:" + ele.id + " appId:" + ele.appId)
parent_ele.appendChild(ele);
@ -286,12 +296,12 @@ function ui_create_ele( parent_ele, ele_type, d )
return ele
}
function ui_create_ctl( parent_ele, ele_type, label, d )
function ui_create_ctl( parent_ele, ele_type, label, d, dfltEleClassName )
{
// create an enclosing div
var div_ele = dom_create_ele("div");
div_ele.className = d.clas;
div_ele.className = dfltEleClassName + "Div"
parent_ele.appendChild( div_ele );
@ -307,18 +317,25 @@ function ui_create_ctl( parent_ele, ele_type, label, d )
div_ele.appendChild(label_ele)
}
return ui_create_ele( label_ele, ele_type, d );
return ui_create_ele( label_ele, ele_type, d, dfltEleClassName );
}
function ui_set_class_name( ele, d, dfltClassName )
{
if( d.hasOwnProperty("className") )
ele.className = d.className;
else
ele.className = dfltClassName
return ele
}
function ui_create_div( parent_ele, d )
{
var div_ele = ui_create_ele( parent_ele, "div", d );
var div_ele = ui_create_ele( parent_ele, "div", d, "uiDiv" );
if( div_ele != null )
{
div_ele.className = d.clas
var p_ele = dom_create_ele("p")
if( d.title != null && d.title.length > 0 )
@ -332,12 +349,12 @@ function ui_create_div( parent_ele, d )
function ui_create_title( parent_ele, d )
{
return ui_create_ele( parent_ele, "label", d );
return ui_create_ele( parent_ele, "label", d, "uiTitle" );
}
function ui_create_button( parent_ele, d )
{
var ele = ui_create_ctl( parent_ele, "button", null, d );
var ele = ui_create_ctl( parent_ele, "button", null, d, "uiButton" );
if( ele != null )
{
@ -350,7 +367,7 @@ function ui_create_button( parent_ele, d )
function ui_create_check( parent_ele, d )
{
var ele = ui_create_ctl( parent_ele, "input", d.title, d )
var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiCheck" )
if( ele != null )
{
@ -370,18 +387,18 @@ function ui_on_select( ele )
function ui_create_select( parent_ele, d )
{
var sel_ele = ui_create_ctl( parent_ele, "select", d.title, d );
var sel_ele = ui_create_ctl( parent_ele, "select", d.title, d, "uiSelect" );
sel_ele.onchange = function() { ui_on_select(this) }
return sel_ele;
}
function ui_create_option( parent_ele, d )
{
var opt_ele = ui_create_ele( parent_ele, "option", d );
var opt_ele = ui_create_ele( parent_ele, "option", d, "uiOption" );
if( opt_ele != null )
{
opt_ele.className = d.clas;
opt_ele.className = d.className;
opt_ele.innerHTML = d.title;
}
@ -390,13 +407,15 @@ function ui_create_option( parent_ele, d )
function ui_create_string( parent_ele, d )
{
var ele = ui_create_ctl( parent_ele, "input", d.title, d );
var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiString" );
if( ele != null )
{
ele.value = d.value;
ele.addEventListener('keyup', function(e) { if(e.keyCode===13){ ui_send_string_value(this, this.value); }} );
}
return ele;
}
function ui_number_keyup( e )
@ -414,14 +433,21 @@ function ui_number_keyup( e )
val = Number.parseInt(ele.value)
else
val = Number.parseFloat(ele.value)
if( !(ele.minValue<=val && val<=ele.maxValue))
ele.style.borderColor = "red"
else
{
ele.style.borderColor = ""
ui_send_int_value(ele,ele.value);
if( ele.decpl == 0 )
{
ui_send_int_value(ele,ele.value);
}
else
{
ui_send_float_value(ele,ele.value);
}
}
}
}
@ -429,7 +455,7 @@ function ui_number_keyup( e )
function ui_create_number( parent_ele, d )
{
var ele = ui_create_ctl( parent_ele, "input", d.title, d );
var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiNumber" );
if( ele != null )
{
@ -452,7 +478,7 @@ function ui_set_progress( ele_id, value )
function ui_create_progress( parent_ele, d )
{
var ele = ui_create_ctl( parent_ele, "progress", d.title, d );
var ele = ui_create_ctl( parent_ele, "progress", d.title, d, "uiProgress" );
if( ele != null )
{
@ -464,6 +490,62 @@ function ui_create_progress( parent_ele, d )
return ele
}
function ui_set_value( d )
{
console.log(d)
var ele = dom_id_to_ele(d.uuId.toString())
if( ele == null )
console.log("ele not found");
else
if( !ele.hasOwnProperty("uiEleType") )
console.log("No type");
if( ele != null && ele.hasOwnProperty("uiEleType"))
{
console.log("found: "+ele.uiEleType)
switch( ele.uiEleType )
{
case "div":
break;
case "title":
ele.innerHTML = d.value
break;
case "button":
break;
case "check":
console.log(d)
dom_set_checkbox(ele.id,d.value)
break;
case "select":
ele.selectedIndex = d.value
break;
case "option":
break;
case "string":
ele.value = d.value
break;
case "number":
ele.value = d.value
break;
case "progress":
ele.value = d.value
break;
default:
ui_error("Unknown UI element type: " + d.type )
}
}
}
function ui_create( d )
{
@ -521,6 +603,12 @@ function ui_create( d )
ui_error("Unknown UI element type: " + d.type )
}
if( ele != null )
{
ele.uiEleType = d.type;
ui_send_echo(ele);
}
}
}
@ -546,7 +634,10 @@ function ws_on_msg( jsonMsg )
ui_create( d )
break;
case 'value':
ui_set_value( d )
break;
default:
ui_error("Unknown UI operation. " + d.op )
}