2020-03-23 14:48:49 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
# include "cwMem.h"
# include "cwThread.h"
# include "cwWebSock.h"
# include "cwWebSockSvr.h"
# include "cwUi.h"
# include "cwText.h"
# include "cwNumericConvert.h"
2020-03-25 15:35:37 +00:00
# include "cwObject.h"
2020-03-23 14:48:49 +00:00
namespace cw
{
namespace ui
{
2020-03-31 17:10:45 +00:00
typedef struct appIdMapRecd_str
{
struct appIdMapRecd_str * link ;
unsigned parentAppId ;
unsigned appId ;
2020-04-06 23:17:04 +00:00
char * eleName ;
2020-03-31 17:10:45 +00:00
} appIdMapRecd_t ;
2020-03-23 14:48:49 +00:00
typedef struct ele_str
{
struct ele_str * parent ; // pointer to parent ele - or nullptr if this ele is attached to the root ui ele
unsigned uuId ; // UI unique id - automatically generated and unique among all elements that are part of this ui_t object.
unsigned appId ; // application assigned id - application assigned id
2020-04-06 23:17:04 +00:00
char * eleName ; // javascript id
2020-03-23 14:48:49 +00:00
} ele_t ;
typedef struct ui_str
{
2020-03-31 17:10:45 +00:00
websockSrv : : handle_t wssH ; // websock server handle
unsigned eleAllocN ; // size of eleA[]
unsigned eleN ; // count of ele's in use
ele_t * * eleA ; // eleA[ eleAllocN ]
uiCallback_t cbFunc ; // app. cb func
void * cbArg ; // app. cb func arg.
appIdMapRecd_t * appIdMap ; // map of application parent/child/js id's
char * buf ; // buf[bufN] output message formatting buffer
unsigned bufN ; //
2020-03-23 14:48:49 +00:00
} ui_t ;
ui_t * _handleToPtr ( handle_t h )
{ return handleToPtr < handle_t , ui_t > ( h ) ; }
2020-03-31 17:10:45 +00:00
void _print_eles ( ui_t * p )
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
{
ele_t * e = p - > eleA [ i ] ;
2020-04-06 23:17:04 +00:00
printf ( " %15s u:%i : u:%i a:%i %s \n " , e - > parent = = nullptr ? " <null> " : e - > parent - > eleName , e - > parent = = nullptr ? - 1 : e - > parent - > uuId , e - > uuId , e - > appId , e - > eleName ) ;
2020-03-31 17:10:45 +00:00
}
}
2020-03-23 14:48:49 +00:00
rc_t _destroy ( ui_t * p )
{
rc_t rc = kOkRC ;
if ( p - > wssH . isValid ( ) )
if ( ( rc = websockSrv : : destroy ( p - > wssH ) ) ! = kOkRC )
return rc ;
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
{
2020-04-06 23:17:04 +00:00
mem : : release ( p - > eleA [ i ] - > eleName ) ;
2020-03-23 14:48:49 +00:00
mem : : release ( p - > eleA [ i ] ) ;
}
2020-03-31 17:10:45 +00:00
appIdMapRecd_t * m = p - > appIdMap ;
while ( m ! = nullptr )
{
appIdMapRecd_t * m0 = m - > link ;
2020-04-06 23:17:04 +00:00
mem : : release ( m - > eleName ) ;
2020-03-31 17:10:45 +00:00
mem : : release ( m ) ;
m = m0 ;
}
2020-03-23 14:48:49 +00:00
mem : : release ( p - > eleA ) ;
2020-04-06 23:17:04 +00:00
mem : : release ( p - > buf ) ;
2020-03-23 14:48:49 +00:00
mem : : release ( p ) ;
return rc ;
}
2020-04-06 23:17:04 +00:00
appIdMapRecd_t * _findAppIdMap ( ui_t * p , unsigned parentAppId , const char * eleName )
2020-03-31 17:10:45 +00:00
{
appIdMapRecd_t * m = p - > appIdMap ;
for ( ; m ! = nullptr ; m = m - > link )
2020-04-06 23:17:04 +00:00
if ( m - > parentAppId = = parentAppId & & textCompare ( eleName , m - > eleName ) = = 0 )
2020-03-31 17:10:45 +00:00
return m ;
return nullptr ;
}
appIdMapRecd_t * _findAppIdMap ( ui_t * p , unsigned parentAppId , unsigned appId )
{
appIdMapRecd_t * m = p - > appIdMap ;
for ( ; m ! = nullptr ; m = m - > link )
if ( m - > parentAppId = = parentAppId & & m - > appId = = appId )
return m ;
return nullptr ;
}
2020-04-06 23:17:04 +00:00
rc_t _allocAppIdMap ( ui_t * p , unsigned parentAppId , unsigned appId , const char * eleName )
2020-03-31 17:10:45 +00:00
{
rc_t rc = kOkRC ;
2020-04-06 23:17:04 +00:00
// The 'eleName' must be valid (or there is no reason to create the map.
// (since it will ultimately be used to locate the appId give the parentAppId and eleName)
if ( eleName = = nullptr | | strlen ( eleName ) = = 0 )
return cwLogError ( kInvalidIdRC , " Registered parent/child app id's must have a valid 'eleName'. " ) ;
2020-03-31 17:10:45 +00:00
// verify that the parent/child pair is unique
if ( _findAppIdMap ( p , parentAppId , appId ) ! = nullptr )
2020-04-06 23:17:04 +00:00
return cwLogError ( kDuplicateRC , " An attempt was made to register a duplicate parent/child appid pair. parentId:%i appId:%i eleName:'%s'. " , parentAppId , appId , cwStringNullGuard ( eleName ) ) ;
2020-03-31 17:10:45 +00:00
// verify that the parent/js pair is unique
2020-04-06 23:17:04 +00:00
if ( _findAppIdMap ( p , parentAppId , eleName ) ! = nullptr )
return cwLogError ( kDuplicateRC , " An attempt was made to register a duplicate parent app id/js id pair. parentId:%i appId:%i eleName:'%s'. " , parentAppId , appId , cwStringNullGuard ( eleName ) ) ;
2020-03-31 17:10:45 +00:00
// allocate and link in a new appId map record
appIdMapRecd_t * m = mem : : allocZ < appIdMapRecd_t > ( ) ;
m - > parentAppId = parentAppId ;
m - > appId = appId ;
2020-04-06 23:17:04 +00:00
m - > eleName = mem : : duplStr ( eleName ) ;
2020-03-31 17:10:45 +00:00
m - > link = p - > appIdMap ;
p - > appIdMap = m ;
return rc ;
}
2020-04-06 23:17:04 +00:00
ele_t * _createEle ( ui_t * p , ele_t * parent , unsigned appId , const char * eleName )
2020-03-23 14:48:49 +00:00
{
ele_t * e = mem : : allocZ < ele_t > ( ) ;
2020-03-31 17:10:45 +00:00
e - > parent = parent ;
2020-03-23 14:48:49 +00:00
e - > uuId = p - > eleN ;
2020-03-31 17:10:45 +00:00
e - > appId = appId ;
2020-04-06 23:17:04 +00:00
e - > eleName = mem : : duplStr ( eleName ) ;
2020-03-23 14:48:49 +00:00
if ( p - > eleN = = p - > eleAllocN )
{
p - > eleAllocN + = 100 ;
p - > eleA = mem : : resizeZ < ele_t * > ( p - > eleA , p - > eleAllocN ) ;
}
p - > eleA [ p - > eleN ] = e ;
p - > eleN + = 1 ;
2020-04-06 23:17:04 +00:00
// if the given appId was not valid ...
if ( appId = = kInvalidId & & parent ! = nullptr )
{
appIdMapRecd_t * m ;
// ... then try to look it up from the appIdMap.
if ( ( m = _findAppIdMap ( p , parent - > appId , eleName ) ) ! = nullptr )
e - > appId = m - > appId ;
}
2020-03-23 14:48:49 +00:00
return e ;
}
2020-03-31 17:10:45 +00:00
// Given a uuId return a pointer to the associated element.
2020-03-23 14:48:49 +00:00
ele_t * _uuIdToEle ( ui_t * p , unsigned uuId , bool errorFl = true )
{
if ( uuId > = p - > eleN )
{
cwLogError ( kInvalidIdRC , " The element uuid:%i is not valid. " , uuId ) ;
return nullptr ;
}
return p - > eleA [ uuId ] ;
}
2020-04-06 23:17:04 +00:00
ele_t * _eleNameToEle ( ui_t * p , const char * eleName , bool errorFl = true )
2020-03-31 17:10:45 +00:00
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2020-04-06 23:17:04 +00:00
if ( textCompare ( p - > eleA [ i ] - > eleName , eleName ) = = 0 )
2020-03-31 17:10:45 +00:00
return p - > eleA [ i ] ;
2020-04-06 23:17:04 +00:00
if ( errorFl )
cwLogError ( kInvalidIdRC , " The element with eleName:%s not found. " , cwStringNullGuard ( eleName ) ) ;
return nullptr ;
}
// Given a parent UuId and a eleName find the associated ele
ele_t * _parentUuId_EleName_ToEle ( ui_t * p , unsigned parentUuId , const char * eleName , bool errorFl = true )
{
// if we are looking for the root
if ( ( parentUuId = = kRootUuId | | parentUuId = = kInvalidId ) & & textCompare ( eleName , " uiDivId " ) = = 0 )
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] - > parent = = nullptr & & p - > eleA [ i ] - > uuId = = kRootUuId )
return p - > eleA [ i ] ;
}
else // we are looking for an elment which is not the root
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( ( ( p - > eleA [ i ] - > parent = = nullptr & & parentUuId = = kRootUuId ) | | ( p - > eleA [ i ] - > parent ! = nullptr & & parentUuId = = p - > eleA [ i ] - > parent - > uuId ) ) & & ( textCompare ( p - > eleA [ i ] - > eleName , eleName ) = = 0 ) )
return p - > eleA [ i ] ;
}
2020-03-31 17:10:45 +00:00
if ( errorFl )
2020-04-06 23:17:04 +00:00
cwLogError ( kInvalidIdRC , " The element with parent uuid:%i and eleName:%s is not found. " , parentUuId , eleName ) ;
2020-03-31 17:10:45 +00:00
return nullptr ;
}
2020-04-06 23:17:04 +00:00
unsigned _findElementUuId ( ui_t * p , const char * eleName )
2020-03-23 14:48:49 +00:00
{
2020-03-31 17:10:45 +00:00
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2020-04-06 23:17:04 +00:00
if ( strcmp ( p - > eleA [ i ] - > eleName , eleName ) = = 0 )
2020-03-31 17:10:45 +00:00
return p - > eleA [ i ] - > uuId ;
return kInvalidId ;
2020-03-23 14:48:49 +00:00
}
2020-04-06 23:17:04 +00:00
const char * _findEleEleName ( ui_t * p , unsigned uuId )
2020-03-23 14:48:49 +00:00
{
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] - > uuId = = uuId )
2020-04-06 23:17:04 +00:00
return p - > eleA [ i ] - > eleName ;
2020-03-23 14:48:49 +00:00
return nullptr ;
}
2020-03-31 17:10:45 +00:00
rc_t _websockSend ( ui_t * p , unsigned wsSessId , const char * msg )
{
return websock : : send ( websockSrv : : websockHandle ( p - > wssH ) , kUiProtocolId , wsSessId , msg , strlen ( msg ) ) ;
}
2020-03-23 14:48:49 +00:00
// Print the attribute data value.
template < typename T0 >
unsigned format_attribute_data ( char * buf , unsigned n , T0 t0 )
{
return toText ( buf , n , t0 ) ;
}
// Override format_attribute_data() for char. string data so that strings are wrapped in quotes.
template < >
unsigned format_attribute_data ( char * buf , unsigned n , const char * t )
{
unsigned i = 0 ;
i + = toText ( buf + i , n - i , " \" " ) ;
i + = toText ( buf + i , n - i , t ) ;
i + = toText ( buf + i , n - i , " \" " ) ;
return i ;
}
// terminating condition for format_attributes()
unsigned format_attributes ( char * buf , unsigned n , unsigned i )
{ return i ; }
template < typename T0 , typename T1 , typename . . . ARGS >
unsigned format_attributes ( char * buf , unsigned n , unsigned i , T0 t0 , T1 t1 , ARGS & & . . . args )
{
i + = toText ( buf + i , n - i , " , \" " ) ;
i + = toText ( buf + i , n - i , t0 ) ;
i + = toText ( buf + i , n - i , " \" : " ) ;
i + = format_attribute_data ( buf + i , n - i , t1 ) ;
return format_attributes ( buf , n , i , std : : forward < ARGS > ( args ) . . . ) ;
}
template < typename . . . ARGS >
2020-04-06 23:17:04 +00:00
rc_t _createOneEle ( ui_t * p , unsigned & uuIdRef , const char * eleTypeStr , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title , ARGS & & . . . args )
2020-03-23 14:48:49 +00:00
{
2020-04-06 23:17:04 +00:00
// { op:create, parent:my_parent_id, value:{ button:{ eleName:my_eleName, appId:appId, uuId:uuId, class:clas, title:'my title' } }
2020-03-23 14:48:49 +00:00
rc_t rc = kOkRC ;
ele_t * newEle = nullptr ;
ele_t * parentEle = nullptr ;
2020-03-31 17:10:45 +00:00
//const unsigned bufN = 1024; // TODO: use preallocated buffer
//char buf[ bufN ];
2020-03-23 14:48:49 +00:00
uuIdRef = kInvalidId ;
2020-03-31 17:10:45 +00:00
if ( parentUuId = = kInvalidId )
parentUuId = kRootUuId ;
// get the parent element
if ( ( parentEle = _uuIdToEle ( p , parentUuId ) ) = = nullptr )
return cwLogError ( kInvalidArgRC , " Unable to locate the parent element (id:%i) . " , parentUuId ) ;
// create the local representation of the new element
2020-04-06 23:17:04 +00:00
newEle = _createEle ( p , parentEle , appId , eleName ) ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
// form the create json message string
2020-04-06 23:17:04 +00:00
//unsigned i = snprintf( p->buf, p->bufN, "{ \"op\":\"create\", \"parent\":\"%s\", \"children\":{ \"%s\":{ \"eleName\":\"%s\", \"appId\":%i, \"uuId\":%i, \"class\":\"%s\", \"title\":\"%s\" ", parentEleName, eleTypeStr, eleName, appId, newEle->uuId, clas, title );
unsigned i = snprintf ( p - > buf , p - > bufN , " { \" op \" : \" create \" , \" parentUuId \" : \" %i \" , \" type \" : \" %s \" , \" eleName \" : \" %s \" , \" appId \" : \" %i \" , \" uuId \" :%i, \" class \" : \" %s \" , \" title \" : \" %s \" " , parentEle - > uuId , eleTypeStr , eleName , appId , newEle - > uuId , clas , title ) ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
// add the UI specific attributes
i + = format_attributes ( p - > buf + i , p - > bufN - i , 0 , std : : forward < ARGS > ( args ) . . . ) ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
// terminate the message
2020-04-06 23:17:04 +00:00
i + = toText ( p - > buf + i , p - > bufN - i , " } " ) ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
if ( i > = p - > bufN )
return cwLogError ( kBufTooSmallRC , " The UI message formatting buffer is too small. (size:%i bytes) " , p->bufN) ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
printf ( " %s \n " , p - > buf ) ;
// send the message
rc = _websockSend ( p , wsSessId , p - > buf ) ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
uuIdRef = newEle - > uuId ;
2020-03-23 14:48:49 +00:00
return rc ;
}
2020-04-06 23:17:04 +00:00
ele_t * _findOrCreateEle ( ui_t * p , ele_t * parentEle , const char * eleName )
{
ele_t * ele ;
if ( ( ele = _parentUuId_EleName_ToEle ( p , parentEle - > uuId , eleName , false ) ) = = nullptr )
ele = _createEle ( p , parentEle , kInvalidId , eleName ) ;
return ele ;
}
rc_t _createElementsFromChildList ( ui_t * p , object_t * po , unsigned wsSessId , ele_t * parentEle ) ;
//
rc_t _createEleFromRsrsc ( ui_t * p , ele_t * parentEle , const char * eleType , object_t * o , unsigned wsSessId )
2020-03-25 15:35:37 +00:00
{
2020-04-06 23:17:04 +00:00
rc_t rc = kOkRC ;
object_t * co = nullptr ;
ele_t * ele = nullptr ;
char * eleName = nullptr ;
2020-03-25 15:35:37 +00:00
2020-04-06 23:17:04 +00:00
if ( ! o - > is_dict ( ) )
return cwLogError ( kSyntaxErrorRC , " All ui element resource records must be dictionaries. " ) ;
// if this object has a 'children' list then unlink it an save it for later
if ( ( co = o - > find ( " children " , kNoRecurseFl | kOptionalFl ) ) ! = nullptr )
2020-03-25 15:35:37 +00:00
{
2020-04-06 23:17:04 +00:00
co = co - > parent ;
co - > unlink ( ) ;
2020-03-31 17:10:45 +00:00
}
2020-04-06 23:17:04 +00:00
// get the ui ele name
if ( ( rc = o - > get ( " name " , eleName ) ) ! = kOkRC )
2020-03-31 17:10:45 +00:00
{
2020-04-06 23:17:04 +00:00
rc = cwLogError ( rc , " The UI element name could not be read. " ) ;
goto errLabel ;
2020-03-31 17:10:45 +00:00
}
2020-04-06 23:17:04 +00:00
// get or create the ele record to associate with this ele
if ( ( ele = _findOrCreateEle ( p , parentEle , eleName ) ) = = nullptr )
{
rc = cwLogError ( kOpFailRC , " The local element '%s' could not be created. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
}
2020-03-25 15:35:37 +00:00
2020-04-06 23:17:04 +00:00
if ( o - > insertPair ( " uuId " , ele - > uuId ) = = nullptr )
2020-03-31 17:10:45 +00:00
{
2020-04-06 23:17:04 +00:00
rc = cwLogError ( kOpFailRC , " The 'uuid' node insertion failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
2020-03-31 17:10:45 +00:00
}
2020-04-06 23:17:04 +00:00
if ( ele - > appId ! = kInvalidId )
2020-03-31 17:10:45 +00:00
{
2020-04-06 23:17:04 +00:00
if ( o - > insertPair ( " appId " , ele - > appId ) = = nullptr )
2020-03-31 17:10:45 +00:00
{
2020-04-06 23:17:04 +00:00
rc = cwLogError ( kOpFailRC , " The 'appId' node insertion failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
2020-03-31 17:10:45 +00:00
}
2020-04-06 23:17:04 +00:00
}
if ( o - > insertPair ( " parentUuId " , parentEle - > uuId ) = = nullptr )
{
rc = cwLogError ( kOpFailRC , " The 'parentUuId' node insertion failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
}
if ( o - > insertPair ( " op " , " create " ) = = nullptr )
{
rc = cwLogError ( kOpFailRC , " The 'op' node insertion failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
2020-03-25 15:35:37 +00:00
}
2020-04-06 23:17:04 +00:00
if ( o - > insertPair ( " type " , eleType ) = = nullptr )
{
rc = cwLogError ( kOpFailRC , " The 'eleType' node insertion failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
}
2020-03-25 15:35:37 +00:00
2020-04-06 23:17:04 +00:00
if ( o - > to_string ( p - > buf , p - > bufN ) > = p - > bufN )
{
rc = cwLogError ( kOpFailRC , " Conversion to JSON string failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
}
printf ( " %s \n " , p - > buf ) ;
if ( ( rc = _websockSend ( p , wsSessId , p - > buf ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " The creation request send failed on UI element '%s'. " , cwStringNullGuard ( eleName ) ) ;
goto errLabel ;
}
// if this element has a list of children then create them here
if ( co ! = nullptr )
rc = _createElementsFromChildList ( p , co - > pair_value ( ) , wsSessId , ele ) ;
errLabel :
if ( co ! = nullptr )
co - > free ( ) ;
return rc ;
}
// 'od' is an object dictionary where each pair in the dictionary has
// the form: 'eleType':{ <object> }
rc_t _createElementsFromChildList ( ui_t * p , object_t * po , unsigned wsSessId , ele_t * parentEle )
{
rc_t rc = kOkRC ;
if ( ! po - > is_dict ( ) )
return cwLogError ( kSyntaxErrorRC , " All UI resource elements must be containers. " ) ;
unsigned childN = po - > child_count ( ) ;
for ( unsigned i = 0 ; i < childN ; + + i )
{
object_t * o = po - > child_ele ( i ) ;
2020-03-31 17:10:45 +00:00
2020-04-06 23:17:04 +00:00
if ( ! o - > is_pair ( ) )
return cwLogError ( kSyntaxErrorRC , " All object dictionary children must be pairs. " ) ;
if ( ( rc = _createEleFromRsrsc ( p , parentEle , o - > pair_label ( ) , o - > pair_value ( ) , wsSessId ) ) ! = kOkRC )
return rc ;
2020-03-31 17:10:45 +00:00
2020-04-06 23:17:04 +00:00
}
return rc ;
}
2020-03-31 17:10:45 +00:00
2020-04-06 23:17:04 +00:00
rc_t _createFromObj ( ui_t * p , object_t * o , unsigned wsSessId , unsigned parentUuId )
{
rc_t rc = kOkRC ;
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 )
return cwLogError ( kSyntaxErrorRC , " UI resources must have a root 'parent' value. " ) ;
2020-03-25 15:35:37 +00:00
2020-04-06 23:17:04 +00:00
// get the parent element name
if ( ( rc = po - > value ( eleName ) ) ! = kOkRC )
return cwLogError ( kOpFailRC , " The root 'parent' value could not be accessed. " ) ;
// find the parent element
if ( ( parentEle = _parentUuId_EleName_ToEle ( p , parentUuId , eleName ) ) = = nullptr )
return cwLogError ( kSyntaxErrorRC , " A parent UI element named '%s' could not be found. " , cwStringNullGuard ( eleName ) ) ;
2020-03-31 17:10:45 +00:00
2020-04-06 23:17:04 +00:00
// unlink the 'parent' pair
po = po - > parent ;
po - > unlink ( ) ;
rc = _createElementsFromChildList ( p , o , wsSessId , parentEle ) ;
po - > free ( ) ;
2020-03-25 15:35:37 +00:00
return rc ;
}
2020-04-06 23:17:04 +00:00
2020-03-23 14:48:49 +00:00
ele_t * _parse_value_msg ( ui_t * p , value_t & valueRef , const char * msg )
{
rc_t rc = kOkRC ;
char argType = 0 ;
ele_t * ele = nullptr ;
unsigned eleUuId = kInvalidId ;
valueRef . tid = kInvalidTId ;
if ( msg = = nullptr )
{
cwLogWarning ( " Empty message received from UI. " ) ;
return nullptr ;
}
// locate the colon prior to the value
const char * s = strchr ( msg , ' : ' ) ;
if ( s = = nullptr | | sscanf ( msg , " value %i %c " , & eleUuId , & argType ) ! = 2 )
{
2020-03-31 17:10:45 +00:00
cwLogError ( kSyntaxErrorRC , " Invalid message from UI: '%s'. " , msg ) ;
2020-03-23 14:48:49 +00:00
goto errLabel ;
}
// advance s past the colon
s + = 1 ;
// parse the argument
switch ( argType )
{
case ' b ' :
if ( ( rc = string_to_number < bool > ( s , valueRef . u . b ) ) = = kOkRC )
valueRef . tid = kBoolTId ;
break ;
case ' i ' :
if ( ( rc = string_to_number < int > ( s , valueRef . u . i ) ) = = kOkRC )
valueRef . tid = kIntTId ;
break ;
case ' u ' :
if ( ( rc = string_to_number < unsigned > ( s , valueRef . u . u ) ) = = kOkRC )
valueRef . tid = kUIntTId ;
break ;
case ' f ' :
if ( ( rc = string_to_number < float > ( s , valueRef . u . f ) ) = = kOkRC )
valueRef . tid = kFloatTId ;
break ;
case ' d ' :
if ( ( rc = string_to_number < double > ( s , valueRef . u . d ) ) = = kOkRC )
valueRef . tid = kDoubleTId ;
break ;
case ' s ' :
if ( ( valueRef . u . s = nextNonWhiteChar ( s ) ) = = nullptr )
valueRef . tid = kStringTId ;
break ;
default :
rc = cwLogError ( kInvalidIdRC , " Unknown value type '%c' in message from UI. " , argType ) ;
goto errLabel ;
}
// locate the element record
if ( ( ele = _uuIdToEle ( p , eleUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI message elment not found. " ) ;
goto errLabel ;
}
errLabel :
return ele ;
}
2020-03-31 17:10:45 +00:00
rc_t _send_app_id_msg ( ui_t * p , unsigned wsSessId , ele_t * ele )
{
rc_t rc = kOkRC ;
2020-04-06 23:17:04 +00:00
unsigned i = snprintf ( p - > buf , p - > bufN , " { \" op \" : \" set_app_id \" , \" parentUuId \" :%i, \" eleName \" : \" %s \" , \" appId \" :%i, \" uuId \" :%i } " , ele - > parent - > uuId , ele - > eleName , ele - > parent - > appId , ele - > appId ) ;
2020-03-31 17:10:45 +00:00
if ( i > = p - > bufN )
return cwLogError ( kBufTooSmallRC , " The 'app_id' msg formatting buffer is too small (%i bytes) . " , p->bufN) ;
if ( ( rc = _websockSend ( p , wsSessId , p - > buf ) ) ! = kOkRC )
return cwLogError ( rc , " 'app_id' msg transmission failed. " ) ;
return rc ;
}
ele_t * _handle_register_msg ( ui_t * p , unsigned wsSessId , const char * msg )
{
printf ( " %s \n " , msg ) ;
return nullptr ;
}
ele_t * _handle_register_msg0 ( ui_t * p , unsigned wsSessId , const char * msg )
{
rc_t rc = kOkRC ;
unsigned parentUuId = kInvalidId ;
ele_t * parentEle = nullptr ;
ele_t * ele = nullptr ;
const char * s0 = nextNonWhiteChar ( msg + strlen ( " register " ) ) ;
2020-04-06 23:17:04 +00:00
const char * eleName = nextNonWhiteChar ( nextWhiteChar ( s0 ) ) ;
2020-03-31 17:10:45 +00:00
// verifity the message tokens
2020-04-06 23:17:04 +00:00
if ( s0 = = nullptr | | eleName = = nullptr )
2020-03-31 17:10:45 +00:00
{
cwLogError ( kSyntaxErrorRC , " 'register' msg format error: '%s' is not a valid message. " , cwStringNullGuard ( msg ) ) ;
goto errLabel ;
}
// verify the parentUuId parsing
if ( ( rc = string_to_number < unsigned > ( s0 , parentUuId ) ) ! = kOkRC )
{
cwLogError ( kSyntaxErrorRC , " 'register' msg parentUuId format error: '%s' does not contain a valid parentUuId. " , cwStringNullGuard ( msg ) ) ;
goto errLabel ;
}
// get the parent ele
if ( ( parentEle = _uuIdToEle ( p , parentUuId ) ) = = nullptr )
{
cwLogError ( kInvalidIdRC , " UI register msg parent element not found. " ) ;
goto errLabel ;
}
// if the child element does not already exist
2020-04-06 23:17:04 +00:00
if ( ( ele = _parentUuId_EleName_ToEle ( p , parentUuId , eleName , false ) ) = = nullptr )
2020-03-31 17:10:45 +00:00
{
2020-04-06 23:17:04 +00:00
// look up the parent/eleName pair map
appIdMapRecd_t * m = _findAppIdMap ( p , parentEle - > appId , eleName ) ;
2020-03-31 17:10:45 +00:00
// create the ele
2020-04-06 23:17:04 +00:00
ele = _createEle ( p , parentEle , m = = nullptr ? kInvalidId : m - > appId , eleName ) ;
2020-03-31 17:10:45 +00:00
2020-04-06 23:17:04 +00:00
printf ( " creating: parent uuid:%i js:%s \n " , parentUuId , eleName ) ;
2020-03-31 17:10:45 +00:00
// notify the app of the new ele's uuid and appId
if ( m ! = nullptr )
_send_app_id_msg ( p , wsSessId , ele ) ;
}
else
{
2020-04-06 23:17:04 +00:00
printf ( " parent uuid:%i js:%s already exists. \n " , parentUuId , eleName ) ;
2020-03-31 17:10:45 +00:00
}
if ( ele ! = nullptr )
_send_app_id_msg ( p , wsSessId , ele ) ;
return ele ;
errLabel :
return nullptr ;
}
opId_t _labelToOpId ( const char * label )
{
typedef struct
{
opId_t id ;
const char * label ;
} map_t ;
map_t mapA [ ] =
{
{ kConnectOpId , " connect " } ,
{ kInitOpId , " init " } ,
{ kValueOpId , " value " } ,
{ kRegisterOpId , " register " } ,
{ kDisconnectOpId , " disconnect " } ,
{ kEndAppIdUpdateOpId , " end_app_id_update " } ,
{ kInvalidOpId , " <invalid> " } ,
} ;
for ( unsigned i = 0 ; mapA [ i ] . id ! = kInvalidOpId ; + + i )
if ( textCompare ( label , mapA [ i ] . label , strlen ( mapA [ i ] . label ) ) = = 0 )
return mapA [ i ] . id ;
return kInvalidOpId ;
}
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
void _websockCb ( void * cbArg , unsigned protocolId , unsigned wsSessId , websock : : msgTypeId_t msg_type , const void * msg , unsigned byteN )
2020-03-23 14:48:49 +00:00
{
ui_t * p = ( ui_t * ) cbArg ;
opId_t opId = kInvalidOpId ;
value_t value ;
switch ( msg_type )
{
case websock : : kConnectTId :
opId = kConnectOpId ;
break ;
case websock : : kDisconnectTId :
opId = kDisconnectOpId ;
break ;
case websock : : kMessageTId :
{
ele_t * ele ;
2020-03-31 17:10:45 +00:00
opId = _labelToOpId ( ( const char * ) msg ) ;
switch ( opId )
2020-03-23 14:48:49 +00:00
{
2020-03-31 17:10:45 +00:00
case kInitOpId :
// Pass on the 'init' msg to the app.
p - > cbFunc ( p - > cbArg , wsSessId , opId , kInvalidId , kInvalidId , kInvalidId , nullptr ) ;
break ;
case kValueOpId :
if ( ( ele = _parse_value_msg ( p , value , ( const char * ) msg ) ) = = nullptr )
cwLogError ( kOpFailRC , " UI Value message parse failed. " ) ;
else
{
unsigned parentEleAppId = ele - > parent = = nullptr ? kInvalidId : ele - > parent - > appId ;
p - > cbFunc ( p - > cbArg , wsSessId , opId , parentEleAppId , ele - > uuId , ele - > appId , & value ) ;
}
break ;
case kRegisterOpId :
_handle_register_msg ( p , wsSessId , ( const char * ) msg ) ;
break ;
case kEndAppIdUpdateOpId :
_print_eles ( p ) ;
cwLogInfo ( " App Id Update Complete. " ) ;
break ;
case kInvalidOpId :
cwLogError ( kInvalidIdRC , " The UI received a NULL op. id. " ) ;
break ;
default :
cwLogError ( kInvalidIdRC , " The UI received an unknown op. id. " ) ;
break ;
} // switch opId
} // kMessageTId
2020-03-23 14:48:49 +00:00
break ;
default :
cwLogError ( kInvalidOpRC , " Unknown websock message type:%i. " , msg_type ) ;
return ;
}
2020-03-31 17:10:45 +00:00
2020-03-23 14:48:49 +00:00
}
}
}
cw : : rc_t cw : : ui : : createUi (
handle_t & h ,
unsigned port ,
uiCallback_t cbFunc ,
void * cbArg ,
const char * physRootDir ,
const char * dfltPageFn ,
unsigned websockTimeOutMs ,
unsigned rcvBufByteN ,
2020-03-31 17:10:45 +00:00
unsigned xmtBufByteN ,
unsigned fmtBufByteN )
2020-03-23 14:48:49 +00:00
{
rc_t rc = kOkRC ;
2020-03-31 17:10:45 +00:00
ele_t * ele ;
2020-03-23 14:48:49 +00:00
websock : : protocol_t protocolA [ ] =
{
{ " http " , kHttpProtocolId , 0 , 0 } ,
{ " ui_protocol " , kUiProtocolId , rcvBufByteN , xmtBufByteN }
} ;
unsigned protocolN = sizeof ( protocolA ) / sizeof ( protocolA [ 0 ] ) ;
if ( ( rc = destroyUi ( h ) ) ! = kOkRC )
return rc ;
2020-03-31 17:10:45 +00:00
if ( cbFunc = = nullptr )
return cwLogError ( kInvalidArgRC , " The UI callback function must be a valid pointer. " ) ;
2020-03-23 14:48:49 +00:00
ui_t * p = mem : : allocZ < ui_t > ( ) ;
if ( ( rc = websockSrv : : create ( p - > wssH , _websockCb , p , physRootDir , dfltPageFn , port , protocolA , protocolN , websockTimeOutMs ) ) ! = kOkRC )
{
cwLogError ( rc , " Internal websock server creation failed. " ) ;
goto errLabel ;
}
p - > eleAllocN = 100 ;
p - > eleA = mem : : allocZ < ele_t * > ( p - > eleAllocN ) ;
p - > eleN = 0 ;
p - > cbFunc = cbFunc ;
p - > cbArg = cbArg ;
2020-03-31 17:10:45 +00:00
p - > buf = mem : : allocZ < char > ( fmtBufByteN ) ;
p - > bufN = fmtBufByteN ;
// create the root element
2020-04-06 23:17:04 +00:00
if ( ( ele = _createEle ( p , nullptr , kRootAppId , " uiDivId " ) ) = = nullptr | | ele - > uuId ! = kRootUuId )
2020-03-31 17:10:45 +00:00
{
cwLogError ( kOpFailRC , " The UI root element creation failed. " ) ;
goto errLabel ;
}
2020-03-23 14:48:49 +00:00
h . set ( p ) ;
errLabel :
if ( rc ! = kOkRC )
{
_destroy ( p ) ;
}
return rc ;
}
cw : : rc_t cw : : ui : : start ( handle_t h )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
if ( ( rc = websockSrv : : start ( p - > wssH ) ) ! = kOkRC )
rc = cwLogError ( rc , " Internal websock server start failed. " ) ;
return rc ;
}
cw : : rc_t cw : : ui : : stop ( handle_t h )
{
rc_t rc = kOkRC ;
ui_t * p = _handleToPtr ( h ) ;
if ( ( rc = websockSrv : : pause ( p - > wssH ) ) ! = kOkRC )
rc = cwLogError ( rc , " Internal websock server stop failed. " ) ;
return rc ;
}
cw : : rc_t cw : : ui : : destroyUi ( handle_t & h )
{
rc_t rc = kOkRC ;
if ( ! h . isValid ( ) )
return rc ;
ui_t * p = _handleToPtr ( h ) ;
if ( ( rc = _destroy ( p ) ) ! = kOkRC )
return rc ;
h . clear ( ) ;
return rc ;
}
2020-04-06 23:17:04 +00:00
unsigned cw : : ui : : findElementAppId ( handle_t h , unsigned parentUuId , const char * eleName )
2020-03-23 14:48:49 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
2020-04-06 23:17:04 +00:00
if ( p - > eleA [ i ] - > parent - > uuId = = parentUuId & & strcmp ( p - > eleA [ i ] - > eleName , eleName ) = = 0 )
2020-03-23 14:48:49 +00:00
return p - > eleA [ i ] - > appId ;
return kInvalidId ;
}
2020-04-06 23:17:04 +00:00
unsigned cw : : ui : : findElementUuId ( handle_t h , unsigned parentUuId , const char * eleName )
2020-03-23 14:48:49 +00:00
{
2020-03-31 17:10:45 +00:00
ui_t * p = _handleToPtr ( h ) ;
ele_t * ele ;
2020-04-06 23:17:04 +00:00
if ( ( ele = _parentUuId_EleName_ToEle ( p , parentUuId , eleName ) ) ! = nullptr )
2020-03-31 17:10:45 +00:00
return ele - > uuId ;
2020-03-23 14:48:49 +00:00
return kInvalidId ;
}
unsigned cw : : ui : : findElementUuId ( handle_t h , unsigned parentUuId , unsigned appId )
{
ui_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > eleN ; + + i )
if ( p - > eleA [ i ] - > parent - > uuId = = parentUuId & & p - > eleA [ i ] - > appId = = appId )
return p - > eleA [ i ] - > uuId ;
return kInvalidId ;
}
2020-04-06 23:17:04 +00:00
const char * cw : : ui : : findElementName ( handle_t h , unsigned uuId )
2020-03-23 14:48:49 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2020-04-06 23:17:04 +00:00
return _findEleEleName ( p , uuId ) ;
2020-03-23 14:48:49 +00:00
}
2020-04-06 23:17:04 +00:00
unsigned cw : : ui : : findElementUuId ( handle_t h , const char * eleName )
2020-03-31 17:10:45 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
2020-04-06 23:17:04 +00:00
return _findElementUuId ( p , eleName ) ;
2020-03-31 17:10:45 +00:00
}
2020-03-25 15:35:37 +00:00
2020-03-31 17:10:45 +00:00
cw : : rc_t cw : : ui : : createFromFile ( handle_t h , const char * fn , unsigned wsSessId , unsigned parentUuId )
2020-03-25 15:35:37 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
object_t * o = nullptr ;
if ( ( rc = objectFromFile ( fn , o ) ) ! = kOkRC )
goto errLabel ;
2020-03-31 17:10:45 +00:00
//o->print();
if ( ( rc = _createFromObj ( p , o , wsSessId , parentUuId ) ) ! = kOkRC )
2020-03-25 15:35:37 +00:00
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
2020-03-31 17:10:45 +00:00
rc = cwLogError ( rc , " UI instantiation from the configuration file '%s' failed. " , cwStringNullGuard ( fn ) ) ;
2020-03-25 15:35:37 +00:00
if ( o ! = nullptr )
o - > free ( ) ;
return rc ;
}
2020-03-31 17:10:45 +00:00
cw : : rc_t cw : : ui : : createFromText ( handle_t h , const char * text , unsigned wsSessId , unsigned parentUuId )
2020-03-25 15:35:37 +00:00
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
object_t * o = nullptr ;
if ( ( rc = objectFromString ( text , o ) ) ! = kOkRC )
goto errLabel ;
2020-03-23 14:48:49 +00:00
2020-03-31 17:10:45 +00:00
if ( ( rc = _createFromObj ( p , o , wsSessId , parentUuId ) ) ! = kOkRC )
2020-03-25 15:35:37 +00:00
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
2020-03-31 17:10:45 +00:00
rc = cwLogError ( rc , " UI instantiation failed from the configuration from string: '%s'. " , cwStringNullGuard ( text ) ) ;
2020-03-25 15:35:37 +00:00
if ( o ! = nullptr )
o - > free ( ) ;
return rc ;
}
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createDiv ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " div " , wsSessId , parentUuId , eleName , appId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createTitle ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " option " , wsSessId , parentUuId , eleName , appId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createButton ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " button " , wsSessId , parentUuId , eleName , appId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createCheck ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title , bool value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " check " , wsSessId , parentUuId , eleName , appId , clas , title , " value " , value ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createSelect ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " select " , wsSessId , parentUuId , eleName , appId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createOption ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " option " , wsSessId , parentUuId , eleName , appId , clas , title ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createString ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title , const char * value )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " string " , wsSessId , parentUuId , eleName , appId , clas , title , " value " , value ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createNumber ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title , double value , double minValue , double maxValue , double stepValue , unsigned decpl )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " number " , wsSessId , parentUuId , eleName , appId , clas , title , " value " , value , " min " , minValue , " max " , maxValue , " step " , stepValue , " decpl " , decpl ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createProgress ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title , double value , double minValue , double maxValue )
{ return _createOneEle ( _handleToPtr ( h ) , uuIdRef , " progress " , wsSessId , parentUuId , eleName , appId , clas , title , " value " , value , " min " , minValue , " max " , maxValue ) ; }
2020-03-23 14:48:49 +00:00
2020-04-06 23:17:04 +00:00
cw : : rc_t cw : : ui : : createText ( handle_t h , unsigned & uuIdRef , unsigned wsSessId , unsigned parentUuId , const char * eleName , unsigned appId , const char * clas , const char * title )
2020-03-23 14:48:49 +00:00
{
rc_t rc = kOkRC ;
return rc ;
}
2020-03-31 17:10:45 +00:00
cw : : rc_t cw : : ui : : registerAppIds ( handle_t h , const appIdMap_t * map , unsigned mapN )
{
ui_t * p = _handleToPtr ( h ) ;
rc_t rc = kOkRC ;
for ( unsigned i = 0 ; i < mapN ; + + i )
2020-04-06 23:17:04 +00:00
if ( ( rc = _allocAppIdMap ( p , map [ i ] . parentAppId , map [ i ] . appId , map [ i ] . eleName ) ) ! = kOkRC )
2020-03-31 17:10:45 +00:00
return rc ;
return rc ;
}
2020-03-23 14:48:49 +00:00