diff --git a/cwUi.cpp b/cwUi.cpp index a02cd3d..da08b03 100644 --- a/cwUi.cpp +++ b/cwUi.cpp @@ -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, "" }, }; @@ -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(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(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(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(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(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(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); diff --git a/cwUi.h b/cwUi.h index b295f7c..52f256f 100644 --- a/cwUi.h +++ b/cwUi.h @@ -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 handle_t; @@ -134,6 +141,8 @@ namespace cw websock::handle_t websockHandle( handle_t h ); ui::handle_t uiHandle( handle_t h ); + + } diff --git a/cwUiTest.cpp b/cwUiTest.cpp index d9f8360..9fd0f7b 100644 --- a/cwUiTest.cpp +++ b/cwUiTest.cpp @@ -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(); + + 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); diff --git a/html/uiTest/js/ui.js b/html/uiTest/js/ui.js index 3feb6c0..139d207 100644 --- a/html/uiTest/js/ui.js +++ b/html/uiTest/js/ui.js @@ -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 ) }