diff --git a/plugins/shd-mcp-plugin/widgets/controller-observability/source.html b/plugins/shd-mcp-plugin/widgets/controller-observability/source.html index a0639d1..4837b44 100644 --- a/plugins/shd-mcp-plugin/widgets/controller-observability/source.html +++ b/plugins/shd-mcp-plugin/widgets/controller-observability/source.html @@ -1330,13 +1330,13 @@ progress::-moz-progress-bar { devices:['Устройства контроллера','Инвентарь устройств с текущими значениями и профилем выбранной строки.'], 'live-state':['Текущие состояния','Нормализованные состояния устройств с freshness, quality и readback.'], statistics:['Статистика состояния','Серверный расчёт событий, периодов, длительностей и почасовой разбивки.'], - health:['Здоровье телеметрии','Состояние нативного источника и Influx/Grafana.'], + health:['Здоровье телеметрии','Настройка источников истории; фактическая доступность не проверяется.'], audit:['Аудит операций','Команды, подтверждения и readback по source-контроллеру.'], inventorySeparate:'Инвентарь устройств загружается отдельным запросом по выбранному контроллеру.', refresh:'Обновить',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей', deviceCount:'устройств',controllerCount:'контроллеров',stateCount:'состояний',samples:'выборок',sampleOne:'выборка',sampleFew:'выборки',sampleMany:'выборок', - sourceAuto:'Автоматически',sourceNative:'Нативный источник',sourceInflux:'Influx/Grafana', - native:'Нативный источник',influx:'Influx/Grafana',available:'доступен',unavailable:'недоступен', + sourceAuto:'Автоматически',sourceNative:'Нативная история',sourceInflux:'История Influx/Grafana', + native:'Нативная история',influx:'История Influx/Grafana',available:'доступен',unavailable:'недоступен',notChecked:'не проверено',configurationOnly:'Показана конфигурация. Доступность истории не проверялась.', configured:'настроен',points:'точек',source:'Источник',actualSource:'Фактический источник',sourceDetails:'Источник и качество',statisticsSummary:'Сводка статистики',summaryValue:'Значения',notAvailable:'нет данных', chartTitle:'Общий график',last:'последнее',seriesOne:'ряд',seriesFew:'ряда',seriesMany:'рядов', time:'Время',value:'Значение',name:'Название',controller:'Контроллер',device:'Устройство', @@ -1363,13 +1363,13 @@ progress::-moz-progress-bar { devices:['Controller devices','Device inventory with current values and a profile action for the selected row.'], 'live-state':['Current live state','Normalized device states with freshness, quality and readback.'], statistics:['State statistics','Server-computed events, periods, durations and hourly buckets.'], - health:['Telemetry health','Native-source and Influx/Grafana status.'], + health:['Telemetry health','History-source configuration; actual availability is not probed.'], audit:['Operation audit','Commands, confirmations and readback for the source controller.'], inventorySeparate:'Device inventory is loaded by a separate request for the selected controller.', refresh:'Refresh',loading:'Loading…',empty:'No data to display.',records:'records', deviceCount:'devices',controllerCount:'controllers',stateCount:'states',samples:'samples',sampleOne:'sample',sampleFew:'samples',sampleMany:'samples', - sourceAuto:'Automatic',sourceNative:'Native source',sourceInflux:'Influx/Grafana', - native:'Native source',influx:'Influx/Grafana',available:'available',unavailable:'unavailable', + sourceAuto:'Automatic',sourceNative:'Native history',sourceInflux:'Influx/Grafana history', + native:'Native history',influx:'Influx/Grafana history',available:'available',unavailable:'unavailable',notChecked:'not checked',configurationOnly:'Configuration is shown. History availability was not probed.', configured:'configured',points:'points',source:'Source',actualSource:'Actual source',sourceDetails:'Source and quality',statisticsSummary:'Statistics summary',summaryValue:'Values',notAvailable:'no data', chartTitle:'Shared chart',last:'last',seriesOne:'series',seriesFew:'series',seriesMany:'series', time:'Time',value:'Value',name:'Name',controller:'Controller',device:'Device', @@ -1417,6 +1417,8 @@ progress::-moz-progress-bar { function formatDuration(value){var seconds=Number(value);if(!Number.isFinite(seconds))return text(value);seconds=Math.max(0,Math.round(seconds));var parts=[];if(seconds>=86400){parts.push(Math.floor(seconds/86400)+' '+t('day'));seconds%=86400}if(seconds>=3600){parts.push(Math.floor(seconds/3600)+' '+t('hour'));seconds%=3600}if(seconds>=60){parts.push(Math.floor(seconds/60)+' '+t('minute'));seconds%=60}if(seconds||parts.length===0)parts.push(seconds+' '+t('second'));return parts.join(' ')} function formatFreshness(value){var ms=Number(value);return Number.isFinite(ms)?formatDuration(ms/1000):text(value)} function sourceLabel(value){return value==='native'?t('native'):value==='influx'?t('influx'):text(value)} + function healthStatusLabel(value,configurationOnly){var status=String(value||'').toLowerCase();if(status==='available')return t('available');if(status==='configured')return t('configured');return configurationOnly?t('notChecked'):t('unavailable')} + function healthSourceStatus(source,configurationOnly){if(source&&source.available===true)return{label:t('available'),kind:'good'};if(source&&source.configured===true)return{label:t('configured'),kind:''};return configurationOnly?{label:t('notChecked'),kind:''}:{label:t('unavailable'),kind:'bad'}} function sourceArgs(){return obj(state.config.sourceArgs)?state.config.sourceArgs:{}} function statisticsSeriesScope(d,useArgs){var args=useArgs?sourceArgs():{},room=String(d.room||d.room_name||d.room_label||args.room||'').trim(),item=String(d.item_name||d.label||d.name||d.item_id||args.item_id||'').trim(),controller=String(d.controller_name||d.controller||d.source_controller_id||args.controller||'').trim(),project=String(d.project||args.project||'').trim();return{project:project,controller:controller,room:room,item:item}} function scopeLabel(d){var scope=statisticsSeriesScope(d,true),parts=[];[scope.project,scope.controller,scope.room,scope.item].forEach(function(value){if(value&&!parts.includes(value))parts.push(value)});return parts.join(' / ')} @@ -1557,7 +1559,7 @@ progress::-moz-progress-bar { function renderLiveState(d){var rows=liveRows(d);var controllers=Array.isArray(d.controllers)?d.controllers:[];var controllerCount=d.controller_count||controllers.length||new Set(rows.map(function(r){return r[0]})).size;summary([{value:controllerCount+' '+t('controllerCount')},{value:(d.state_count||rows.length)+' '+t('stateCount')},{value:t('readback')+': '+boolLabel(d.readback)}]);el.content.append(rows.length?table([t('controller'),t('id'),t('name'),t('value'),t('unit'),t('quality'),t('freshness'),t('readback'),t('unavailableReason')],rows):empty())} function renderStatistics(d){if(Array.isArray(d.series)){renderStatisticsCollection(d);return}var s=obj(d.summary)?d.summary:{};var available=d.statistics_available===true;var scope=scopeLabel(d),sourcePill=statisticsSourcePill(d),summaryItems=[];if(scope)summaryItems.push({value:scope});summaryItems.push({value:kindLabel(d.statistics_kind)},{value:sampleLabel(s.sample_count||0)});if(sourcePill)summaryItems.push(sourcePill);summaryItems.push({value:dateText(d.from)+' → '+dateText(d.to)});summary(summaryItems);if(!available){var unavailable=card(t('status'));unavailable.append(document.createTextNode(t('noStats')+' '+text(d.statistics_error_code||'')));el.content.append(unavailable)}else if(d.statistics_kind==='binary'){el.content.append(metricGrid([[t('events'),formatNumber(s.event_count)], [t('transitions'),formatNumber(s.transition_count)], [t('periods'),formatNumber(s.period_count)], [t('activeTime'),formatDuration(s.active_seconds)], [t('activeShare'),formatNumber(s.active_percent)+'%'], [t('coverage'),formatDuration(s.coverage_seconds)], [t('averageDuration'),formatDuration(s.average_duration_seconds)], [t('medianDuration'),formatDuration(s.median_duration_seconds)], [t('shortest'),formatDuration(s.min_duration_seconds)], [t('longest'),formatDuration(s.max_duration_seconds)]]))}else if(d.statistics_kind==='numeric'){el.content.append(metricGrid([[t('samples'),formatNumber(s.sample_count)], [t('minimum'),formatNumber(s.min)], [t('maximum'),formatNumber(s.max)], [t('average'),formatNumber(s.average)], [t('median'),formatNumber(s.median)]]))}var meta=card(t('sourceDetails'));meta.append(table([t('source'),t('kind'),t('quality'),t('unit'),t('start'),t('end')],[[sourceLabel(d.selected_history_source||d.history_source_requested),kindLabel(d.statistics_kind),d.quality,d.value_unit||'—',dateText(d.from),dateText(d.to)]]));el.content.append(meta);var intervals=Array.isArray(d.intervals)?d.intervals:[];if(intervals.length){el.content.append(card(t('intervals'),table([t('start'),t('end'),t('duration'),t('status')],intervals.map(function(i){return[dateText(i.start),dateText(i.end),formatDuration(i.duration_seconds),i.open?t('open'):t('checked')]}))))}var buckets=Array.isArray(d.buckets)?d.buckets:[];if(buckets.length){var headers=d.statistics_kind==='binary'?[t('bucket'),t('events'),t('transitions'),t('periods'),t('activeTime')]:[t('bucket'),t('samples'),t('minimum'),t('maximum'),t('average')];el.content.append(card(t('hourly'),table(headers,buckets.map(function(b){return d.statistics_kind==='binary'?[dateText(b.bucket_start),formatNumber(b.event_count),formatNumber(b.transition_count),formatNumber(b.period_count),formatDuration(b.active_seconds)]:[dateText(b.bucket_start),formatNumber(b.sample_count),formatNumber(b.min),formatNumber(b.max),formatNumber(b.average)]}))));}var points=Array.isArray(d.points)?d.points:[];if(points.length)el.content.append(card(t('points'),table([t('time'),t('value'),t('quality')],points.slice(-80).reverse().map(function(p){return[dateText(p.time!==undefined?p.time:p.source_timestamp),p.value,p.quality]}))))} function renderTopology(d){var controllers=Array.isArray(d.controllers)?d.controllers:[];summary([{value:controllers.length+' '+t('controllerCount')},{value:t('inventorySeparate')}]);el.content.append(controllers.length?table([t('controller'),t('type'),t('revision')],controllers.map(function(c){var role=c.controller_role==='source'?t('source'):t('controller');return[c.controller_name||c.controller,role+' · '+(c.controller_type||'—'),c.source_revision]})):empty())} - function renderHealth(d){var list=Array.isArray(d.controllers)?d.controllers:[d];summary([{value:list.length+' '+t('controllerCount')}]);var grid=document.createElement('div');grid.className='grid';list.forEach(function(c){var sources=Array.isArray(c.sources)?c.sources:[];grid.append(card(c.controller||c.source_controller_id||t('controller'),'
'+pill(c.status||'—',c.status==='available'?'good':'')+'
'+pill(status,statusKind)+'
'+(configurationOnly?''+esc(t('configurationOnly'))+'
':'')+'