diff --git a/src/webui/static/app/servicemapper.js b/src/webui/static/app/servicemapper.js index f4e8873d..26b2857e 100644 --- a/src/webui/static/app/servicemapper.js +++ b/src/webui/static/app/servicemapper.js @@ -4,10 +4,8 @@ tvheadend.service_mapper_status_panel = null; -tvheadend.service_mapper_status = function() +tvheadend.service_mapper_status = function(panel, index) { - var panel; - /* Fields */ var ok = new Ext.form.Label({ fieldLabel: 'Mapped', @@ -31,7 +29,7 @@ tvheadend.service_mapper_status = function() }); /* Panel */ - panel = new Ext.FormPanel({ + var mpanel = new Ext.FormPanel({ method: 'get', title: 'Service Mapper', frame: true, @@ -72,9 +70,9 @@ tvheadend.service_mapper_status = function() } }); - tvheadend.service_mapper_status_panel = panel; - return panel; -}; + tvheadend.service_mapper_status_panel = mpanel; + tvheadend.paneladd(panel, mpanel, index); +} /* * Start mapping @@ -182,4 +180,4 @@ tvheadend.service_mapper = function(t, e, store, select) }); win.show(); -}; +} diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index ef77fbc4..13caa2b9 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -1,44 +1,19 @@ /** * */ -tvheadend.status_subs = function() { - - tvheadend.subsStore = new Ext.data.JsonStore({ - root: 'entries', - totalProperty: 'totalCount', - fields: [ - { name: 'id' }, - { name: 'hostname' }, - { name: 'username' }, - { name: 'title' }, - { name: 'channel' }, - { name: 'service' }, - { name: 'state' }, - { name: 'errors' }, - { name: 'in' }, - { name: 'out' }, - { - name: 'start', - type: 'date', - dateFormat: 'U' /* unix time */ - } - ], - url: 'api/status/subscriptions', - autoLoad: true, - id: 'id' - }); - - - - tvheadend.comet.on('subscriptions', function(m) { +tvheadend.status_subs = function(panel, index) +{ + var subs = null; + var store = null; + function update(m) { if (m.reload != null) - tvheadend.subsStore.reload(); + store.reload(); if (m.updateEntry != null) { - r = tvheadend.subsStore.getById(m.id); + r = store.getById(m.id); if (typeof r === 'undefined') { - tvheadend.subsStore.reload(); + store.reload(); return; } @@ -49,379 +24,495 @@ tvheadend.status_subs = function() { r.data.in = m.in; r.data.out = m.out; - tvheadend.subsStore.afterEdit(r); - tvheadend.subsStore.fireEvent('updated', tvheadend.subsStore, r, - Ext.data.Record.COMMIT); + store.afterEdit(r); + store.fireEvent('updated', store, r, Ext.data.Record.COMMIT); } - }); - - function renderDate(value) { - var dt = new Date(value); - return dt.format('D j M H:i'); } - function renderBw(value, item, store) { - var txt = parseInt(value / 125); - var href = 'javascript:tvheadend.subscription_bw_monitor(' + store.id + ');'; - return '' + txt + ''; + function builder() { + if (subs) + return; + + store = new Ext.data.JsonStore({ + root: 'entries', + totalProperty: 'totalCount', + fields: [ + { name: 'id' }, + { name: 'hostname' }, + { name: 'username' }, + { name: 'title' }, + { name: 'channel' }, + { name: 'service' }, + { name: 'state' }, + { name: 'errors' }, + { name: 'in' }, + { name: 'out' }, + { + name: 'start', + type: 'date', + dateFormat: 'U' /* unix time */ + } + ], + url: 'api/status/subscriptions', + autoLoad: true, + id: 'id' + }); + tvheadend.subsStore = store; + + tvheadend.comet.on('subscriptions', update); + + function renderBw(value, item, record) { + var txt = parseInt(value / 125); + var href = 'javascript:tvheadend.subscription_bw_monitor(' + record.id + ');'; + return '' + txt + ''; + } + + var subsCm = new Ext.grid.ColumnModel([ + { + width: 50, + id: 'hostname', + header: "Hostname", + dataIndex: 'hostname' + }, + { + width: 50, + id: 'username', + header: "Username", + dataIndex: 'username' + }, + { + width: 80, + id: 'title', + header: "Title", + dataIndex: 'title' + }, + { + width: 50, + id: 'channel', + header: "Channel", + dataIndex: 'channel' + }, + { + width: 200, + id: 'service', + header: "Service", + dataIndex: 'service' + }, + { + width: 50, + id: 'start', + header: "Start", + dataIndex: 'start', + renderer: function(v) { + var dt = new Date(v); + return dt.format('D j M H:i'); + } + }, + { + width: 50, + id: 'state', + header: "State", + dataIndex: 'state' + }, + { + width: 50, + id: 'errors', + header: "Errors", + dataIndex: 'errors' + }, + { + width: 50, + id: 'in', + header: "Input (kb/s)", + dataIndex: 'in', + renderer: renderBw, + }, + { + width: 50, + id: 'out', + header: "Output (kb/s)", + dataIndex: 'out', + renderer: renderBw + } + ]); + + subs = new Ext.grid.GridPanel({ + border: false, + loadMask: true, + stripeRows: true, + disableSelection: true, + store: store, + cm: subsCm, + flex: 1, + viewConfig: { + forceFit: true + } + }); + + dpanel.add(subs); + dpanel.doLayout(false, true); + } + + function destroyer() { + if (subs === null || !tvheadend.dynamic) + return; + dpanel.removeAll() + tvheadend.subsStore = null; + store.destroy(); + store = null; + subs = null; } - var subsCm = new Ext.grid.ColumnModel([ - { - width: 50, - id: 'hostname', - header: "Hostname", - dataIndex: 'hostname' - }, - { - width: 50, - id: 'username', - header: "Username", - dataIndex: 'username' - }, - { - width: 80, - id: 'title', - header: "Title", - dataIndex: 'title' - }, - { - width: 50, - id: 'channel', - header: "Channel", - dataIndex: 'channel' - }, - { - width: 200, - id: 'service', - header: "Service", - dataIndex: 'service' - }, - { - width: 50, - id: 'start', - header: "Start", - dataIndex: 'start', - renderer: renderDate - }, - { - width: 50, - id: 'state', - header: "State", - dataIndex: 'state' - }, - { - width: 50, - id: 'errors', - header: "Errors", - dataIndex: 'errors' - }, - { - width: 50, - id: 'in', - header: "Input (kb/s)", - dataIndex: 'in', - renderer: renderBw - }, - { - width: 50, - id: 'out', - header: "Output (kb/s)", - dataIndex: 'out', - renderer: renderBw - } - ]); - - var subs = new Ext.grid.GridPanel({ + var dpanel = new Ext.Panel({ border: false, - loadMask: true, - stripeRows: true, - disableSelection: true, + header: false, + layout: 'fit', title: 'Subscriptions', - iconCls: 'eye', - store: tvheadend.subsStore, - cm: subsCm, - flex: 1, - viewConfig: { - forceFit: true - } + iconCls: 'eye' }); - return subs; + + tvheadend.paneladd(panel, dpanel, index); + tvheadend.panelreg(panel, dpanel, builder, destroyer); }; /** * Streams */ -tvheadend.status_streams = function() { +tvheadend.status_streams = function(panel, index) +{ + var grid = null; + var store = null; - tvheadend.streamStatusStore = new Ext.data.JsonStore({ - root: 'entries', - totalProperty: 'totalCount', - fields: [ - { name: 'uuid' }, - { name: 'input' }, - { name: 'username' }, - { name: 'stream' }, - { name: 'subs' }, - { name: 'weight' }, - { name: 'signal' }, - { name: 'ber' }, - { name: 'unc' }, - { name: 'snr' }, - { name: 'bps' }, - { name: 'cc' }, - { name: 'te' }, - { name: 'signal_scale' }, - { name: 'snr_scale' }, - { name: 'ec_bit' }, - { name: 'tc_bit' }, - { name: 'ec_block' }, - { name: 'tc_block' } - ], - url: 'api/status/inputs', - autoLoad: true, - id: 'uuid' - }); + function update(m) { + if (m.reload != null) { + store.reload(); + return; + } + if (m.update == null) + return; + var r = store.getById(m.uuid); + if (!r) { + store.reload(); + return; + } + r.data.subs = m.subs; + r.data.weight = m.weight; + r.data.signal = m.signal; + r.data.ber = m.ber; + r.data.unc = m.unc; + r.data.snr = m.snr; + r.data.bps = m.bps; + r.data.cc = m.cc; + r.data.te = m.te; + r.data.signal_scale = m.signal_scale; + r.data.snr_scale = m.snr_scale; + r.data.ec_bit = m.ec_bit; + r.data.tc_bit = m.tc_bit; + r.data.ec_block = m.ec_block; + r.data.tc_block = m.tc_block; - function renderBw(value, item, store) { - var txt = parseInt(value / 1024); - var href = "javascript:tvheadend.stream_bw_monitor('" + store.id + "');"; - return '' + txt + ''; + store.afterEdit(r); + store.fireEvent('updated', store, Ext.data.Record.COMMIT); } - function renderBer(value, item, store) { - if (store.data.tc_bit == 0) - return value; // fallback (driver/vendor dependent ber) + function builder() { + if (grid) + return; - // ber = error_bit_count / total_bit_count - var ber = store.data.ec_bit / store.data.tc_bit; - return ber; + store = new Ext.data.JsonStore({ + root: 'entries', + totalProperty: 'totalCount', + fields: [ + { name: 'uuid' }, + { name: 'input' }, + { name: 'username' }, + { name: 'stream' }, + { name: 'subs' }, + { name: 'weight' }, + { name: 'signal' }, + { name: 'ber' }, + { name: 'unc' }, + { name: 'snr' }, + { name: 'bps' }, + { name: 'cc' }, + { name: 'te' }, + { name: 'signal_scale' }, + { name: 'snr_scale' }, + { name: 'ec_bit' }, + { name: 'tc_bit' }, + { name: 'ec_block' }, + { name: 'tc_block' } + ], + url: 'api/status/inputs', + autoLoad: true, + id: 'uuid' + }); + tvheadend.streamStatusStore = store; + + tvheadend.comet.on('input_status', update); + + function renderBw(value, item, record) { + var txt = parseInt(value / 1024); + var href = "javascript:tvheadend.stream_bw_monitor('" + record.id + "');"; + return '' + txt + ''; + } + + function renderBer(value, item, store) { + if (store.data.tc_bit == 0) + return value; // fallback (driver/vendor dependent ber) + + // ber = error_bit_count / total_bit_count + var ber = store.data.ec_bit / store.data.tc_bit; + return ber; + } + + function renderPer(value, item, store) { + if (value == 0) // value: total_block_count + return 'Unknown'; + + // per = error_block_count / total_block_count + var per = store.data.ec_block / value; + return per; + } + + var cm = new Ext.grid.ColumnModel([ + { + width: 120, + header: "Input", + dataIndex: 'input' + }, + { + width: 100, + header: "Stream", + dataIndex: 'stream' + }, + { + width: 50, + header: "Subs #", + dataIndex: 'subs' + }, + { + width: 50, + header: "Weight", + dataIndex: 'weight' + }, + { + width: 50, + header: "Bandwidth (kb/s)", + dataIndex: 'bps', + renderer: renderBw + }, + { + width: 50, + header: "BER", + dataIndex: 'ber', + renderer: renderBer + }, + { + width: 50, + header: "PER", + dataIndex: 'tc_block', + renderer: renderPer + }, + { + width: 50, + header: "Uncorrected Blocks", + dataIndex: 'unc' + }, + { + width: 50, + header: "Transport Errors", + dataIndex: 'te' + }, + { + width: 50, + header: "Continuity Errors", + dataIndex: 'cc' + } + ]); + + cm.config.push(new Ext.ux.grid.ProgressColumn({ + header: "SNR", + dataIndex: 'snr', + width: 85, + colored: true, + ceiling: 65535, + tvh_renderer: function(v, p, record) { + var scale = record.get('snr_scale'); + if (scale == 1) + return v; + if (scale == 2 && v > 0) { + var snr = v * 0.0001; + return snr.toFixed(1) + " dB"; + } + return 'Unknown'; + }, + destroy: function() { + } + })); + + cm.config.push(new Ext.ux.grid.ProgressColumn({ + header: "Signal Strength", + dataIndex: 'signal', + width: 85, + colored: true, + ceiling: 65535, + tvh_renderer: function(v, p, record) { + var scale = record.get('snr_scale'); + if (scale == 1) + return v; + if (scale == 2 && v > 0) { + var snr = v * 0.0001; + return snr.toFixed(1) + " dBm"; + } + return 'Unknown'; + }, + destroy: function() { + } + })); + + grid = new Ext.grid.GridPanel({ + border: false, + loadMask: true, + stripeRows: true, + disableSelection: true, + store: store, + cm: cm, + flex: 1, + viewConfig: { + forceFit: true + } + }); + + dpanel.add(grid); + dpanel.doLayout(false, true); } - function renderPer(value, item, store) { - if (value == 0) // value: total_block_count - return 'Unknown'; - - // per = error_block_count / total_block_count - var per = store.data.ec_block / value; - return per; + function destroyer() { + if (grid === null || !tvheadend.dynamic) + return; + dpanel.removeAll() + tvheadend.streamStatusStore = null; + store.destroy(); + store = null; + grid = null; } - var cm = new Ext.grid.ColumnModel([ - { - width: 120, - header: "Input", - dataIndex: 'input' - }, - { - width: 100, - header: "Stream", - dataIndex: 'stream' - }, - { - width: 50, - header: "Subs #", - dataIndex: 'subs' - }, - { - width: 50, - header: "Weight", - dataIndex: 'weight' - }, - { - width: 50, - header: "Bandwidth (kb/s)", - dataIndex: 'bps', - renderer: renderBw - }, - { - width: 50, - header: "BER", - dataIndex: 'ber', - renderer: renderBer - }, - { - width: 50, - header: "PER", - dataIndex: 'tc_block', - renderer: renderPer - }, - { - width: 50, - header: "Uncorrected Blocks", - dataIndex: 'unc' - }, - { - width: 50, - header: "Transport Errors", - dataIndex: 'te' - }, - { - width: 50, - header: "Continuity Errors", - dataIndex: 'cc' - } - ]); - - cm.config.push(new Ext.ux.grid.ProgressColumn({ - header: "SNR", - dataIndex: 'snr', - width: 85, - colored: true, - ceiling: 65535, - tvh_renderer: function(v, p, record) { - var scale = record.get('snr_scale'); - if (scale == 1) - return v; - if (scale == 2 && v > 0) { - var snr = v * 0.0001; - return snr.toFixed(1) + " dB"; - } - return 'Unknown'; - } - })); - - cm.config.push(new Ext.ux.grid.ProgressColumn({ - header: "Signal Strength", - dataIndex: 'signal', - width: 85, - colored: true, - ceiling: 65535, - tvh_renderer: function(v, p, record) { - var scale = record.get('snr_scale'); - if (scale == 1) - return v; - if (scale == 2 && v > 0) { - var snr = v * 0.0001; - return snr.toFixed(1) + " dBm"; - } - return 'Unknown'; - } - })); - - tvheadend.comet.on('input_status', function(m) { - if (m.reload != null) - tvheadend.streamStatusStore.reload(); - if (m.update != null) { - var r = tvheadend.streamStatusStore.getById(m.uuid); - if (r) { - r.data.subs = m.subs; - r.data.weight = m.weight; - r.data.signal = m.signal; - r.data.ber = m.ber; - r.data.unc = m.unc; - r.data.snr = m.snr; - r.data.bps = m.bps; - r.data.cc = m.cc; - r.data.te = m.te; - r.data.signal_scale = m.signal_scale; - r.data.snr_scale = m.snr_scale; - r.data.ec_bit = m.ec_bit; - r.data.tc_bit = m.tc_bit; - r.data.ec_block = m.ec_block; - r.data.tc_block = m.tc_block; - - tvheadend.streamStatusStore.afterEdit(r); - tvheadend.streamStatusStore.fireEvent('updated', - tvheadend.streamStatusStore, - r, - Ext.data.Record.COMMIT); - } else { - tvheadend.streamStatusStore.reload(); - } - } - }); - - var panel = new Ext.grid.GridPanel({ + var dpanel = new Ext.Panel({ border: false, - loadMask: true, - stripeRows: true, - disableSelection: true, + header: false, + layout: 'fit', title: 'Stream', - iconCls: 'hardware', - store: tvheadend.streamStatusStore, - cm: cm, - flex: 1, - viewConfig: { - forceFit: true - } + iconCls: 'hardware' }); - return panel; + + tvheadend.paneladd(panel, dpanel, index); + tvheadend.panelreg(panel, dpanel, builder, destroyer); }; /** * */ -tvheadend.status_conns = function() { +tvheadend.status_conns = function(panel, index) { - var store = new Ext.data.JsonStore({ - root: 'entries', - totalProperty: 'totalCount', - fields: [ - { name: 'id' }, - { name: 'type' }, - { name: 'peer' }, - { name: 'user' }, - { - name: 'started', - type: 'date', - dateFormat: 'U' /* unix time */ - } - ], - url: 'api/status/connections', - autoLoad: true, - id: 'id' - }); + var grid = null; + var store = null; - tvheadend.comet.on('connections', function(m) { + function update(m) { if (m.reload != null) store.reload(); - }); - - function renderDate(value) { - var dt = new Date(value); - return dt.format('Y-m-d H:i:s'); } - var cm = new Ext.grid.ColumnModel([{ - width: 50, - id: 'type', - header: "Type", - dataIndex: 'type' - }, { - width: 50, - id: 'peer', - header: "IP Address", - dataIndex: 'peer' - }, { - width: 50, - id: 'user', - header: "Username", - dataIndex: 'user' - }, { - width: 50, - id: 'started', - header: "Started", - dataIndex: 'started', - renderer: renderDate - }]); + function builder() { + if (grid) + return; - var panel = new Ext.grid.GridPanel({ - border: false, - loadMask: true, - stripeRows: true, - disableSelection: true, - title: 'Connections', - iconCls: 'eye', - store: store, - cm: cm, - flex: 1, - viewConfig: { - forceFit: true + store = new Ext.data.JsonStore({ + root: 'entries', + totalProperty: 'totalCount', + fields: [ + { name: 'id' }, + { name: 'type' }, + { name: 'peer' }, + { name: 'user' }, + { + name: 'started', + type: 'date', + dateFormat: 'U' /* unix time */ + } + ], + url: 'api/status/connections', + autoLoad: true, + id: 'id' + }); + + tvheadend.comet.on('connections', update); + + function renderDate(value) { + var dt = new Date(value); + return dt.format('Y-m-d H:i:s'); } + + var cm = new Ext.grid.ColumnModel([{ + width: 50, + id: 'type', + header: "Type", + dataIndex: 'type' + }, { + width: 50, + id: 'peer', + header: "IP Address", + dataIndex: 'peer' + }, { + width: 50, + id: 'user', + header: "Username", + dataIndex: 'user' + }, { + width: 50, + id: 'started', + header: "Started", + dataIndex: 'started', + renderer: renderDate + }]); + + grid = new Ext.grid.GridPanel({ + border: false, + loadMask: true, + stripeRows: true, + disableSelection: true, + store: store, + cm: cm, + flex: 1, + viewConfig: { + forceFit: true + } + }); + + dpanel.add(grid); + dpanel.doLayout(false, true); + } + + function destroyer() { + if (grid === null || !tvheadend.dynamic) + return; + dpanel.removeAll() + store.destroy(); + store = null; + grid = null; + } + + var dpanel = new Ext.Panel({ + border: false, + header: false, + layout: 'fit', + title: 'Connections', + iconCls: 'eye' }); - return panel; + + tvheadend.paneladd(panel, dpanel, index); + tvheadend.panelreg(panel, dpanel, builder, destroyer); }; tvheadend.status = function() { @@ -430,13 +521,12 @@ tvheadend.status = function() { autoScroll: true, activeTab: 0, iconCls: 'eye', - items: [ - new tvheadend.status_streams, - new tvheadend.status_subs, - new tvheadend.status_conns, - new tvheadend.service_mapper_status - ] + items: [], }); + tvheadend.status_streams(panel); + tvheadend.status_subs(panel); + tvheadend.status_conns(panel); + tvheadend.service_mapper_status(panel); return panel; }; @@ -510,8 +600,9 @@ tvheadend.subscription_bw_monitor = function(id) { var task = { interval: 1000, run: function() { - r = tvheadend.subsStore.getById(id); - if (typeof r === 'undefined') { + var store = tvheadend.subsStore; + var r = store ? store.getById(id) : null; + if (!store || typeof r === 'undefined') { chart.stop(); Ext.TaskMgr.stop(task); return; @@ -600,10 +691,11 @@ tvheadend.stream_bw_monitor = function(id) { }); var task = { - interval: 1000, + interval: 10000, run: function() { - r = tvheadend.streamStatusStore.getById(id); - if (typeof r === 'undefined') { + var store = tvheadend.streamStatusStore; + var r = store ? store.getById(id) : null; + if (!store || typeof r === 'undefined') { chart.stop(); Ext.TaskMgr.stop(task); return;