idnode: several improvements to the auto-updating features.

This can now be disabled via tick-box at bottom of the grid and no data
is actually sent in the update, just which nodes have been updated.

There is still an inefficiency in that a bunch of nodes being updated could
result in loads of reloads, but that could be improved with a bit of client
side buffering/delay.
This commit is contained in:
Adam Sutton 2013-07-10 12:10:53 +01:00
parent c587fd3fdc
commit bfd8cffdc2
6 changed files with 164 additions and 183 deletions

View file

@ -290,6 +290,7 @@ idnode_get_u32
ptr = ((void*)self) + p->off;
switch (p->type) {
case PT_INT:
case PT_BOOL:
*u32 = *(int*)ptr;
return 0;
case PT_U16:
@ -401,6 +402,7 @@ idnode_cmp_sort
case PT_INT:
case PT_U16:
case PT_U32:
case PT_BOOL:
{
uint32_t u32a = 0, u32b = 0;
idnode_get_u32(ina, sort->key, &u32a);
@ -412,7 +414,6 @@ idnode_cmp_sort
}
break;
case PT_DBL:
case PT_BOOL:
// TODO
break;
}
@ -577,18 +578,15 @@ int
idnode_write0 ( idnode_t *self, htsmsg_t *c, int optmask, int dosave )
{
int save = 0;
void (*savefn)(idnode_t*) = NULL;
const idclass_t *idc = self->in_class;
for (; idc; idc = idc->ic_super)
for (; idc; idc = idc->ic_super) {
save |= prop_write_values(self, idc->ic_properties, c, optmask, NULL);
if (save) {
if (dosave) {
for(; idc != NULL; idc = idc->ic_super) {
if(idc->ic_save != NULL) {
idc->ic_save(self);
break;
}
}
}
if (!savefn && idc->ic_save)
savefn = idc->ic_save;
}
if (save && dosave) {
if (savefn) savefn(self);
idnode_notify(self, NULL, 0);
}
return save;
@ -721,17 +719,20 @@ void
idnode_notify
(idnode_t *in, const char *chn, int force)
{
const char *uuid = idnode_uuid_as_str(in);
/* Forced */
if (chn || force) {
htsmsg_t *m = idnode_serialize0(in, 0);
notify_by_msg(chn ?: "idnodeParamsChanged", m);
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", uuid);
notify_by_msg(chn ?: "idnodeUpdated", m);
/* Rate-limited */
} else {
pthread_mutex_lock(&idnode_mutex);
if (!idnode_queue)
idnode_queue = htsmsg_create_map();
htsmsg_set_u32(idnode_queue, idnode_uuid_as_str(in), 1);
htsmsg_set_u32(idnode_queue, uuid, 1);
pthread_cond_signal(&idnode_cond);
pthread_mutex_unlock(&idnode_mutex);
}
@ -743,6 +744,15 @@ idnode_notify_simple (void *in)
idnode_notify(in, NULL, 0);
}
void
idnode_notify_title_changed (void *in)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(in));
htsmsg_add_str(m, "text", idnode_get_title(in));
notify_by_msg("idnodeUpdated", m);
}
/*
* Thread for handling notifications
*/
@ -771,20 +781,20 @@ idnode_thread ( void *p )
HTSMSG_FOREACH(f, q) {
node = idnode_find(f->hmf_name, NULL);
if (node) {
m = idnode_serialize0(node, 0);
if (m)
notify_by_msg("idnodeUpdated", m);
} else {
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
if (node)
notify_by_msg("idnodeUpdated", m);
else
notify_by_msg("idnodeDeleted", m);
}
}
/* Finished */
pthread_mutex_unlock(&global_lock);
htsmsg_destroy(q);
/* Wait */
usleep(500000);
pthread_mutex_lock(&idnode_mutex);
}

View file

@ -120,6 +120,7 @@ idnode_set_t *idnode_find_all(const idclass_t *idc);
void idnode_notify
(idnode_t *in, const char *chn, int force);
void idnode_notify_simple (void *in);
void idnode_notify_title_changed (void *in);
htsmsg_t *idclass_serialize0 (const idclass_t *idc, int optmask);
htsmsg_t *idnode_serialize0 (idnode_t *self, int optmask);

View file

@ -54,7 +54,7 @@ const idclass_t mpegts_input_class =
.id = "displayname",
.name = "Name",
.off = offsetof(mpegts_input_t, mi_displayname),
.notify = idnode_notify_simple,
.notify = idnode_notify_title_changed,
},
{}
}

View file

@ -452,6 +452,7 @@ mpegts_mux_initial_scan_done ( mpegts_mux_t *mm )
/* Save */
mm->mm_initial_scan_done = 1;
mm->mm_config_save(mm);
idnode_updated(&mm->mm_id);
}
/* **************************************************************************

View file

@ -2170,90 +2170,109 @@ extjs_tvhlog(http_connection_t *hc, const char *remain, void *opaque)
}
static int
extjs_idnode
(http_connection_t *hc, const char *remain, void *opaque)
extjs_idnode_tree
( http_connection_t *hc, const char *uuid, const char *root,
idnode_set_t *(*rootfn)(void), htsmsg_t **out )
{
htsbuf_queue_t *hq = &hc->hc_reply;
int isroot = 0;
htsmsg_t *out = NULL;
int isroot;
idnode_t *node = NULL;
const char *uuid = http_arg_get(&hc->hc_req_args, "uuid");
const char *op = http_arg_get(&hc->hc_req_args, "op");
#if 0
const char *root = http_arg_get(&hc->hc_req_args, "root");
if (uuid == NULL)
uuid = http_arg_get(&hc->hc_req_args, "node");
if (!strcmp(uuid, "root")) {
isroot = 1;
uuid = root;
}
if (op == NULL)
op = "get";
if(uuid == NULL)
/* Validate */
if (!uuid)
return HTTP_STATUS_BAD_REQUEST;
isroot = !strcmp("root", uuid);
if (isroot && !(root || rootfn))
return HTTP_STATUS_BAD_REQUEST;
#endif
pthread_mutex_lock(&global_lock);
if(http_access_verify(hc, ACCESS_ADMIN)) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_UNAUTHORIZED;
if (!isroot || root) {
if (!(node = idnode_find(isroot ? root : uuid, NULL))) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
}
#if 0
node = idnode_find(uuid, NULL);
#endif
*out = htsmsg_create_list();
if (!strcmp(op, "get")) {
out = htsmsg_create_list();
/* Root node */
if (isroot && node) {
htsmsg_t *m = idnode_serialize(node);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(node));
htsmsg_add_msg(out, NULL, m);
htsmsg_add_msg(*out, NULL, m);
/* Children */
} else {
idnode_set_t *v = node ? idnode_get_childs(node) : rootfn();
if (v) {
int i;
for(i = 0; i < v->is_count; i++) {
htsmsg_t *m = idnode_serialize(v->is_array[i]);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i]));
htsmsg_add_msg(*out, NULL, m);
}
idnode_set_free(v);
}
}
pthread_mutex_unlock(&global_lock);
return 0;
}
static int
extjs_idnode0
(http_connection_t *hc, const char *remain, void *opaque,
idnode_set_t *(*rootfn)(void))
{
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *out = NULL;
idnode_t *node = NULL;
const char *uuid, *root, *op = http_arg_get(&hc->hc_req_args, "op");
if (!op) return HTTP_STATUS_BAD_REQUEST;
/* Get details */
if (!strcmp(op, "get")) {
if (!(uuid = http_arg_get(&hc->hc_req_args, "uuid")))
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
if (!(node = idnode_find(uuid, NULL))) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_BAD_REQUEST;
}
out = htsmsg_create_map();
htsmsg_t *m = idnode_serialize(node);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(node));
htsmsg_add_msg(out, "nodes", m);
/* Update */
} else if (!strcmp(op, "save")) {
const char *s;
htsmsg_t *conf;
htsmsg_field_t *f;
htsmsg_t *conf, *nodes;
if ((s = http_arg_get(&hc->hc_req_args, "nodes"))) {
printf("s = %s\n", s);
htsmsg_t *nodes = htsmsg_json_deserialize(s);
htsmsg_field_t *f;
if (nodes) {
if ((nodes = htsmsg_json_deserialize(s))) {
pthread_mutex_lock(&global_lock);
HTSMSG_FOREACH(f, nodes) {
if (!(conf = htsmsg_get_map_by_field(f))) continue;
if (!(uuid = htsmsg_get_str(conf, "uuid"))) continue;
if (!(node = idnode_find(uuid, NULL))) continue;
idnode_update(node, conf);
}
pthread_mutex_unlock(&global_lock);
htsmsg_destroy(nodes);
}
} else if ((s = http_arg_get(&hc->hc_req_args, "conf"))) {
if ((conf = htsmsg_json_deserialize(s))) {
idnode_update(node, conf);
htsmsg_destroy(conf);
}
}
out = htsmsg_create_map();
} else if (!strcmp(op, "childs")) {
out = htsmsg_create_list();
if (isroot) {
htsmsg_t *m = idnode_serialize(node);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(node));
htsmsg_add_msg(out, NULL, m);
} else {
idnode_set_t *v;
if ((v = idnode_get_childs(node))) {
int i;
for(i = 0; i < v->is_count; i++) {
htsmsg_t *m = idnode_serialize(v->is_array[i]);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i]));
htsmsg_add_msg(out, NULL, m);
}
idnode_set_free(v);
}
}
}
pthread_mutex_unlock(&global_lock);
/* Children */
} else if (!strcmp(op, "childs")) {
int e;
uuid = http_arg_get(&hc->hc_req_args, "uuid");
root = http_arg_get(&hc->hc_req_args, "root");
if ((e = extjs_idnode_tree(hc, uuid, root, rootfn, &out)))
return e;
}
if (!out)
return HTTP_STATUS_BAD_REQUEST;
@ -2264,53 +2283,11 @@ printf("s = %s\n", s);
return 0;
}
/**
*
*/
static int
extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque,
idnode_set_t *(*rootfn)(void))
extjs_idnode
(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "node");
htsmsg_t *out = NULL;
if(s == NULL)
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
if(http_access_verify(hc, ACCESS_ADMIN)) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_UNAUTHORIZED;
}
out = htsmsg_create_list();
idnode_set_t *v;
if(!strcmp(s, "root")) {
v = rootfn();
} else {
v = idnode_get_childs(idnode_find(s, NULL));
}
if(v != NULL) {
int i;
for(i = 0; i < v->is_count; i++) {
htsmsg_t *m = idnode_serialize(v->is_array[i]);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i]));
htsmsg_add_msg(out, NULL, m);
}
}
pthread_mutex_unlock(&global_lock);
if (v) idnode_set_free(v);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
return extjs_idnode0(hc, remain, opaque, NULL);
}
/**
@ -2319,7 +2296,7 @@ extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque,
static int
extjs_tvadapters(http_connection_t *hc, const char *remain, void *opaque)
{
return extjs_get_idnode(hc, remain, opaque, &linuxdvb_root);
return extjs_idnode0(hc, remain, opaque, &linuxdvb_root);
}

View file

@ -152,16 +152,17 @@ tvheadend.idnode_editor = function(item, conf)
/* Buttons */
var saveBtn = new Ext.Button({
text : 'Save',
text : 'Save',
handler : function() {
var node = panel.getForm().getFieldValues();
node.uuid = item.uuid;
var params = {
uuid: item.uuid,
op : 'save',
conf: Ext.util.JSON.encode(panel.getForm().getFieldValues())
op : 'save',
nodes : Ext.util.JSON.encode([node])
};
Ext.Ajax.request({
url : 'api/idnode',
params : params,
url : 'api/idnode',
params : params,
success : function(d) {
}
});
@ -177,7 +178,7 @@ tvheadend.idnode_editor = function(item, conf)
labelWidth : 200,
autoWidth : true,
autoHeight : !conf.fixedHeight,
width : 600,
width : 600,
//defaults: {width: 330},
defaultType : 'textfield',
buttonAlign : 'left',
@ -564,6 +565,14 @@ tvheadend.idnode_grid = function(panel, conf)
}
/* Grid Panel */
var auto = new Ext.form.Checkbox({
checked : true,
listeners : {
check : function ( s, c ) {
if (c) store.reload();
}
}
});
var grid = new Ext.grid.EditorGridPanel({
stripeRows : true,
title : conf.titleP,
@ -582,35 +591,21 @@ tvheadend.idnode_grid = function(panel, conf)
pageSize : 50,
displayInfo : true,
displayMsg : conf.titleP + ' {0} - {1} of {2}',
emptyMsg : 'No ' + conf.titleP.toLowerCase() + ' to display'
emptyMsg : 'No ' + conf.titleP.toLowerCase() + ' to display',
items : [ '-', 'Auto-refresh', auto ]
})
});
panel.add(grid);
/* Add comet listeners */
if (conf.comet) {
tvheadend.comet.on(conf.comet, function(o) {
var fs = [];
var d = {};
for ( i = 0; i < o.params.length; i++)
if (o.params[i].id) {
fs.push(o.params[i].id);
d[o.params[i].id] = o.params[i].value;
}
var rec = Ext.data.Record.create(fs);
rec = new rec(d, o.id);
store.add(rec);
});
}
tvheadend.comet.on('idnodeParamsChanged', function(o) {
var r = store.getById(o.id);
if (r) {
for ( i = 0; i < o.params.length; i++)
if (o.params[i].id)
r.set(o.params[i].id, o.params[i].value);
r.commit();
}
});
var update = function(o) {
if (auto.getValue())
store.reload();
};
if (conf.comet)
tvheadend.comet.on(conf.comet, update);
tvheadend.comet.on('idnodeUpdated', update);
tvheadend.comet.on('idnodeDeleted', update);
}
/* Request data */
@ -633,20 +628,22 @@ tvheadend.idnode_grid = function(panel, conf)
tvheadend.idnode_tree = function (conf)
{
var current = null;
var params = conf.params || {};
params.op = 'childs';
var loader = new Ext.tree.TreeLoader({
dataUrl : conf.url,
baseParams : conf.params,
dataUrl : conf.url,
baseParams : params,
preloadChildren : conf.preload,
nodeParameter : 'uuid'
});
var tree = new Ext.tree.TreePanel({
loader : loader,
flex : 1,
border : false,
root : new Ext.tree.AsyncTreeNode({
id : conf.root || 'root',
text : conf.title || ''
loader : loader,
flex : 1,
border : false,
root : new Ext.tree.AsyncTreeNode({
id : conf.root || 'root',
text : conf.title || ''
}),
listeners : {
click: function(n) {
@ -662,27 +659,22 @@ tvheadend.idnode_tree = function (conf)
}
});
tvheadend.comet.on('idnodeNameChanged', function(o) {
var n = tree.getNodeById(o.id);
if(n) {
n.setText(o.text);
// TODO: top-level reload
tvheadend.comet.on('idnodeUpdated', function(o) {
var n = tree.getNodeById(o.uuid);
if (n) {
if (o.text) n.setText(o.text);
loader.load(n);
}
});
tvheadend.comet.on('idnodeParamsChanged', function(o) {
var n = tree.getNodeById(o.id);
if(n) {
n.attributes.params = o.params;
}
});
var panel = new Ext.Panel({
title : conf.title || '',
layout : 'hbox',
flex : 1,
padding : 5,
border : false,
title : conf.title || '',
layout : 'hbox',
flex : 1,
padding : 5,
border : false,
layoutConfig : {
align : 'stretch'
},