From 2d8ccadcd91accc078f5494358282ad81a0b9f69 Mon Sep 17 00:00:00 2001 From: Paulo Vitor Magacho Date: Tue, 2 Apr 2024 08:32:47 -0300 Subject: [PATCH] Fixed many bugs. Improved stability and added features --- .gitignore | 18 +- dist/module.js | 2 +- dist/module.js.map | 2 +- dist/plugin.json | 2 +- package.json | 9 +- pkg/main.go | 2 +- pkg/plugin/annotation_query.go | 4 +- pkg/plugin/datasource.go | 60 ++- pkg/plugin/datasource_models.go | 6 +- pkg/plugin/helpers.go | 415 +++++++++--------- pkg/plugin/steam.go | 57 ++- pkg/plugin/timeseries_query.go | 242 ++++++---- pkg/plugin/timeseries_query_models.go | 79 ++-- pkg/plugin/timeseries_response_error_model.go | 15 + pkg/plugin/timeseries_response_models.go | 74 ++-- .../timeseries_response_single_model.go | 20 + .../timeseries_response_summary_model.go | 3 - pkg/plugin/webidcache.go | 172 ++++++-- src/datasource.ts | 85 +++- src/helper.ts | 53 --- src/types.ts | 12 +- yarn.lock | 43 +- 22 files changed, 841 insertions(+), 534 deletions(-) diff --git a/.gitignore b/.gitignore index e26b968..289a2a0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -node_modules/ - # Runtime data pids *.pid @@ -19,12 +17,16 @@ lib-cov # Coverage directory used by tools like istanbul coverage -# Compiled binary addons (https://nodejs.org/api/addons.html) -dist/ -artifacts/ -work/ -ci/ -e2e-results/ +node_modules +dist +vendor +.tscache +coverage +.idea +.vscode + +# OS generated files +.DS_Store # Editor .idea diff --git a/dist/module.js b/dist/module.js index d84e02b..e8d45d2 100644 --- a/dist/module.js +++ b/dist/module.js @@ -1,2 +1,2 @@ -define(["@grafana/data","react","@grafana/ui","lodash","rxjs","@grafana/runtime"],((e,t,a,n,l,r)=>(()=>{"use strict";var i={781:t=>{t.exports=e},531:e=>{e.exports=r},7:e=>{e.exports=a},241:e=>{e.exports=n},959:e=>{e.exports=t},269:e=>{e.exports=l}},s={};function o(e){var t=s[e];if(void 0!==t)return t.exports;var a=s[e]={exports:{}};return i[e](a,a.exports,o),a.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var a in t)o.o(t,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var u={};return(()=>{o.r(u),o.d(u,{plugin:()=>M});var e=o(781),t=o(959),a=o.n(t),n=o(7);function l(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e){for(var t=1;ti(r({},e),{jsonData:i(r({},e.jsonData),{url:e.url})});class m extends t.PureComponent{render(){const{options:e}=this.props,t=h(e);return a().createElement("div",null,a().createElement(n.DataSourceHttpSettings,{defaultUrl:"https://server.name/piwebapi",dataSourceConfig:t,onChange:this.onHttpOptionsChange,showAccessOptions:!0}),a().createElement("h3",{className:"page-heading"},"Custom Configuration"),a().createElement("div",{className:"gf-form-group"},a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable PI Points in Query",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.pipoint,onChange:this.onPiPointChange}))),a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable New Data Format",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.newFormat,onChange:this.onNewFormatChange}))),a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable Unit in Data",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.useUnit,onChange:this.onUseUnitChange}))),a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable Experimental Features",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.useExperimental,onChange:this.onUseExperimentalChange}))),t.jsonData.useExperimental&&a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable Steaming Support",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.useStreaming,onChange:this.onUseStreamingChange})))),a().createElement("h3",{className:"page-heading"},"PI/AF Connection Details"),a().createElement("div",{className:"gf-form-group"},t.jsonData.pipoint&&a().createElement("div",{className:"gf-form"},a().createElement(s,{label:"PI Server",labelWidth:10,inputWidth:25,onChange:this.onPIServerChange,value:t.jsonData.piserver||"",placeholder:"Default PI Server to use for data requests"})),a().createElement("div",{className:"gf-form"},a().createElement(s,{label:"AF Server",labelWidth:10,inputWidth:25,onChange:this.onAFServerChange,value:t.jsonData.afserver||"",placeholder:"Default AF Server to use for data requests"})),a().createElement("div",{className:"gf-form"},a().createElement(s,{label:"AF Database",labelWidth:10,inputWidth:25,onChange:this.onAFDatabaseChange,value:t.jsonData.afdatabase||"",placeholder:"Default AF Database server for AF queries"}))))}constructor(...e){super(...e),l(this,"onPIServerChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{piserver:e.target.value});t(i(r({},a),{jsonData:n}))})),l(this,"onAFServerChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{afserver:e.target.value});t(i(r({},a),{jsonData:n}))})),l(this,"onAFDatabaseChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{afdatabase:e.target.value});t(i(r({},a),{jsonData:n}))})),l(this,"onHttpOptionsChange",(e=>{const{onOptionsChange:t}=this.props;t(h(e))})),l(this,"onPiPointChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{piserver:e.target.checked?a.jsonData.piserver:"",pipoint:e.target.checked});t(i(r({},a),{jsonData:n}))})),l(this,"onNewFormatChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{newFormat:e.target.checked});t(i(r({},a),{jsonData:n}))})),l(this,"onUseUnitChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{useUnit:e.target.checked});t(i(r({},a),{jsonData:n}))})),l(this,"onUseExperimentalChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{useExperimental:e.target.checked,useStreaming:!!e.target.checked&&a.jsonData.useStreaming});t(i(r({},a),{jsonData:n}))})),l(this,"onUseStreamingChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{useStreaming:e.target.checked});t(i(r({},a),{jsonData:n}))}))}}var c=o(241);function d(){return d=Object.assign||function(e){for(var t=1;ta().createElement(a().Fragment,null,a().createElement(n.InlineFormLabel,{width:t,tooltip:l},e),r),v=()=>a().createElement("div",{className:"gf-form gf-form--grow"},a().createElement("div",{className:"gf-form-label gf-form-label--grow"})),b=e=>{var t=d({},p(e));return a().createElement(f,null,a().createElement(g,t))},f=e=>a().createElement("div",{className:"gf-form-inline"},e.children,a().createElement(v,null)),y=e=>{var t=d({},p(e));return a().createElement(E,null,a().createElement(g,t))},E=e=>a().createElement(a().Fragment,null,e.children),S={target:";",attributes:[],segments:[],regex:{enable:!1},summary:{types:[],basis:"EventWeighted",interval:"",nodata:"Null"},expression:"",interpolate:{enable:!1},useLastValue:{enable:!1},recordedValues:{enable:!1},digitalStates:{enable:!1},enableStreaming:{enable:!1},useUnit:{enable:!1},isPiPoint:!1},C=({isRaw:e,onChange:l})=>{const[r,i]=(0,t.useState)(!1);return(0,t.useEffect)((()=>{i(!1)}),[e]),e?a().createElement(a().Fragment,null,a().createElement(n.Button,{"aria-label":"Switch to visual editor",icon:"pen",variant:"secondary",type:"button",onClick:()=>{i(!0)}}),a().createElement(n.ConfirmModal,{isOpen:r,title:"Switch to visual editor mode",body:"Are you sure to switch to visual editor mode? You will lose the changes done in raw query mode.",confirmText:"Yes, switch to editor mode",dismissText:"No, stay in raw query mode",onConfirm:()=>{l(!1)},onDismiss:()=>{i(!1)}})):a().createElement(n.Button,{"aria-label":"Switch to text editor",icon:"pen",variant:"secondary",type:"button",onClick:()=>{l(!0)}})};function P(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function I(e){for(var t=1;t{var t;return e.value?a().createElement("div",{className:"gf-form-label "+("template"===e.value.type?"query-keyword":"")},null!==(t=e.label)&&void 0!==t?t:"--no label--"):a().createElement("a",{className:"gf-form-label query-part"},a().createElement(n.Icon,{name:"plus"}))};class x extends t.PureComponent{isValueEmpty(e){return!e||!e.value||!e.value.length||e.value===D}calcBasisValueChanged(e){var t;const a=this.props.query,n=a.summary;n.basis=null===(t=e.value)||void 0===t?void 0:t.value,this.onChange(w(I({},a),{summary:n}))}getCalcBasisSegments(){return(0,c.map)(this.calculationBasis,(e=>({label:e,value:{value:e,expandable:!0}})))}calcNoDataValueChanged(e){var t;const a=this.props.query,n=a.summary;n.nodata=null===(t=e.value)||void 0===t?void 0:t.value,this.onChange(w(I({},a),{summary:n}))}getNoDataSegments(){return(0,c.map)(this.noDataReplacement,(e=>({label:e,value:{value:e,expandable:!0}})))}onSummaryValueChanged(e,t){const a=this.state.summaries.slice(0);a[t]=e,this.isValueEmpty(e.value)&&a.splice(t,1),this.setState({summaries:a},this.stateCallback)}getSummarySegments(){const e=(0,c.filter)(this.summaryTypes,(e=>-1===this.state.summaries.map((e=>{var t;return null===(t=e.value)||void 0===t?void 0:t.value})).indexOf(e))),t=(0,c.map)(e,(e=>({label:e,value:{value:e,expandable:!0}})));return t.unshift({label:D,value:{value:D}}),t}removeSummary(e){const t=(0,c.filter)(this.state.summaries,(t=>t!==e));this.setState({summaries:t})}onSummaryAction(e){const t=this.state.summaries.slice(0);if(!this.isValueEmpty(e.value)){var a;let n={label:e.label,value:{value:null===(a=e.value)||void 0===a?void 0:a.value,expandable:!0}};t.push(n)}this.setState({summarySegment:{},summaries:t},this.stateCallback)}removeAttribute(e){const t=(0,c.filter)(this.state.attributes,(t=>t!==e));this.attributeChangeValue(t)}onAttributeAction(e){const{query:t}=this.props,a=this.state.attributes.slice(0);if(!this.isValueEmpty(e.value)){var n;let l={label:e.label,value:{value:null===(n=e.value)||void 0===n?void 0:n.value,expandable:!t.isPiPoint}};a.push(l)}this.attributeChangeValue(a)}getSegmentPathUpTo(e,t){const a=e.slice(0,t);return(0,c.reduce)(a,((e,t)=>{var a;return t.value?(null===(a=t.value.value)||void 0===a?void 0:a.startsWith("-Select"))?e:e?e+"\\"+t.value.value:t.value.value:""}),"")}checkAttributeSegments(e,t){var a;const{datasource:n,data:l}=this.props,r=this,i={path:this.getSegmentPathUpTo(t.slice(0),t.length),type:"attributes"};var s;return n.metricFindQuery(i,Object.assign(null!==(s=null==l||null===(a=l.request)||void 0===a?void 0:a.scopedVars)&&void 0!==s?s:{},{isPiPoint:!1})).then((t=>{const a={};(0,c.each)(t,(e=>{a[e.Path.substring(e.Path.indexOf("|")+1)]=e.WebId}));const l=(0,c.filter)(e,(e=>{var t;const l=n.templateSrv.replace(null===(t=e.value)||void 0===t?void 0:t.value);return void 0!==a[l]}));r.availableAttributes=a,this.attributeChangeValue(l)})).catch((t=>{r.error=t.message||"Failed to issue metric query",this.attributeChangeValue(e)}))}checkPiPointSegments(e,t){var a;const{datasource:n,data:l}=this.props,r=this,i={path:e.path,webId:r.getSelectedPIServer(),pointName:e.label,type:"pipoint"};var s;return n.metricFindQuery(i,Object.assign(null!==(s=null==l||null===(a=l.request)||void 0===a?void 0:a.scopedVars)&&void 0!==s?s:{},{isPiPoint:!0})).then((()=>{r.attributeChangeValue(t)})).catch((e=>{r.error=e.message||"Failed to issue metric query",r.attributeChangeValue([])}))}getSelectedPIServer(){var e;let t="";return this.piServer.forEach((e=>{const a=this.props.query.target.split(";");a.length>=2&&a[0]===e.text&&(t=e.WebId)})),this.piServer.length>0?null===(e=this.piServer[0].value)||void 0===e?void 0:e.webId:t}textEditorChanged(){const{query:e,onChange:t}=this.props,a=e.target.split(";"),n=a.length>0?a[0].split("\\"):[];let l=[],r=[];n.length>1||1===n.length&&""!==n[0]?(a.splice(0,1),(0,c.each)(n,((e,t)=>{l.push({label:e,value:{type:e.match(/\${\w+}/gi)?"template":void 0,value:e,expandable:!0}})})),(0,c.each)(a,(function(e,t){""!==e&&r.push({label:e,value:{value:e,expandable:!1}})})),this.getElementSegments(n.length+1,l).then((e=>{e.length>0&&l.push({label:"Select Element",value:{value:"-Select Element-"}})})).then((()=>{this.updateArray(l,r,this.state.summaries,e.isPiPoint,(()=>{t(w(I({},e),{query:void 0,rawQuery:!1}))}))}))):(l=this.checkAfServer(),this.updateArray(l,this.state.attributes,this.state.summaries,e.isPiPoint,(()=>{this.onChange(w(I({},e),{query:void 0,rawQuery:!1,attributes:this.state.attributes,segments:this.state.segments}))})))}render(){const{query:e,onChange:t,onRunQuery:l}=this.props,r=(0,c.defaults)(e,S),{useLastValue:i,useUnit:s,interpolate:o,query:u,rawQuery:h,digitalStates:m,enableStreaming:d,recordedValues:p,expression:g,isPiPoint:f,summary:E,display:P,regex:D}=r;return a().createElement(a().Fragment,null,this.props.datasource.piPointConfig&&a().createElement(n.InlineField,{label:"Is Pi Point?",labelWidth:O},a().createElement(n.InlineSwitch,{value:f,onChange:this.onIsPiPointChange})),!!h&&a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Raw Query",labelWidth:O,grow:!0},a().createElement(n.Input,{onBlur:this.stateCallback,value:u,onChange:e=>t(w(I({},r),{query:e.target.value})),placeholder:"enter query"})),a().createElement(C,{isRaw:!0,onChange:e=>this.textEditorChanged()})),!h&&a().createElement(a().Fragment,null,a().createElement("div",{className:"gf-form-inline"},a().createElement(y,{label:f?"PI Server":"AF Elements",tooltip:f?"Select PI server.":"Select AF Element."},this.state.segments.map(((e,t)=>a().createElement(n.SegmentAsync,{key:"element-"+t,Component:a().createElement(j,{value:e.value,label:e.label}),onChange:e=>this.onSegmentChange(e,t),loadOptions:e=>this.getElementSegments(t),allowCustomValue:!0,inputMinWidth:200}))),a().createElement(v,null),!f&&a().createElement(C,{isRaw:!1,onChange:e=>{t(w(I({},r),{query:r.target,rawQuery:e}))}}))),a().createElement(b,{label:f?"Pi Points":"Attributes"},this.state.attributes.map(((e,t)=>f?a().createElement(n.SegmentAsync,{key:"attributes-"+t,Component:a().createElement(j,{value:e.value,label:e.label}),disabled:0===this.piServer.length,onChange:e=>this.onPiPointChange(e,t),loadOptions:this.getAttributeSegmentsPI,reloadOptionsOnChange:!0,allowCustomValue:!0,inputMinWidth:F}):a().createElement(n.Segment,{key:"attributes-"+t,Component:a().createElement(j,{value:e.value,label:e.label}),disabled:this.state.segments.length<=2,onChange:e=>this.onAttributeChange(e,t),options:this.getAttributeSegmentsAF(),allowCustomValue:!0,inputMinWidth:F}))),f&&a().createElement(n.SegmentAsync,{Component:a().createElement(j,{value:this.state.attributeSegment.value,label:this.state.attributeSegment.label}),disabled:0===this.piServer.length,onChange:this.onAttributeAction,loadOptions:this.getAttributeSegmentsPI,reloadOptionsOnChange:!0,allowCustomValue:!0,inputMinWidth:F}),!f&&a().createElement(n.Segment,{Component:a().createElement(j,{value:this.state.attributeSegment.value,label:this.state.attributeSegment.label}),disabled:this.state.segments.length<=2,onChange:this.onAttributeAction,options:this.getAttributeSegmentsAF(),allowCustomValue:!0,inputMinWidth:F}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Use Last Value",tooltip:"Fetch only last value from time range",labelWidth:O},a().createElement(n.InlineSwitch,{value:i.enable,onChange:()=>this.onChange(w(I({},r),{useLastValue:w(I({},i),{enable:!i.enable})}))})),this.props.datasource.useUnitConfig&&a().createElement(n.InlineField,{label:"Use unit from datapoints",tooltip:"Use unit in label from PI tag or PI AF attribute",labelWidth:O},a().createElement(n.InlineSwitch,{value:s.enable,onChange:()=>this.onChange(w(I({},r),{useUnit:w(I({},s),{enable:!s.enable})}))})),this.props.datasource.useStreaming&&a().createElement(n.InlineField,{label:"Enable Streaming",labelWidth:O,tooltip:"Enable streaming data if it is supported for the point type."},a().createElement(n.InlineSwitch,{value:d.enable,onChange:()=>this.onChange(w(I({},r),{enableStreaming:w(I({},d),{enable:!d.enable})}))}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Calculation",labelWidth:O,tooltip:"Modify all attributes by an equation. Use '.' for current item. Leave Attributes empty if you wish to perform element based calculations."},a().createElement(n.Input,{onBlur:l,value:g,onChange:e=>t(w(I({},r),{expression:e.target.value})),placeholder:"'.'*2"}))),!i.enable&&a().createElement(a().Fragment,null,a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Max Recorded Values",labelWidth:O,tooltip:"Maximum number of recorded value to retrive from the data archive, without using interpolation."},a().createElement(n.Input,{onBlur:l,value:p.maxNumber,onChange:e=>t(w(I({},r),{recordedValues:w(I({},p),{maxNumber:parseInt(e.target.value,10)})})),type:"number",placeholder:"1000"})),a().createElement(n.InlineField,{label:"Recorded Values",labelWidth:O},a().createElement(n.InlineSwitch,{value:p.enable,onChange:()=>this.onChange(w(I({},r),{recordedValues:w(I({},p),{enable:!p.enable})}))})),a().createElement(n.InlineField,{label:"Digital States",labelWidth:O},a().createElement(n.InlineSwitch,{value:m.enable,onChange:()=>this.onChange(w(I({},r),{digitalStates:w(I({},m),{enable:!m.enable})}))}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:g?"Interval Period":"Interpolate Period",labelWidth:O,tooltip:"Override time between sampling, e.g. '30s'. Defaults to timespan/chart width."},a().createElement(n.Input,{onBlur:l,value:o.interval,onChange:e=>t(w(I({},r),{interpolate:w(I({},o),{interval:e.target.value})})),placeholder:"30s"})),a().createElement(n.InlineField,{label:g?"Interval Values":"Interpolate",labelWidth:O},a().createElement(n.InlineSwitch,{value:o.enable,onChange:()=>this.onChange(w(I({},r),{interpolate:w(I({},o),{enable:!o.enable})}))})),a().createElement(n.InlineField,{label:"Replace Bad Data",labelWidth:O,tooltip:"Replacement for bad quality values."},a().createElement(n.Segment,{Component:a().createElement(j,{value:{value:E.nodata},label:E.nodata}),onChange:this.calcNoDataValueChanged,options:this.getNoDataSegments(),allowCustomValue:!0}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Summary Period",labelWidth:O,tooltip:"Define the summary period, e.g. '30s'."},a().createElement(n.Input,{onBlur:l,value:E.interval,onChange:e=>t(w(I({},r),{summary:w(I({},E),{interval:e.target.value})})),placeholder:"30s"})),a().createElement(n.InlineField,{label:"Basis",labelWidth:O,tooltip:"Defines the possible calculation options when performing summary calculations over time-series data."},a().createElement(n.Segment,{Component:a().createElement(j,{value:{value:E.basis},label:E.basis}),onChange:this.calcBasisValueChanged,options:this.getCalcBasisSegments(),allowCustomValue:!0})),a().createElement(n.InlineField,{label:"Summaries",labelWidth:O,tooltip:"PI Web API summary options."},a().createElement(n.InlineFieldRow,null,this.state.summaries.map(((e,t)=>a().createElement(n.Segment,{key:"summaries-"+t,Component:a().createElement(j,{value:e.value,label:e.label}),onChange:e=>this.onSummaryValueChanged(e,t),options:this.getSummarySegments(),allowCustomValue:!0}))),a().createElement(n.Segment,{Component:a().createElement(j,{value:this.state.summarySegment.value,label:this.state.summarySegment.label}),onChange:this.onSummaryAction,options:this.getSummarySegments(),allowCustomValue:!0}))))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Display Name",labelWidth:O,tooltip:"If single attribute, modify display name. Otherwise use regex to modify display name."},a().createElement(n.Input,{onBlur:l,value:P,onChange:e=>t(w(I({},r),{display:e.target.value})),placeholder:"Display"})),a().createElement(n.InlineField,{label:"Enable Regex Replace",labelWidth:O},a().createElement(n.InlineSwitch,{value:D.enable,onChange:()=>{this.onChange(w(I({},r),{regex:w(I({},D),{enable:!D.enable})}))}})),a().createElement(n.InlineField,{label:"Search",labelWidth:16},a().createElement(n.Input,{onBlur:l,value:D.search,onChange:e=>t(w(I({},r),{regex:w(I({},D),{search:e.target.value})})),placeholder:"(.*)"})),a().createElement(n.InlineField,{label:"Replace",labelWidth:16},a().createElement(n.Input,{onBlur:l,value:D.replace,onChange:e=>t(w(I({},r),{regex:w(I({},D),{replace:e.target.value})})),placeholder:"$1"}))))}constructor(e){super(e),P(this,"error",void 0),P(this,"piServer",[]),P(this,"availableAttributes",{}),P(this,"summaryTypes",void 0),P(this,"calculationBasis",void 0),P(this,"noDataReplacement",void 0),P(this,"state",{isPiPoint:!1,segments:[],attributes:[],summaries:[],attributeSegment:{},summarySegment:{},calculationBasisSegment:{},noDataReplacementSegment:{}}),P(this,"segmentChangeValue",(e=>{const t=this.props.query;this.setState({segments:e},(()=>this.onChange(w(I({},t),{segments:e}))))})),P(this,"attributeChangeValue",(e=>{const t=this.props.query;this.setState({attributes:e},(()=>this.onChange(w(I({},t),{attributes:e}))))})),P(this,"onPiPointChange",((e,t)=>{let a=this.state.attributes.slice(0);e.label===D?(0,c.remove)(a,((e,a)=>a===t)):a[t]=e,this.checkPiPointSegments(e,a)})),P(this,"onAttributeChange",((e,t)=>{var a;let n=this.state.attributes.slice(0);n[t].label!==(null===(a=e.value)||void 0===a?void 0:a.value)&&(n[t]=e,this.checkAttributeSegments(n,this.state.segments))})),P(this,"onSegmentChange",((e,t)=>{var a;const{query:n}=this.props;let l=this.state.segments.slice(0);l[t].label!==(null===(a=e.value)||void 0===a?void 0:a.value)&&this.setState({attributes:[]},(()=>e.label===D?(l=(0,c.slice)(l,0,t),void this.checkAttributeSegments([],l).then((()=>{var e;0===l.length?l.push({label:""}):(null===(e=l[l.length-1].value)||void 0===e?void 0:e.expandable)&&l.push({label:"Select Element",value:{value:"-Select Element-"}}),n.isPiPoint&&(this.piServer=[]),this.segmentChangeValue(l)}))):(l[t]=e,n.isPiPoint?(this.piServer.push(e),void this.segmentChangeValue(l)):(t{var t;(null===(t=e.value)||void 0===t?void 0:t.expandable)&&l.push({label:"Select Element",value:{value:"-Select Element-"}}),this.segmentChangeValue(l)}))))))})),P(this,"getElementSegments",((e,t)=>{var a;const{datasource:n,query:l,data:r}=this.props,i=this,s=l.isPiPoint?{type:"dataserver"}:{path:this.getSegmentPathUpTo(null!=t?t:this.state.segments.slice(0),e),afServerWebId:this.state.segments.length>0&&this.state.segments[0].value?this.state.segments[0].value.webId:void 0};if(!l.isPiPoint){var o,u,h;if((null===(o=n.afserver)||void 0===o?void 0:o.name)&&0===e)return Promise.resolve([{label:n.afserver.name,value:{value:n.afserver.name,expandable:!0}}]);if((null===(u=n.afserver)||void 0===u?void 0:u.name)&&(null===(h=n.afdatabase)||void 0===h?void 0:h.name)&&1===e)return Promise.resolve([{label:n.afdatabase.name,value:{value:n.afdatabase.name,expandable:!0}}])}var m;return n.metricFindQuery(s,Object.assign(null!==(m=null==r||null===(a=r.request)||void 0===a?void 0:a.scopedVars)&&void 0!==m?m:{},{isPiPoint:l.isPiPoint})).then((e=>{const t=(0,c.map)(e,(e=>({label:e.text,value:{webId:e.WebId,value:e.text,expandable:!l.isPiPoint&&e.expandable}})));if(0===t.length)return t;const a=n.templateSrv.getVariables();return(0,c.each)(a,(e=>{let a={label:"${"+e.name+"}",value:{type:"template",value:"${"+e.name+"}",expandable:!l.isPiPoint}};t.unshift(a)})),t.unshift({label:D,value:{value:D}}),t})).catch((e=>(i.error=e.message||"Failed to issue metric query",[])))})),P(this,"getAttributeSegmentsPI",(e=>{var t;const{datasource:a,query:n,data:l}=this.props,r=this,i={path:"",webId:this.getSelectedPIServer(),pointName:(null!=e?e:"")+"*",type:"pipoint"};let s=[];var o;return s.push({label:D,value:{value:D}}),a.metricFindQuery(i,Object.assign(null!==(o=null==l||null===(t=l.request)||void 0===t?void 0:t.scopedVars)&&void 0!==o?o:{},{isPiPoint:n.isPiPoint})).then((t=>{s=(0,c.map)(t,(e=>({path:e.Path,label:e.text,value:{value:e.text,expandable:!1}}))),e&&e.length>0&&s.unshift({label:e,value:{value:e,expandable:!1}});const l=a.templateSrv.getVariables();return(0,c.each)(l,(e=>{let t={label:"${"+e.name+"}",value:{type:"template",value:"${"+e.name+"}",expandable:!n.isPiPoint}};s.unshift(t)})),s})).catch((e=>(r.error=e.message||"Failed to issue metric query",s)))})),P(this,"getAttributeSegmentsAF",(e=>{let t=[];return t.push({label:D,value:{value:D}}),(0,c.forOwn)(this.availableAttributes,((e,a)=>{let n={label:a,value:{value:a,expandable:!0}};t.push(n)})),t})),P(this,"buildFromTarget",((e,t,a)=>{const n=e.target.split(";"),l=n.length>0?n[0].split("\\"):[];return l.length>1||1===l.length&&""!==l[0]?(n.splice(0,1),(0,c.each)(l,((e,a)=>{t.push({label:e,value:{type:e.match(/\${\w+}/gi)?"template":void 0,value:e,expandable:!0}})})),(0,c.each)(n,((e,t)=>{""!==e&&a.push({label:e,value:{value:e,expandable:!1}})})),this.getElementSegments(l.length+1,t).then((e=>(e.length>0&&t.push({label:"Select Element",value:{value:"-Select Element-"}}),t)))):Promise.resolve(t)})),P(this,"checkAfServer",(()=>{var e;const{datasource:t}=this.props,a=[];var n;return(null===(e=t.afserver)||void 0===e?void 0:e.name)?(a.push({label:t.afserver.name,value:{value:t.afserver.name,expandable:!0}}),(null===(n=t.afdatabase)||void 0===n?void 0:n.name)&&a.push({label:t.afdatabase.name,value:{value:t.afdatabase.name,expandable:!0}}),a.push({label:"Select Element",value:{value:"-Select Element-"}})):a.push({label:""}),a})),P(this,"updateArray",((e,t,a,n,l)=>{this.setState({segments:e,attributes:t,summaries:a,isPiPoint:n},(()=>{n||this.checkAttributeSegments(t,this.state.segments).then((()=>{l&&l()}))}))})),P(this,"scopedVarsDone",!1),P(this,"componentDidMount",(()=>{this.initialLoad(!1)})),P(this,"componentDidUpdate",(()=>{var e,t,a;const{query:n}=this.props;"Done"===(null===(e=this.props.data)||void 0===e?void 0:e.state)&&(null===(a=this.props.data)||void 0===a||null===(t=a.request)||void 0===t?void 0:t.scopedVars)&&!this.scopedVarsDone&&(this.scopedVarsDone=!0,this.initialLoad(!n.isPiPoint))})),P(this,"initialLoad",(e=>{const{query:t}=this.props,a=(0,c.defaults)(t,S),{segments:n,attributes:l,summary:r,isPiPoint:i}=a;var s;let o=e?[]:null!==(s=null==n?void 0:n.slice(0))&&void 0!==s?s:[];var u;let h=e?[]:null!==(u=null==l?void 0:l.slice(0))&&void 0!==u?u:[];var m;let d=null!==(m=null==r?void 0:r.types)&&void 0!==m?m:[];if(i||0!==o.length)i&&o.length>0&&(this.piServer=o);else{if(t.target&&t.target.length>0&&";"!==t.target)return h=[],void this.buildFromTarget(t,o,h).then((e=>{this.updateArray(e,h,d,!1)})).catch((e=>console.error(e)));o=this.checkAfServer()}this.updateArray(o,h,d,!!i,(()=>{this.onChange(t)}))})),P(this,"onChange",(e=>{const{onChange:t,onRunQuery:a}=this.props;var n,l;e.summary.types=this.state.summaries,e.rawQuery?e.target=null!==(n=e.query)&&void 0!==n?n:"":(e.elementPath=this.getSegmentPathUpTo(this.state.segments,this.state.segments.length),e.target=e.elementPath+";"+(0,c.join)(null===(l=e.attributes)||void 0===l?void 0:l.map((e=>{var t;return null===(t=e.value)||void 0===t?void 0:t.value})),";")),t(e),e.target&&e.target.length>0&&a()})),P(this,"stateCallback",(()=>{const e=this.props.query;this.onChange(e)})),P(this,"onIsPiPointChange",(e=>{const{query:t}=this.props,a=!t.isPiPoint;this.setState({segments:a?[{label:""}]:this.checkAfServer(),attributes:[],isPiPoint:a},(()=>{this.onChange(w(I({},t),{expression:"",attributes:this.state.attributes,segments:this.state.segments,isPiPoint:a}))}))})),this.onSegmentChange=this.onSegmentChange.bind(this),this.calcBasisValueChanged=this.calcBasisValueChanged.bind(this),this.calcNoDataValueChanged=this.calcNoDataValueChanged.bind(this),this.onSummaryAction=this.onSummaryAction.bind(this),this.onSummaryValueChanged=this.onSummaryValueChanged.bind(this),this.onAttributeAction=this.onAttributeAction.bind(this),this.onAttributeChange=this.onAttributeChange.bind(this),this.summaryTypes=["Total","Average","Minimum","Maximum","Range","StdDev","PopulationStdDev","Count","PercentGood","All","AllForNonNumeric"],this.calculationBasis=["TimeWeighted","EventWeighted","TimeWeightedContinuous","TimeWeightedDiscrete","EventWeightedExcludeMostRecentEvent","EventWeightedExcludeEarliestEvent","EventWeightedIncludeBothEnds"],this.noDataReplacement=["Null","Drop","Previous","0","Keep"]}}var A=o(269),W=o(531);function V(e){return(0,c.map)(e,(e=>{var t,a;return{text:e.Name,expandable:void 0===e.HasChildren||!0===e.HasChildren||(null!==(t=e.Path)&&void 0!==t?t:"").split("\\").length<=3,HasChildren:e.HasChildren,Items:null!==(a=e.Items)&&void 0!==a?a:[],Path:e.Path,WebId:e.WebId}}))}function N(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function T(e){for(var t=1;t{const t=c.target;if(t&&t[e])return{label:t[e].Name,value:t[e]}};var S;return m.getAssetServer(m.afserver.name).then((e=>{v(e.WebId)})),a().createElement(a().Fragment,null,a().createElement("div",{className:"gf-form-group"},a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Database",labelWidth:B,grow:!0},a().createElement(n.AsyncSelect,{key:null!=g?g:"database-key",loadOptions:()=>m.getDatabases(g).then((e=>e.map((e=>({label:e.Name,value:e}))))),loadingMessage:"Loading",value:E("database"),onChange:e=>{y(e.value),d(k(T({},h),{database:e.value,template:void 0}))},defaultOptions:!0})),a().createElement(n.InlineField,{label:"Event Frames",labelWidth:B,grow:!0},a().createElement(n.AsyncSelect,{key:null!==(S=null==f?void 0:f.WebId)&&void 0!==S?S:"default-template-key",loadOptions:()=>m.getEventFrameTemplates(null==f?void 0:f.WebId).then((e=>e.map((e=>({label:e.Name,value:e}))))),loadingMessage:"Loading",value:E("template"),onChange:e=>d(k(T({},h),{template:e.value})),defaultOptions:!0})),a().createElement(n.InlineField,{label:"Show Start and End Time",labelWidth:B,grow:!0},a().createElement(n.InlineSwitch,{value:!!h.showEndTime,onChange:e=>d(k(T({},h),{showEndTime:e.currentTarget.checked}))}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Category name",labelWidth:B,grow:!0},a().createElement(n.Input,{type:"text",value:h.categoryName,onBlur:e=>p(),onChange:e=>d(k(T({},h),{categoryName:e.currentTarget.value})),placeholder:"Enter category name"})),a().createElement(n.InlineField,{label:"Name Filter",labelWidth:B,grow:!0},a().createElement(n.Input,{type:"text",value:h.nameFilter,onBlur:e=>p(),onChange:e=>d(k(T({},h),{nameFilter:e.currentTarget.value})),placeholder:"Enter name filter"}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Enable Name Regex Replacement",labelWidth:B,grow:!1},a().createElement(n.InlineSwitch,{value:null===(r=h.regex)||void 0===r?void 0:r.enable,onChange:e=>d(k(T({},h),{regex:k(T({},h.regex),{enable:e.currentTarget.checked})}))})),a().createElement(n.InlineField,{label:"Name Filter",labelWidth:20,grow:!1},a().createElement(n.Input,{type:"text",value:null===(i=h.regex)||void 0===i?void 0:i.search,onBlur:e=>p(),onChange:e=>d(k(T({},h),{regex:k(T({},h.regex),{search:e.currentTarget.value})})),placeholder:"(.*)",width:50})),a().createElement(n.InlineField,{label:"Replace",labelWidth:20,grow:!0},a().createElement(n.Input,{type:"text",value:null==h||null===(s=h.regex)||void 0===s?void 0:s.replace,onBlur:e=>p(),onChange:e=>d(k(T({},h),{regex:k(T({},h.regex),{replace:e.currentTarget.value})})),placeholder:"$1"}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Enable Attribute Usage",labelWidth:B,grow:!1},a().createElement(n.InlineSwitch,{value:null===(o=h.attribute)||void 0===o?void 0:o.enable,onChange:e=>d(k(T({},h),{attribute:k(T({},h.attribute),{enable:e.currentTarget.checked})}))})),a().createElement(n.InlineField,{label:"Attribute Name",labelWidth:B,grow:!0},a().createElement(n.Input,{type:"text",value:null===(u=h.attribute)||void 0===u?void 0:u.name,onBlur:e=>p(),onChange:e=>d(k(T({},h),{attribute:k(T({},h.attribute),{name:e.currentTarget.value})})),placeholder:"Enter name"})))))}));function R(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}class U extends W.DataSourceWithBackend{applyTemplateVariables(e,t){return a=function(e){for(var t=1;te.substring(1,e.length-2).split(",")[0]))),e.filter=null!==(l=e.filter)&&void 0!==l?l:"*","servers"===e.type?(null===(r=a.afserver)||void 0===r?void 0:r.name)?a.getAssetServer(a.afserver.name).then((e=>[e])).then(V):a.getAssetServers().then(V):"databases"===e.type&&e.afServerWebId?a.getDatabases(e.afServerWebId,{}).then(V):"databases"===e.type?a.getAssetServer(e.path).then((e=>{var t;return a.getDatabases(null!==(t=e.WebId)&&void 0!==t?t:"",{})})).then(V):"databaseElements"===e.type?a.getDatabase(e.path).then((e=>{var t;return a.getDatabaseElements(null!==(t=e.WebId)&&void 0!==t?t:"",{selectedFields:"Items.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren"})})).then(V):"elements"===e.type?a.getElement(e.path).then((t=>{var n;return a.getElements(null!==(n=t.WebId)&&void 0!==n?n:"",{selectedFields:"Items.Description%3BItems.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren",nameFilter:e.filter})})).then(V):"attributes"===e.type?a.getElement(e.path).then((t=>{var n;return a.getAttributes(null!==(n=t.WebId)&&void 0!==n?n:"",{searchFullHierarchy:"true",selectedFields:"Items.Type%3BItems.DefaultUnitsName%3BItems.Description%3BItems.WebId%3BItems.Name%3BItems.Path",nameFilter:e.filter})})).then(V):"dataserver"===e.type?a.getDataServers().then(V):"pipoint"===e.type?a.piPointSearch(e.webId,e.pointName).then(V):Promise.reject("Bad type")}eventFrameToAnnotation(e,t){const a=e.target,n=[],l=Intl.DateTimeFormat().resolvedOptions().locale;return t.forEach((e=>{let t=this.transformDataFrameToMap(e);for(let e=0;eStart: "+new Date(t.time[e]).toLocaleString(l)+"
End: ",t.timeEnd[e]?i+=new Date(t.timeEnd[e]).toLocaleString(l):i+="Eventframe is open";const s={time:t.time[e],timeEnd:a.showEndTime?t.timeEnd[e]:void 0,title:r,id:t.id[e],text:i,tags:["OSISoft PI"]};n.push(s)}})),n}transformDataFrameToMap(e){const t={};return e.fields.forEach((e=>{t[e.name]=e.values.toArray()})),t}restGet(e){const t=this.backendSrv.fetch({url:`/api/datasources/${this.id}/resources/${e}`,method:"GET",headers:{"Content-Type":"application/json"}});return(0,A.firstValueFrom)(t).then((e=>e))}getDataServers(){return this.restGet("/dataservers").then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getDataServer(e){return e?this.restGet("/dataservers?name="+e).then((e=>e.data)):Promise.resolve({})}getAssetServers(){return this.restGet("/assetservers").then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getAssetServer(e){return e?this.restGet("/assetservers?path=\\\\"+e).then((e=>e.data)):Promise.resolve({})}getDatabase(e){return e?this.restGet("/assetdatabases?path=\\\\"+e).then((e=>e.data)):Promise.resolve({})}getDatabases(e,t){return e?this.restGet("/assetservers/"+e+"/assetdatabases").then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]})):Promise.resolve([])}getElement(e){return e?this.restGet("/elements?path=\\\\"+e).then((e=>e.data)):Promise.resolve({})}getEventFrameTemplates(e){return e?this.restGet("/assetdatabases/"+e+"/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId").then((e=>{var t;return(0,c.filter)(null!==(t=e.data.Items)&&void 0!==t?t:[],(e=>"EventFrame"===e.InstanceType))})):Promise.resolve([])}getElementTemplates(e){return e?this.restGet("/assetdatabases/"+e+"/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId").then((e=>{var t;return(0,c.filter)(null!==(t=e.data.Items)&&void 0!==t?t:[],(e=>"Element"===e.InstanceType))})):Promise.resolve([])}getAttributes(e,t){let a="?"+(0,c.map)(t,((e,t)=>t+"="+e)).join("&");return"?"===a&&(a=""),this.restGet("/elements/"+e+"/attributes"+a).then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getDatabaseElements(e,t){let a="?"+(0,c.map)(t,((e,t)=>t+"="+e)).join("&");return"?"===a&&(a=""),this.restGet("/assetdatabases/"+e+"/elements"+a).then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getElements(e,t){let a="?"+(0,c.map)(t,((e,t)=>t+"="+e)).join("&");return"?"===a&&(a=""),this.restGet("/elements/"+e+"/elements"+a).then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}piPointSearch(e,t){let a=this.templateSrv.replace(t),n=`${a}`,l=!1;if(a!==t){const e=RegExp("\\{(\\w|,)+\\}","gs");let t;for(;null!==(t=e.exec(a));)t.index===e.lastIndex&&e.lastIndex++,t.forEach(((e,t)=>{0===t&&(a=a.replace(e,e.replace("{","(").replace("}",")").replace(",","|")),n=n.replace(e,"*"),l=!0)}))}return this.restGet("/dataservers/"+e+"/points?maxCount=50&nameFilter="+n).then((e=>{var t;return e&&(null===(t=e.data)||void 0===t?void 0:t.Items)?l?e.data.Items.filter((e=>{var t;return null===(t=e.Name)||void 0===t?void 0:t.match(a)})):e.data.Items:[]}))}constructor(e,t=(0,W.getTemplateSrv)(),a=(0,W.getBackendSrv)()){super(e),R(this,"templateSrv",void 0),R(this,"backendSrv",void 0),R(this,"piserver",void 0),R(this,"afserver",void 0),R(this,"afdatabase",void 0),R(this,"piPointConfig",void 0),R(this,"newFormatConfig",void 0),R(this,"useUnitConfig",void 0),R(this,"useExperimental",void 0),R(this,"useStreaming",void 0),this.templateSrv=t,this.backendSrv=a,this.piserver={name:(e.jsonData||{}).piserver,webid:void 0},this.afserver={name:(e.jsonData||{}).afserver,webid:void 0},this.afdatabase={name:(e.jsonData||{}).afdatabase,webid:void 0},this.piPointConfig=e.jsonData.pipoint||!1,this.newFormatConfig=e.jsonData.newFormat||!1,this.useUnitConfig=e.jsonData.useUnit||!1,this.useExperimental=e.jsonData.useExperimental||!1,this.useStreaming=e.jsonData.useStreaming||!1,this.annotations={QueryEditor:q,prepareQuery:e=>(e.target&&(e.target.queryType="Annotation",e.target.isAnnotation=!0),e.target),processEvents:(e,t)=>(0,A.of)(this.eventFrameToAnnotation(e,t))},Promise.all([this.getDataServer(this.piserver.name).then((e=>this.piserver.webid=e.WebId)),this.getAssetServer(this.afserver.name).then((e=>this.afserver.webid=e.WebId)),this.getDatabase(this.afserver.name&&this.afdatabase.name?this.afserver.name+"\\"+this.afdatabase.name:void 0).then((e=>this.afdatabase.webid=e.WebId))])}}const M=new e.DataSourcePlugin(U).setQueryEditor(x).setConfigEditor(m)})(),u})())); +define(["@grafana/data","react","@grafana/ui","lodash","rxjs","@grafana/runtime"],((e,t,a,n,l,r)=>(()=>{"use strict";var i={781:t=>{t.exports=e},531:e=>{e.exports=r},7:e=>{e.exports=a},241:e=>{e.exports=n},959:e=>{e.exports=t},269:e=>{e.exports=l}},s={};function o(e){var t=s[e];if(void 0!==t)return t.exports;var a=s[e]={exports:{}};return i[e](a,a.exports,o),a.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var a in t)o.o(t,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var u={};return(()=>{o.r(u),o.d(u,{plugin:()=>M});var e=o(781),t=o(959),a=o.n(t),n=o(7);function l(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e){for(var t=1;ti(r({},e),{jsonData:i(r({},e.jsonData),{url:e.url})});class m extends t.PureComponent{render(){const{options:e}=this.props,t=h(e);return a().createElement("div",null,a().createElement(n.DataSourceHttpSettings,{defaultUrl:"https://server.name/piwebapi",dataSourceConfig:t,onChange:this.onHttpOptionsChange,showAccessOptions:!0}),a().createElement("h3",{className:"page-heading"},"Custom Configuration"),a().createElement("div",{className:"gf-form-group"},a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable PI Points in Query",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.pipoint,onChange:this.onPiPointChange}))),a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable New Data Format",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.newFormat,onChange:this.onNewFormatChange}))),a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable Unit in Data",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.useUnit,onChange:this.onUseUnitChange}))),a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable Experimental Features",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.useExperimental,onChange:this.onUseExperimentalChange}))),t.jsonData.useExperimental&&a().createElement("div",{className:"gf-form-inline"},a().createElement(n.InlineField,{label:"Enable Steaming Support",labelWidth:24},a().createElement(n.InlineSwitch,{value:t.jsonData.useStreaming,onChange:this.onUseStreamingChange})))),a().createElement("h3",{className:"page-heading"},"PI/AF Connection Details"),a().createElement("div",{className:"gf-form-group"},t.jsonData.pipoint&&a().createElement("div",{className:"gf-form"},a().createElement(s,{label:"PI Server",labelWidth:10,inputWidth:25,onChange:this.onPIServerChange,value:t.jsonData.piserver||"",placeholder:"Default PI Server to use for data requests"})),a().createElement("div",{className:"gf-form"},a().createElement(s,{label:"AF Server",labelWidth:10,inputWidth:25,onChange:this.onAFServerChange,value:t.jsonData.afserver||"",placeholder:"Default AF Server to use for data requests"})),a().createElement("div",{className:"gf-form"},a().createElement(s,{label:"AF Database",labelWidth:10,inputWidth:25,onChange:this.onAFDatabaseChange,value:t.jsonData.afdatabase||"",placeholder:"Default AF Database server for AF queries"}))))}constructor(...e){super(...e),l(this,"onPIServerChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{piserver:e.target.value});t(i(r({},a),{jsonData:n}))})),l(this,"onAFServerChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{afserver:e.target.value});t(i(r({},a),{jsonData:n}))})),l(this,"onAFDatabaseChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{afdatabase:e.target.value});t(i(r({},a),{jsonData:n}))})),l(this,"onHttpOptionsChange",(e=>{const{onOptionsChange:t}=this.props;t(h(e))})),l(this,"onPiPointChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{piserver:e.target.checked?a.jsonData.piserver:"",pipoint:e.target.checked});t(i(r({},a),{jsonData:n}))})),l(this,"onNewFormatChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{newFormat:e.target.checked});t(i(r({},a),{jsonData:n}))})),l(this,"onUseUnitChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{useUnit:e.target.checked});t(i(r({},a),{jsonData:n}))})),l(this,"onUseExperimentalChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{useExperimental:e.target.checked,useStreaming:!!e.target.checked&&a.jsonData.useStreaming});t(i(r({},a),{jsonData:n}))})),l(this,"onUseStreamingChange",(e=>{const{onOptionsChange:t,options:a}=this.props,n=i(r({},a.jsonData),{useStreaming:e.target.checked});t(i(r({},a),{jsonData:n}))}))}}var c=o(241);function d(){return d=Object.assign||function(e){for(var t=1;ta().createElement(a().Fragment,null,a().createElement(n.InlineFormLabel,{width:t,tooltip:l},e),r),v=()=>a().createElement("div",{className:"gf-form gf-form--grow"},a().createElement("div",{className:"gf-form-label gf-form-label--grow"})),b=e=>{var t=d({},p(e));return a().createElement(y,null,a().createElement(g,t))},y=e=>a().createElement("div",{className:"gf-form-inline"},e.children,a().createElement(v,null)),f=e=>{var t=d({},p(e));return a().createElement(E,null,a().createElement(g,t))},E=e=>a().createElement(a().Fragment,null,e.children),S={target:";",attributes:[],segments:[],regex:{enable:!1},summary:{types:[],basis:"EventWeighted",interval:"",nodata:"Null"},expression:"",interpolate:{enable:!1},useLastValue:{enable:!1},recordedValues:{enable:!1},digitalStates:{enable:!1},enableStreaming:{enable:!1},useUnit:{enable:!1},isPiPoint:!1},P=({isRaw:e,onChange:l})=>{const[r,i]=(0,t.useState)(!1);return(0,t.useEffect)((()=>{i(!1)}),[e]),e?a().createElement(a().Fragment,null,a().createElement(n.Button,{"aria-label":"Switch to visual editor",icon:"pen",variant:"secondary",type:"button",onClick:()=>{i(!0)}}),a().createElement(n.ConfirmModal,{isOpen:r,title:"Switch to visual editor mode",body:"Are you sure to switch to visual editor mode? You will lose the changes done in raw query mode.",confirmText:"Yes, switch to editor mode",dismissText:"No, stay in raw query mode",onConfirm:()=>{l(!1)},onDismiss:()=>{i(!1)}})):a().createElement(n.Button,{"aria-label":"Switch to text editor",icon:"pen",variant:"secondary",type:"button",onClick:()=>{l(!0)}})};function C(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function I(e){for(var t=1;t{var t;return e.value?a().createElement("div",{className:"gf-form-label "+("template"===e.value.type?"query-keyword":"")},null!==(t=e.label)&&void 0!==t?t:"--no label--"):a().createElement("a",{className:"gf-form-label query-part"},a().createElement(n.Icon,{name:"plus"}))};class j extends t.PureComponent{isValueEmpty(e){return!e||!e.value||!e.value.length||e.value===D}calcBasisValueChanged(e){var t;const a=this.props.query,n=a.summary;n.basis=null===(t=e.value)||void 0===t?void 0:t.value,this.onChange(w(I({},a),{summary:n}))}getCalcBasisSegments(){return(0,c.map)(this.calculationBasis,(e=>({label:e,value:{value:e,expandable:!0}})))}calcNoDataValueChanged(e){var t;const a=this.props.query,n=a.summary;n.nodata=null===(t=e.value)||void 0===t?void 0:t.value,this.onChange(w(I({},a),{summary:n}))}getNoDataSegments(){return(0,c.map)(this.noDataReplacement,(e=>({label:e,value:{value:e,expandable:!0}})))}onSummaryValueChanged(e,t){const a=this.state.summaries.slice(0);a[t]=e,this.isValueEmpty(e.value)&&a.splice(t,1),this.setState({summaries:a},this.stateCallback)}getSummarySegments(){const e=(0,c.filter)(this.summaryTypes,(e=>-1===this.state.summaries.map((e=>{var t;return null===(t=e.value)||void 0===t?void 0:t.value})).indexOf(e))),t=(0,c.map)(e,(e=>({label:e,value:{value:e,expandable:!0}})));return t.unshift({label:D,value:{value:D}}),t}removeSummary(e){const t=(0,c.filter)(this.state.summaries,(t=>t!==e));this.setState({summaries:t})}onSummaryAction(e){const t=this.state.summaries.slice(0);if(!this.isValueEmpty(e.value)){var a;let n={label:e.label,value:{value:null===(a=e.value)||void 0===a?void 0:a.value,expandable:!0}};t.push(n)}this.setState({summarySegment:{},summaries:t},this.stateCallback)}removeAttribute(e){const t=(0,c.filter)(this.state.attributes,(t=>t!==e));this.attributeChangeValue(t)}onAttributeAction(e){const{query:t}=this.props,a=this.state.attributes.slice(0);if(!this.isValueEmpty(e.value)){var n;let l={label:e.label,value:{value:null===(n=e.value)||void 0===n?void 0:n.value,expandable:!t.isPiPoint}};a.push(l)}this.attributeChangeValue(a)}getSegmentPathUpTo(e,t){const a=e.slice(0,t);return(0,c.reduce)(a,((e,t)=>{var a;return t.value?(null===(a=t.value.value)||void 0===a?void 0:a.startsWith("-Select"))?e:e?e+"\\"+t.value.value:t.value.value:""}),"")}checkAttributeSegments(e,t){var a;const{datasource:n,data:l}=this.props,r=this,i={path:this.getSegmentPathUpTo(t.slice(0),t.length),type:"attributes"};var s;return n.metricFindQuery(i,Object.assign(null!==(s=null==l||null===(a=l.request)||void 0===a?void 0:a.scopedVars)&&void 0!==s?s:{},{isPiPoint:!1})).then((t=>{const a={};(0,c.each)(t,(e=>{a[e.Path.substring(e.Path.indexOf("|")+1)]=e.WebId}));const l=(0,c.filter)(e,(e=>{var t;const l=n.templateSrv.replace(null===(t=e.value)||void 0===t?void 0:t.value);return void 0!==a[l]}));r.availableAttributes=a,this.attributeChangeValue(l)})).catch((t=>{r.error=t.message||"Failed to issue metric query",this.attributeChangeValue(e)}))}checkPiPointSegments(e,t){var a;const{datasource:n,data:l}=this.props,r=this,i={path:e.path,webId:r.getSelectedPIServer(),pointName:e.label,type:"pipoint"};var s;return n.metricFindQuery(i,Object.assign(null!==(s=null==l||null===(a=l.request)||void 0===a?void 0:a.scopedVars)&&void 0!==s?s:{},{isPiPoint:!0})).then((()=>{r.attributeChangeValue(t)})).catch((e=>{r.error=e.message||"Failed to issue metric query",r.attributeChangeValue([])}))}getSelectedPIServer(){var e;let t="";return this.piServer.forEach((e=>{const a=this.props.query.target.split(";");a.length>=2&&a[0]===e.text&&(t=e.WebId)})),this.piServer.length>0?null===(e=this.piServer[0].value)||void 0===e?void 0:e.webId:t}textEditorChanged(){const{query:e,onChange:t}=this.props,a=e.target.split(";"),n=a.length>0?a[0].split("\\"):[];let l=[],r=[];n.length>1||1===n.length&&""!==n[0]?(a.splice(0,1),(0,c.each)(n,((e,t)=>{l.push({label:e,value:{type:e.match(/\${\w+}/gi)?"template":void 0,value:e,expandable:!0}})})),(0,c.each)(a,(function(e,t){""!==e&&r.push({label:e,value:{value:e,expandable:!1}})})),this.getElementSegments(n.length+1,l).then((e=>{e.length>0&&l.push({label:"Select Element",value:{value:"-Select Element-"}})})).then((()=>{this.updateArray(l,r,this.state.summaries,e.isPiPoint,(()=>{t(w(I({},e),{query:void 0,rawQuery:!1}))}))}))):(l=this.checkAfServer(),this.updateArray(l,this.state.attributes,this.state.summaries,e.isPiPoint,(()=>{this.onChange(w(I({},e),{query:void 0,rawQuery:!1,attributes:this.state.attributes,segments:this.state.segments}))})))}render(){const{query:e,onChange:t,onRunQuery:l}=this.props,r=(0,c.defaults)(e,S),{useLastValue:i,useUnit:s,interpolate:o,query:u,rawQuery:h,digitalStates:m,enableStreaming:d,recordedValues:p,expression:g,isPiPoint:y,summary:E,display:C,regex:D}=r;return a().createElement(a().Fragment,null,this.props.datasource.piPointConfig&&a().createElement(n.InlineField,{label:"Is Pi Point?",labelWidth:O},a().createElement(n.InlineSwitch,{value:y,onChange:this.onIsPiPointChange})),!!h&&a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Raw Query",labelWidth:O,grow:!0},a().createElement(n.Input,{onBlur:this.stateCallback,value:u,onChange:e=>t(w(I({},r),{query:e.target.value})),placeholder:"enter query"})),a().createElement(P,{isRaw:!0,onChange:e=>this.textEditorChanged()})),!h&&a().createElement(a().Fragment,null,a().createElement("div",{className:"gf-form-inline"},a().createElement(f,{label:y?"PI Server":"AF Elements",tooltip:y?"Select PI server.":"Select AF Element."},this.state.segments.map(((e,t)=>a().createElement(n.SegmentAsync,{key:"element-"+t,Component:a().createElement(x,{value:e.value,label:e.label}),onChange:e=>this.onSegmentChange(e,t),loadOptions:e=>this.getElementSegments(t),allowCustomValue:!0,inputMinWidth:200}))),a().createElement(v,null),!y&&a().createElement(P,{isRaw:!1,onChange:e=>{t(w(I({},r),{query:r.target,rawQuery:e}))}}))),a().createElement(b,{label:y?"Pi Points":"Attributes"},this.state.attributes.map(((e,t)=>y?a().createElement(n.SegmentAsync,{key:"attributes-"+t,Component:a().createElement(x,{value:e.value,label:e.label}),disabled:0===this.piServer.length,onChange:e=>this.onPiPointChange(e,t),loadOptions:this.getAttributeSegmentsPI,reloadOptionsOnChange:!0,allowCustomValue:!0,inputMinWidth:F}):a().createElement(n.Segment,{key:"attributes-"+t,Component:a().createElement(x,{value:e.value,label:e.label}),disabled:this.state.segments.length<=2,onChange:e=>this.onAttributeChange(e,t),options:this.getAttributeSegmentsAF(),allowCustomValue:!0,inputMinWidth:F}))),y&&a().createElement(n.SegmentAsync,{Component:a().createElement(x,{value:this.state.attributeSegment.value,label:this.state.attributeSegment.label}),disabled:0===this.piServer.length,onChange:this.onAttributeAction,loadOptions:this.getAttributeSegmentsPI,reloadOptionsOnChange:!0,allowCustomValue:!0,inputMinWidth:F}),!y&&a().createElement(n.Segment,{Component:a().createElement(x,{value:this.state.attributeSegment.value,label:this.state.attributeSegment.label}),disabled:this.state.segments.length<=2,onChange:this.onAttributeAction,options:this.getAttributeSegmentsAF(),allowCustomValue:!0,inputMinWidth:F}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Use Last Value",tooltip:"Fetch only last value from time range",labelWidth:O},a().createElement(n.InlineSwitch,{value:i.enable,onChange:()=>this.onChange(w(I({},r),{useLastValue:w(I({},i),{enable:!i.enable})}))})),this.props.datasource.useUnitConfig&&a().createElement(n.InlineField,{label:"Use unit from datapoints",tooltip:"Use unit in label from PI tag or PI AF attribute",labelWidth:O},a().createElement(n.InlineSwitch,{value:s.enable,onChange:()=>this.onChange(w(I({},r),{useUnit:w(I({},s),{enable:!s.enable})}))})),this.props.datasource.useStreaming&&a().createElement(n.InlineField,{label:"Enable Streaming",labelWidth:O,tooltip:"Enable streaming data if it is supported for the point type."},a().createElement(n.InlineSwitch,{value:d.enable,onChange:()=>this.onChange(w(I({},r),{enableStreaming:w(I({},d),{enable:!d.enable})}))}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Calculation",labelWidth:O,tooltip:"Modify all attributes by an equation. Use '.' for current item. Leave Attributes empty if you wish to perform element based calculations."},a().createElement(n.Input,{onBlur:l,value:g,onChange:e=>t(w(I({},r),{expression:e.target.value})),placeholder:"'.'*2"}))),!i.enable&&a().createElement(a().Fragment,null,a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Max Recorded Values",labelWidth:O,tooltip:"Maximum number of recorded value to retrive from the data archive, without using interpolation."},a().createElement(n.Input,{onBlur:l,value:p.maxNumber,onChange:e=>t(w(I({},r),{recordedValues:w(I({},p),{maxNumber:parseInt(e.target.value,10)})})),type:"number",placeholder:"1000"})),a().createElement(n.InlineField,{label:"Recorded Values",labelWidth:O},a().createElement(n.InlineSwitch,{value:p.enable,onChange:()=>this.onChange(w(I({},r),{recordedValues:w(I({},p),{enable:!p.enable})}))})),a().createElement(n.InlineField,{label:"Digital States",labelWidth:O},a().createElement(n.InlineSwitch,{value:m.enable,onChange:()=>this.onChange(w(I({},r),{digitalStates:w(I({},m),{enable:!m.enable})}))}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:g?"Interval Period":"Interpolate Period",labelWidth:O,tooltip:"Override time between sampling, e.g. '30s'. Defaults to timespan/chart width."},a().createElement(n.Input,{onBlur:l,value:o.interval,onChange:e=>t(w(I({},r),{interpolate:w(I({},o),{interval:e.target.value})})),placeholder:"30s"})),a().createElement(n.InlineField,{label:g?"Interval Values":"Interpolate",labelWidth:O},a().createElement(n.InlineSwitch,{value:o.enable,onChange:()=>this.onChange(w(I({},r),{interpolate:w(I({},o),{enable:!o.enable})}))})),a().createElement(n.InlineField,{label:"Replace Bad Data",labelWidth:O,tooltip:"Replacement for bad quality values."},a().createElement(n.Segment,{Component:a().createElement(x,{value:{value:E.nodata},label:E.nodata}),onChange:this.calcNoDataValueChanged,options:this.getNoDataSegments(),allowCustomValue:!0}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Summary Period",labelWidth:O,tooltip:"Define the summary period, e.g. '30s'."},a().createElement(n.Input,{onBlur:l,value:E.interval,onChange:e=>t(w(I({},r),{summary:w(I({},E),{interval:e.target.value})})),placeholder:"30s"})),a().createElement(n.InlineField,{label:"Basis",labelWidth:O,tooltip:"Defines the possible calculation options when performing summary calculations over time-series data."},a().createElement(n.Segment,{Component:a().createElement(x,{value:{value:E.basis},label:E.basis}),onChange:this.calcBasisValueChanged,options:this.getCalcBasisSegments(),allowCustomValue:!0})),a().createElement(n.InlineField,{label:"Summaries",labelWidth:O,tooltip:"PI Web API summary options."},a().createElement(n.InlineFieldRow,null,this.state.summaries.map(((e,t)=>a().createElement(n.Segment,{key:"summaries-"+t,Component:a().createElement(x,{value:e.value,label:e.label}),onChange:e=>this.onSummaryValueChanged(e,t),options:this.getSummarySegments(),allowCustomValue:!0}))),a().createElement(n.Segment,{Component:a().createElement(x,{value:this.state.summarySegment.value,label:this.state.summarySegment.label}),onChange:this.onSummaryAction,options:this.getSummarySegments(),allowCustomValue:!0}))))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Display Name",labelWidth:O,tooltip:"If single attribute, modify display name. Otherwise use regex to modify display name."},a().createElement(n.Input,{onBlur:l,value:C,onChange:e=>t(w(I({},r),{display:e.target.value})),placeholder:"Display"})),a().createElement(n.InlineField,{label:"Enable Regex Replace",labelWidth:O},a().createElement(n.InlineSwitch,{value:D.enable,onChange:()=>{this.onChange(w(I({},r),{regex:w(I({},D),{enable:!D.enable})}))}})),a().createElement(n.InlineField,{label:"Search",labelWidth:16},a().createElement(n.Input,{onBlur:l,value:D.search,onChange:e=>t(w(I({},r),{regex:w(I({},D),{search:e.target.value})})),placeholder:"(.*)"})),a().createElement(n.InlineField,{label:"Replace",labelWidth:16},a().createElement(n.Input,{onBlur:l,value:D.replace,onChange:e=>t(w(I({},r),{regex:w(I({},D),{replace:e.target.value})})),placeholder:"$1"}))))}constructor(e){super(e),C(this,"error",void 0),C(this,"piServer",[]),C(this,"availableAttributes",{}),C(this,"summaryTypes",void 0),C(this,"calculationBasis",void 0),C(this,"noDataReplacement",void 0),C(this,"state",{isPiPoint:!1,segments:[],attributes:[],summaries:[],attributeSegment:{},summarySegment:{},calculationBasisSegment:{},noDataReplacementSegment:{}}),C(this,"segmentChangeValue",(e=>{const t=this.props.query;this.setState({segments:e},(()=>this.onChange(w(I({},t),{segments:e}))))})),C(this,"attributeChangeValue",(e=>{const t=this.props.query;this.setState({attributes:e},(()=>this.onChange(w(I({},t),{attributes:e}))))})),C(this,"onPiPointChange",((e,t)=>{let a=this.state.attributes.slice(0);e.label===D?(0,c.remove)(a,((e,a)=>a===t)):a[t]=e,this.checkPiPointSegments(e,a)})),C(this,"onAttributeChange",((e,t)=>{var a;let n=this.state.attributes.slice(0);n[t].label!==(null===(a=e.value)||void 0===a?void 0:a.value)&&(n[t]=e,this.checkAttributeSegments(n,this.state.segments))})),C(this,"onSegmentChange",((e,t)=>{var a;const{query:n}=this.props;let l=this.state.segments.slice(0);l[t].label!==(null===(a=e.value)||void 0===a?void 0:a.value)&&this.setState({attributes:[]},(()=>e.label===D?(l=(0,c.slice)(l,0,t),void this.checkAttributeSegments([],l).then((()=>{var e;0===l.length?l.push({label:""}):(null===(e=l[l.length-1].value)||void 0===e?void 0:e.expandable)&&l.push({label:"Select Element",value:{value:"-Select Element-"}}),n.isPiPoint&&(this.piServer=[]),this.segmentChangeValue(l)}))):(l[t]=e,n.isPiPoint?(this.piServer.push(e),void this.segmentChangeValue(l)):(t{var t;(null===(t=e.value)||void 0===t?void 0:t.expandable)&&l.push({label:"Select Element",value:{value:"-Select Element-"}}),this.segmentChangeValue(l)}))))))})),C(this,"getElementSegments",((e,t)=>{var a;const{datasource:n,query:l,data:r}=this.props,i=this,s=l.isPiPoint?{type:"dataserver"}:{path:this.getSegmentPathUpTo(null!=t?t:this.state.segments.slice(0),e),afServerWebId:this.state.segments.length>0&&this.state.segments[0].value?this.state.segments[0].value.webId:void 0};if(!l.isPiPoint){var o,u,h;if((null===(o=n.afserver)||void 0===o?void 0:o.name)&&0===e)return Promise.resolve([{label:n.afserver.name,value:{value:n.afserver.name,expandable:!0}}]);if((null===(u=n.afserver)||void 0===u?void 0:u.name)&&(null===(h=n.afdatabase)||void 0===h?void 0:h.name)&&1===e)return Promise.resolve([{label:n.afdatabase.name,value:{value:n.afdatabase.name,expandable:!0}}])}var m;return n.metricFindQuery(s,Object.assign(null!==(m=null==r||null===(a=r.request)||void 0===a?void 0:a.scopedVars)&&void 0!==m?m:{},{isPiPoint:l.isPiPoint})).then((e=>{const t=(0,c.map)(e,(e=>({label:e.text,value:{webId:e.WebId,value:e.text,expandable:!l.isPiPoint&&e.expandable}})));if(0===t.length)return t;const a=n.templateSrv.getVariables();return(0,c.each)(a,(e=>{let a={label:"${"+e.name+"}",value:{type:"template",value:"${"+e.name+"}",expandable:!l.isPiPoint}};t.unshift(a)})),t.unshift({label:D,value:{value:D}}),t})).catch((e=>(i.error=e.message||"Failed to issue metric query",[])))})),C(this,"getAttributeSegmentsPI",(e=>{var t;const{datasource:a,query:n,data:l}=this.props,r=this,i={path:"",webId:this.getSelectedPIServer(),pointName:(null!=e?e:"")+"*",type:"pipoint"};let s=[];var o;return s.push({label:D,value:{value:D}}),a.metricFindQuery(i,Object.assign(null!==(o=null==l||null===(t=l.request)||void 0===t?void 0:t.scopedVars)&&void 0!==o?o:{},{isPiPoint:n.isPiPoint})).then((t=>{s=(0,c.map)(t,(e=>({path:e.Path,label:e.text,value:{value:e.text,expandable:!1}}))),e&&e.length>0&&s.unshift({label:e,value:{value:e,expandable:!1}});const l=a.templateSrv.getVariables();return(0,c.each)(l,(e=>{let t={label:"${"+e.name+"}",value:{type:"template",value:"${"+e.name+"}",expandable:!n.isPiPoint}};s.unshift(t)})),s})).catch((e=>(r.error=e.message||"Failed to issue metric query",s)))})),C(this,"getAttributeSegmentsAF",(e=>{let t=[];return t.push({label:D,value:{value:D}}),(0,c.forOwn)(this.availableAttributes,((e,a)=>{let n={label:a,value:{value:a,expandable:!0}};t.push(n)})),t})),C(this,"buildFromTarget",((e,t,a)=>{const n=e.target.split(";"),l=n.length>0?n[0].split("\\"):[];return l.length>1||1===l.length&&""!==l[0]?(n.splice(0,1),(0,c.each)(l,((e,a)=>{t.push({label:e,value:{type:e.match(/\${\w+}/gi)?"template":void 0,value:e,expandable:!0}})})),(0,c.each)(n,((e,t)=>{""!==e&&a.push({label:e,value:{value:e,expandable:!1}})})),this.getElementSegments(l.length+1,t).then((e=>(e.length>0&&t.push({label:"Select Element",value:{value:"-Select Element-"}}),t)))):Promise.resolve(t)})),C(this,"checkAfServer",(()=>{var e;const{datasource:t}=this.props,a=[];var n;return(null===(e=t.afserver)||void 0===e?void 0:e.name)?(a.push({label:t.afserver.name,value:{value:t.afserver.name,expandable:!0}}),(null===(n=t.afdatabase)||void 0===n?void 0:n.name)&&a.push({label:t.afdatabase.name,value:{value:t.afdatabase.name,expandable:!0}}),a.push({label:"Select Element",value:{value:"-Select Element-"}})):a.push({label:""}),a})),C(this,"updateArray",((e,t,a,n,l)=>{this.setState({segments:e,attributes:t,summaries:a,isPiPoint:n},(()=>{n||this.checkAttributeSegments(t,this.state.segments).then((()=>{l&&l()}))}))})),C(this,"scopedVarsDone",!1),C(this,"componentDidMount",(()=>{this.initialLoad(!1)})),C(this,"componentDidUpdate",(()=>{var e,t,a;const{query:n}=this.props;"Done"===(null===(e=this.props.data)||void 0===e?void 0:e.state)&&(null===(a=this.props.data)||void 0===a||null===(t=a.request)||void 0===t?void 0:t.scopedVars)&&!this.scopedVarsDone&&(this.scopedVarsDone=!0,this.initialLoad(!n.isPiPoint))})),C(this,"initialLoad",(e=>{const{query:t}=this.props,a=(0,c.defaults)(t,S),{segments:n,attributes:l,summary:r,isPiPoint:i}=a;var s;let o=e?[]:null!==(s=null==n?void 0:n.slice(0))&&void 0!==s?s:[];var u;let h=e?[]:null!==(u=null==l?void 0:l.slice(0))&&void 0!==u?u:[];var m;let d=null!==(m=null==r?void 0:r.types)&&void 0!==m?m:[];if(i||0!==o.length)i&&o.length>0&&(this.piServer=o);else{if(t.target&&t.target.length>0&&";"!==t.target)return h=[],void this.buildFromTarget(t,o,h).then((e=>{this.updateArray(e,h,d,!1)})).catch((e=>console.error(e)));o=this.checkAfServer()}this.updateArray(o,h,d,!!i,(()=>{this.onChange(t)}))})),C(this,"onChange",(e=>{const{onChange:t,onRunQuery:a}=this.props;var n,l;e.summary.types=this.state.summaries,e.rawQuery?e.target=null!==(n=e.query)&&void 0!==n?n:"":(e.elementPath=this.getSegmentPathUpTo(this.state.segments,this.state.segments.length),e.target=e.elementPath+";"+(0,c.join)(null===(l=e.attributes)||void 0===l?void 0:l.map((e=>{var t;return null===(t=e.value)||void 0===t?void 0:t.value})),";")),t(e),e.target&&e.target.length>0&&a()})),C(this,"stateCallback",(()=>{const e=this.props.query;this.onChange(e)})),C(this,"onIsPiPointChange",(e=>{const{query:t}=this.props,a=!t.isPiPoint;this.setState({segments:a?[{label:""}]:this.checkAfServer(),attributes:[],isPiPoint:a},(()=>{this.onChange(w(I({},t),{expression:"",attributes:this.state.attributes,segments:this.state.segments,isPiPoint:a}))}))})),this.onSegmentChange=this.onSegmentChange.bind(this),this.calcBasisValueChanged=this.calcBasisValueChanged.bind(this),this.calcNoDataValueChanged=this.calcNoDataValueChanged.bind(this),this.onSummaryAction=this.onSummaryAction.bind(this),this.onSummaryValueChanged=this.onSummaryValueChanged.bind(this),this.onAttributeAction=this.onAttributeAction.bind(this),this.onAttributeChange=this.onAttributeChange.bind(this),this.summaryTypes=["Total","Average","Minimum","Maximum","Range","StdDev","PopulationStdDev","Count","PercentGood","All","AllForNonNumeric"],this.calculationBasis=["TimeWeighted","EventWeighted","TimeWeightedContinuous","TimeWeightedDiscrete","EventWeightedExcludeMostRecentEvent","EventWeightedExcludeEarliestEvent","EventWeightedIncludeBothEnds"],this.noDataReplacement=["Null","Drop","Previous","0","Keep"]}}var A=o(269),W=o(531);function V(e){return(0,c.map)(e,(e=>{var t,a;return{text:e.Name,expandable:void 0===e.HasChildren||!0===e.HasChildren||(null!==(t=e.Path)&&void 0!==t?t:"").split("\\").length<=3,HasChildren:e.HasChildren,Items:null!==(a=e.Items)&&void 0!==a?a:[],Path:e.Path,WebId:e.WebId}}))}function N(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function T(e){for(var t=1;t{const t=c.target;if(t&&t[e])return{label:t[e].Name,value:t[e]}};var S;return m.getAssetServer(m.afserver.name).then((e=>{v(e.WebId)})),a().createElement(a().Fragment,null,a().createElement("div",{className:"gf-form-group"},a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Database",labelWidth:B,grow:!0},a().createElement(n.AsyncSelect,{key:null!=g?g:"database-key",loadOptions:()=>m.getDatabases(g).then((e=>e.map((e=>({label:e.Name,value:e}))))),loadingMessage:"Loading",value:E("database"),onChange:e=>{f(e.value),d(k(T({},h),{database:e.value,template:void 0}))},defaultOptions:!0})),a().createElement(n.InlineField,{label:"Event Frames",labelWidth:B,grow:!0},a().createElement(n.AsyncSelect,{key:null!==(S=null==y?void 0:y.WebId)&&void 0!==S?S:"default-template-key",loadOptions:()=>m.getEventFrameTemplates(null==y?void 0:y.WebId).then((e=>e.map((e=>({label:e.Name,value:e}))))),loadingMessage:"Loading",value:E("template"),onChange:e=>d(k(T({},h),{template:e.value})),defaultOptions:!0})),a().createElement(n.InlineField,{label:"Show Start and End Time",labelWidth:B,grow:!0},a().createElement(n.InlineSwitch,{value:!!h.showEndTime,onChange:e=>d(k(T({},h),{showEndTime:e.currentTarget.checked}))}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Category name",labelWidth:B,grow:!0},a().createElement(n.Input,{type:"text",value:h.categoryName,onBlur:e=>p(),onChange:e=>d(k(T({},h),{categoryName:e.currentTarget.value})),placeholder:"Enter category name"})),a().createElement(n.InlineField,{label:"Name Filter",labelWidth:B,grow:!0},a().createElement(n.Input,{type:"text",value:h.nameFilter,onBlur:e=>p(),onChange:e=>d(k(T({},h),{nameFilter:e.currentTarget.value})),placeholder:"Enter name filter"}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Enable Name Regex Replacement",labelWidth:B,grow:!1},a().createElement(n.InlineSwitch,{value:null===(r=h.regex)||void 0===r?void 0:r.enable,onChange:e=>d(k(T({},h),{regex:k(T({},h.regex),{enable:e.currentTarget.checked})}))})),a().createElement(n.InlineField,{label:"Name Filter",labelWidth:20,grow:!1},a().createElement(n.Input,{type:"text",value:null===(i=h.regex)||void 0===i?void 0:i.search,onBlur:e=>p(),onChange:e=>d(k(T({},h),{regex:k(T({},h.regex),{search:e.currentTarget.value})})),placeholder:"(.*)",width:50})),a().createElement(n.InlineField,{label:"Replace",labelWidth:20,grow:!0},a().createElement(n.Input,{type:"text",value:null==h||null===(s=h.regex)||void 0===s?void 0:s.replace,onBlur:e=>p(),onChange:e=>d(k(T({},h),{regex:k(T({},h.regex),{replace:e.currentTarget.value})})),placeholder:"$1"}))),a().createElement(n.InlineFieldRow,null,a().createElement(n.InlineField,{label:"Enable Attribute Usage",labelWidth:B,grow:!1},a().createElement(n.InlineSwitch,{value:null===(o=h.attribute)||void 0===o?void 0:o.enable,onChange:e=>d(k(T({},h),{attribute:k(T({},h.attribute),{enable:e.currentTarget.checked})}))})),a().createElement(n.InlineField,{label:"Attribute Name",labelWidth:B,grow:!0},a().createElement(n.Input,{type:"text",value:null===(u=h.attribute)||void 0===u?void 0:u.name,onBlur:e=>p(),onChange:e=>d(k(T({},h),{attribute:k(T({},h.attribute),{name:e.currentTarget.value})})),placeholder:"Enter name"})))))}));function R(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}class U extends W.DataSourceWithBackend{applyTemplateVariables(e,t){return a=function(e){for(var t=1;te.substring(1,e.length-2).split(",")[0]))),e.filter=null!==(l=e.filter)&&void 0!==l?l:"*","servers"===e.type?(null===(r=a.afserver)||void 0===r?void 0:r.name)?a.getAssetServer(a.afserver.name).then((e=>[e])).then(V):a.getAssetServers().then(V):"databases"===e.type&&e.afServerWebId?a.getDatabases(e.afServerWebId,{}).then(V):"databases"===e.type?a.getAssetServer(e.path).then((e=>{var t;return a.getDatabases(null!==(t=e.WebId)&&void 0!==t?t:"",{})})).then(V):"databaseElements"===e.type?a.getDatabase(e.path).then((e=>{var t;return a.getDatabaseElements(null!==(t=e.WebId)&&void 0!==t?t:"",{selectedFields:"Items.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren"})})).then(V):"elements"===e.type?a.getElement(e.path).then((t=>{var n;return a.getElements(null!==(n=t.WebId)&&void 0!==n?n:"",{selectedFields:"Items.Description%3BItems.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren",nameFilter:e.filter})})).then(V):"attributes"===e.type?a.getElement(e.path).then((t=>{var n;return a.getAttributes(null!==(n=t.WebId)&&void 0!==n?n:"",{searchFullHierarchy:"true",selectedFields:"Items.Type%3BItems.DefaultUnitsName%3BItems.Description%3BItems.WebId%3BItems.Name%3BItems.Path",nameFilter:e.filter})})).then(V):"dataserver"===e.type?a.getDataServers().then(V):"pipoint"===e.type?a.piPointSearch(e.webId,e.pointName).then(V):Promise.reject("Bad type")}buildQueryParameters(e){return e.targets=(0,c.filter)(e.targets,(e=>{var t;return!(!e||!e.target||0===(null===(t=e.attributes)||void 0===t?void 0:t.length)||";"===e.target||e.hide||e.target.startsWith("Select AF"))})),e.targets=(0,c.map)(e.targets,(t=>{if(t.rawQuery&&t.target){const{attributes:a,elementPath:n}=function(e){const t=e.split(";"),a=t[0].split("\\");t.splice(0,1);let n=[];if(a.length>1||1===a.length&&""!==a[0]){const e=a.join("\\");return(0,c.each)(t,(function(e,t){""!==e&&n.push({label:e,value:{value:e,expandable:!1}})})),{attributes:n,elementPath:e}}return{attributes:n,elementPath:null}}(this.templateSrv.replace(t.target,e.scopedVars));t.attributes=a,t.elementPath=n}var a;const n={enableStreaming:t.enableStreaming,target:this.templateSrv.replace(t.elementPath,e.scopedVars),elementPath:this.templateSrv.replace(t.elementPath,e.scopedVars),attributes:(0,c.map)(t.attributes,(t=>{var a;return this.templateSrv.replace((null===(a=t.value)||void 0===a?void 0:a.value)||t,e.scopedVars)})),isAnnotation:!!t.isAnnotation,segments:(0,c.map)(t.segments,(t=>{var a;return this.templateSrv.replace(null===(a=t.value)||void 0===a?void 0:a.value,e.scopedVars)})),display:t.display?this.templateSrv.replace(t.display,e.scopedVars):void 0,refId:t.refId,hide:t.hide,interpolate:t.interpolate||{enable:!1},useLastValue:t.useLastValue||{enable:!1},useUnit:t.useUnit||{enable:!1},recordedValues:t.recordedValues||{enable:!1},digitalStates:t.digitalStates||{enable:!1},webid:null!==(a=t.webid)&&void 0!==a?a:"",webids:t.webids||[],regex:t.regex||{enable:!1},expression:t.expression||"",summary:t.summary||{types:[]},startTime:e.range.from,endTime:e.range.to,isPiPoint:!!t.isPiPoint,scopedVars:e.scopedVars};return n.expression&&(n.expression=this.templateSrv.replace(n.expression,e.scopedVars)),void 0!==n.summary.types&&(n.summary.types=(0,c.filter)(n.summary.types,(e=>null!=e&&""!==e))),n})),e}eventFrameToAnnotation(e,t){const a=e.target,n=[],l=Intl.DateTimeFormat().resolvedOptions().locale;return t.forEach((e=>{let t=this.transformDataFrameToMap(e);for(let e=0;eStart: "+new Date(t.time[e]).toLocaleString(l)+"
End: ",t.timeEnd[e]?i+=new Date(t.timeEnd[e]).toLocaleString(l):i+="Eventframe is open";const s={time:t.time[e],timeEnd:a.showEndTime?t.timeEnd[e]:void 0,title:r,id:t.id[e],text:i,tags:["OSISoft PI"]};n.push(s)}})),n}transformDataFrameToMap(e){const t={};return e.fields.forEach((e=>{t[e.name]=e.values.toArray()})),t}restGet(e){const t=this.backendSrv.fetch({url:`/api/datasources/${this.id}/resources${e}`,method:"GET",headers:{"Content-Type":"application/json"}});return(0,A.firstValueFrom)(t).then((e=>e))}getDataServers(){return this.restGet("/dataservers").then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getDataServer(e){return e?this.restGet("/dataservers?name="+e).then((e=>e.data)):Promise.resolve({})}getAssetServers(){return this.restGet("/assetservers").then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getAssetServer(e){return e?this.restGet("/assetservers?path=\\\\"+e).then((e=>e.data)):Promise.resolve({})}getDatabase(e){return e?this.restGet("/assetdatabases?path=\\\\"+e).then((e=>e.data)):Promise.resolve({})}getDatabases(e,t){return e?this.restGet("/assetservers/"+e+"/assetdatabases").then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]})):Promise.resolve([])}getElement(e){return e?this.restGet("/elements?path=\\\\"+e).then((e=>e.data)):Promise.resolve({})}getEventFrameTemplates(e){return e?this.restGet("/assetdatabases/"+e+"/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId").then((e=>{var t;return(0,c.filter)(null!==(t=e.data.Items)&&void 0!==t?t:[],(e=>"EventFrame"===e.InstanceType))})):Promise.resolve([])}getElementTemplates(e){return e?this.restGet("/assetdatabases/"+e+"/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId").then((e=>{var t;return(0,c.filter)(null!==(t=e.data.Items)&&void 0!==t?t:[],(e=>"Element"===e.InstanceType))})):Promise.resolve([])}getAttributes(e,t){let a="?"+(0,c.map)(t,((e,t)=>t+"="+e)).join("&");return"?"===a&&(a=""),this.restGet("/elements/"+e+"/attributes"+a).then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getDatabaseElements(e,t){let a="?"+(0,c.map)(t,((e,t)=>t+"="+e)).join("&");return"?"===a&&(a=""),this.restGet("/assetdatabases/"+e+"/elements"+a).then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}getElements(e,t){let a="?"+(0,c.map)(t,((e,t)=>t+"="+e)).join("&");return"?"===a&&(a=""),this.restGet("/elements/"+e+"/elements"+a).then((e=>{var t;return null!==(t=e.data.Items)&&void 0!==t?t:[]}))}piPointSearch(e,t){let a=this.templateSrv.replace(t),n=`${a}`,l=!1;if(a!==t){const e=RegExp("\\{(\\w|,)+\\}","gs");let t;for(;null!==(t=e.exec(a));)t.index===e.lastIndex&&e.lastIndex++,t.forEach(((e,t)=>{0===t&&(a=a.replace(e,e.replace("{","(").replace("}",")").replace(",","|")),n=n.replace(e,"*"),l=!0)}))}return this.restGet("/dataservers/"+e+"/points?maxCount=50&nameFilter="+n).then((e=>{var t;return e&&(null===(t=e.data)||void 0===t?void 0:t.Items)?l?e.data.Items.filter((e=>{var t;return null===(t=e.Name)||void 0===t?void 0:t.match(a)})):e.data.Items:[]}))}constructor(e,t=(0,W.getTemplateSrv)(),a=(0,W.getBackendSrv)()){super(e),R(this,"templateSrv",void 0),R(this,"backendSrv",void 0),R(this,"piserver",void 0),R(this,"afserver",void 0),R(this,"afdatabase",void 0),R(this,"piPointConfig",void 0),R(this,"newFormatConfig",void 0),R(this,"useUnitConfig",void 0),R(this,"useExperimental",void 0),R(this,"useStreaming",void 0),this.templateSrv=t,this.backendSrv=a,this.piserver={name:(e.jsonData||{}).piserver,webid:void 0},this.afserver={name:(e.jsonData||{}).afserver,webid:void 0},this.afdatabase={name:(e.jsonData||{}).afdatabase,webid:void 0},this.piPointConfig=e.jsonData.pipoint||!1,this.newFormatConfig=e.jsonData.newFormat||!1,this.useUnitConfig=e.jsonData.useUnit||!1,this.useExperimental=e.jsonData.useExperimental||!1,this.useStreaming=e.jsonData.useStreaming||!1,this.annotations={QueryEditor:q,prepareQuery:e=>(e.target&&(e.target.queryType="Annotation",e.target.isAnnotation=!0),e.target),processEvents:(e,t)=>(0,A.of)(this.eventFrameToAnnotation(e,t))},Promise.all([this.getDataServer(this.piserver.name).then((e=>this.piserver.webid=e.WebId)),this.getAssetServer(this.afserver.name).then((e=>this.afserver.webid=e.WebId)),this.getDatabase(this.afserver.name&&this.afdatabase.name?this.afserver.name+"\\"+this.afdatabase.name:void 0).then((e=>this.afdatabase.webid=e.WebId))])}}const M=new e.DataSourcePlugin(U).setQueryEditor(j).setConfigEditor(m)})(),u})())); //# sourceMappingURL=module.js.map \ No newline at end of file diff --git a/dist/module.js.map b/dist/module.js.map index e09b425..2f1279f 100644 --- a/dist/module.js.map +++ b/dist/module.js.map @@ -1 +1 @@ -{"version":3,"file":"module.js","mappings":"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","sources":["webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"@grafana/data\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"@grafana/runtime\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"@grafana/ui\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"lodash\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"react\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"rxjs\"","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/bootstrap","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/compat get default export","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/define property getters","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/hasOwnProperty shorthand","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/make namespace object","webpack://gridprotectionalliance-osisoftpi-datasource/./config/ConfigEditor.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./components/Forms.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./types.ts","webpack://gridprotectionalliance-osisoftpi-datasource/./components/QueryEditorModeSwitcher.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./query/QueryEditor.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./helper.ts","webpack://gridprotectionalliance-osisoftpi-datasource/./query/AnnotationsQueryEditor.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./datasource.ts","webpack://gridprotectionalliance-osisoftpi-datasource/./module.ts"],"sourcesContent":["module.exports = __WEBPACK_EXTERNAL_MODULE__781__;","module.exports = __WEBPACK_EXTERNAL_MODULE__531__;","module.exports = __WEBPACK_EXTERNAL_MODULE__7__;","module.exports = __WEBPACK_EXTERNAL_MODULE__241__;","module.exports = __WEBPACK_EXTERNAL_MODULE__959__;","module.exports = __WEBPACK_EXTERNAL_MODULE__269__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import React, { ChangeEvent, PureComponent } from 'react';\nimport { LegacyForms, DataSourceHttpSettings, InlineField, InlineSwitch } from '@grafana/ui';\nimport { DataSourcePluginOptionsEditorProps, DataSourceJsonData, DataSourceSettings } from '@grafana/data';\nimport { PIWebAPIDataSourceJsonData } from '../types';\n\nconst { FormField } = LegacyForms;\n\ninterface Props extends DataSourcePluginOptionsEditorProps {}\n\nconst coerceOptions = (\n options: DataSourceSettings\n): DataSourceSettings => {\n return {\n ...options,\n jsonData: {\n ...options.jsonData,\n url: options.url,\n },\n };\n};\n\ninterface State {}\n\nexport class PIWebAPIConfigEditor extends PureComponent {\n onPIServerChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n piserver: event.target.value,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onAFServerChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n afserver: event.target.value,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onAFDatabaseChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n afdatabase: event.target.value,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onHttpOptionsChange = (options: DataSourceSettings) => {\n const { onOptionsChange } = this.props;\n onOptionsChange(coerceOptions(options));\n };\n\n onPiPointChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n piserver: event.target.checked ? options.jsonData.piserver : '',\n pipoint: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onNewFormatChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n newFormat: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onUseUnitChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n useUnit: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onUseExperimentalChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n useExperimental : event.target.checked,\n useStreaming : event.target.checked ? options.jsonData.useStreaming : false,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onUseStreamingChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n useStreaming: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n render() {\n const { options: originalOptions } = this.props;\n const options = coerceOptions(originalOptions);\n\n return (\n
\n \n\n

Custom Configuration

\n\n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n {options.jsonData.useExperimental && (\n
\n \n \n \n
\n )}\n
\n\n

PI/AF Connection Details

\n\n
\n {options.jsonData.pipoint && (\n
\n \n
\n )}\n
\n \n
\n
\n \n
\n
\n
\n );\n }\n}\n","import React, { InputHTMLAttributes, FunctionComponent } from 'react';\nimport { InlineFormLabel } from '@grafana/ui';\n\nexport interface Props extends InputHTMLAttributes {\n label: string;\n tooltip?: string;\n labelWidth?: number;\n children?: React.ReactNode;\n queryEditor?: JSX.Element;\n}\n\nexport const QueryField: FunctionComponent> = ({ label, labelWidth = 12, tooltip, children }) => (\n <>\n \n {label}\n \n {children}\n \n);\n\nexport const QueryRowTerminator = () => {\n return (\n
\n
\n
\n );\n};\n\nexport const QueryInlineField = ({ ...props }) => {\n return (\n \n \n \n );\n};\n\nexport const QueryEditorRow = (props: Partial) => {\n return (\n
\n {props.children}\n \n
\n );\n};\n\nexport const QueryRawInlineField = ({ ...props }) => {\n return (\n \n \n \n );\n};\n\nexport const QueryRawEditorRow = (props: Partial) => {\n return <>{props.children};\n};\n","import { DataQuery } from '@grafana/schema';\nimport { DataSourceJsonData, Labels, QueryResultMeta, TimeSeriesPoints } from '@grafana/data';\n\nexport interface PiwebapiElementPath {\n path: string;\n variable: string;\n}\n\nexport interface PiwebapiInternalRsp {\n data: PiwebapiRsp;\n status: number;\n url: string;\n}\n\nexport interface PiwebapTargetRsp {\n refId: string;\n target: string;\n tags: Labels;\n datapoints: TimeSeriesPoints;\n path?: string;\n meta?: QueryResultMeta;\n unit?: string;\n}\n\nexport interface PiwebapiRsp {\n Name?: string;\n InstanceType?: string;\n Items?: PiwebapiRsp[];\n WebId?: string;\n HasChildren?: boolean;\n Type?: string;\n DefaultUnitsName?: string;\n Description?: string;\n Path?: string;\n}\n\nexport interface PiDataServer {\n name: string | undefined;\n webid: string | undefined;\n}\n\nexport interface PIWebAPISelectableValue {\n webId?: string;\n value?: string;\n type?: string;\n expandable?: boolean;\n}\n\nexport interface PIWebAPIAnnotationsQuery extends DataQuery {\n target: string;\n}\n\nexport interface PIWebAPIQuery extends DataQuery {\n target?: string;\n elementPath?: string;\n attributes?: any[];\n segments?: any[];\n isPiPoint?: boolean;\n isAnnotation?: boolean;\n webid?: string;\n webids?: string[];\n display?: any;\n interpolate?: any;\n recordedValues?: any;\n digitalStates?: any;\n enableStreaming: any;\n useLastValue?: any;\n useUnit?: any;\n regex?: any;\n summary?: any;\n expression?: string;\n rawQuery?: boolean;\n query?: string;\n // annotations items\n database?: PiwebapiRsp;\n template?: PiwebapiRsp;\n showEndTime?: boolean;\n attribute?: any;\n nameFilter?: string;\n categoryName?: string;\n}\n\nexport const defaultQuery: Partial = {\n target: ';',\n attributes: [],\n segments: [],\n regex: { enable: false },\n summary: { types: [], basis: 'EventWeighted', interval: '', nodata: 'Null' },\n expression: '',\n interpolate: { enable: false },\n useLastValue: { enable: false },\n recordedValues: { enable: false },\n digitalStates: { enable: false },\n enableStreaming: { enable: false },\n useUnit: { enable: false },\n isPiPoint: false,\n};\n\n/**\n * These are options configured for each DataSource instance\n */\nexport interface PIWebAPIDataSourceJsonData extends DataSourceJsonData {\n url?: string;\n access?: string;\n piserver?: string;\n afserver?: string;\n afdatabase?: string;\n pipoint?: boolean;\n newFormat?: boolean;\n useUnit?: boolean;\n useExperimental?: boolean;\n useStreaming?: boolean;\n}\n\n/**\n * Value that is used in the backend, but never sent over HTTP to the frontend\n */\nexport interface PIWebAPISecureJsonData {\n apiKey?: string;\n}\n","import React, { useEffect, useState } from 'react';\nimport { Button, ConfirmModal } from '@grafana/ui';\n\ntype Props = {\n isRaw: boolean;\n onChange: (newIsRaw: boolean) => void;\n};\n\nexport const QueryEditorModeSwitcher = ({ isRaw, onChange }: Props): JSX.Element => {\n const [isModalOpen, setModalOpen] = useState(false);\n\n useEffect(() => {\n // if the isRaw changes, we hide the modal\n setModalOpen(false);\n }, [isRaw]);\n\n if (isRaw) {\n return (\n <>\n {\n // we show the are-you-sure modal\n setModalOpen(true);\n }}\n >\n {\n onChange(false);\n }}\n onDismiss={() => {\n setModalOpen(false);\n }}\n />\n \n );\n } else {\n return (\n {\n onChange(true);\n }}\n >\n );\n }\n};\n","import { each, filter, forOwn, join, reduce, map, slice, remove, defaults } from 'lodash';\n\nimport React, { PureComponent, ChangeEvent } from 'react';\nimport { Icon, InlineField, InlineFieldRow, InlineSwitch, Input, SegmentAsync, Segment } from '@grafana/ui';\nimport { QueryEditorProps, SelectableValue, TypedVariableModel } from '@grafana/data';\n\nimport { PiWebAPIDatasource } from '../datasource';\nimport { QueryInlineField, QueryRawInlineField, QueryRowTerminator } from '../components/Forms';\nimport { PIWebAPISelectableValue, PIWebAPIDataSourceJsonData, PIWebAPIQuery, defaultQuery } from '../types';\nimport { QueryEditorModeSwitcher } from 'components/QueryEditorModeSwitcher';\n\nconst LABEL_WIDTH = 24;\nconst MIN_ELEM_INPUT_WIDTH = 200;\nconst MIN_ATTR_INPUT_WIDTH = 250;\n\ninterface State {\n isPiPoint: boolean;\n segments: Array>;\n attributes: Array>;\n summaries: Array>;\n attributeSegment: SelectableValue;\n summarySegment: SelectableValue;\n calculationBasisSegment: SelectableValue;\n noDataReplacementSegment: SelectableValue;\n}\n\ntype Props = QueryEditorProps;\n\nconst REMOVE_LABEL = '-REMOVE-';\n\nconst CustomLabelComponent = (props: any) => {\n if (props.value) {\n return (\n
\n {props.label ?? '--no label--'}\n
\n );\n }\n return (\n \n \n \n );\n};\n\nexport class PIWebAPIQueryEditor extends PureComponent {\n error: any;\n piServer: any[] = [];\n availableAttributes: any = {};\n summaryTypes: string[];\n calculationBasis: string[];\n noDataReplacement: string[];\n state: State = {\n isPiPoint: false,\n segments: [],\n attributes: [],\n summaries: [],\n attributeSegment: {},\n summarySegment: {},\n calculationBasisSegment: {},\n noDataReplacementSegment: {},\n };\n\n constructor(props: any) {\n super(props);\n this.onSegmentChange = this.onSegmentChange.bind(this);\n this.calcBasisValueChanged = this.calcBasisValueChanged.bind(this);\n this.calcNoDataValueChanged = this.calcNoDataValueChanged.bind(this);\n this.onSummaryAction = this.onSummaryAction.bind(this);\n this.onSummaryValueChanged = this.onSummaryValueChanged.bind(this);\n this.onAttributeAction = this.onAttributeAction.bind(this);\n this.onAttributeChange = this.onAttributeChange.bind(this);\n\n this.summaryTypes = [\n // 'None', // A summary type is not specified.\n 'Total', // A totalization over the time range.\n 'Average', // The average value over the time range.\n 'Minimum', // The minimum value over the time range.\n 'Maximum', // The maximum value over the time range.\n 'Range', // The range value over the time range (minimum-maximum).\n 'StdDev', // The standard deviation over the time range.\n 'PopulationStdDev', // The population standard deviation over the time range.\n 'Count', // The sum of event count over the time range when calculation basis is event weighted. The sum of event time duration over the time range when calculation basis is time weighted.\n 'PercentGood', // Percent of data with good value during the calculation period. For time weighted calculations, the percentage is based on time. For event weighted calculations, the percent is based on event count.\n 'All', // A convenience for requesting all available summary calculations.\n 'AllForNonNumeric', // A convenience for requesting all available summary calculations for non-numeric data.\n ];\n\n this.calculationBasis = [\n 'TimeWeighted', // Weight the values in the calculation by the time over which they apply. Interpolation is based on whether the attribute is stepped. Interpolated events are generated at the boundaries if necessary.\n 'EventWeighted', // Evaluate values with equal weighting for each event. No interpolation is done. There must be at least one event within the time range to perform a successful calculation. Two events are required for standard deviation. In handling events at the boundary of the calculation, the AFSDK uses following rules:\n 'TimeWeightedContinuous', // Apply weighting as in TimeWeighted, but do all interpolation between values as if they represent continuous data, (standard interpolation) regardless of whether the attribute is stepped.\n 'TimeWeightedDiscrete', // Apply weighting as in TimeWeighted but interpolation between values is performed as if they represent discrete, unrelated values (stair step plot) regardless of the attribute is stepped.\n 'EventWeightedExcludeMostRecentEvent', // The calculation behaves the same as _EventWeighted_, except in the handling of events at the boundary of summary intervals in a multiple intervals calculation. Use this option to prevent events at the intervals boundary from being double count at both intervals. With this option, events at the end time (most recent time) of an interval is not used in that interval.\n 'EventWeightedExcludeEarliestEvent', // Similar to the option _EventWeightedExcludeMostRecentEvent_. Events at the start time(earliest time) of an interval is not used in that interval.\n 'EventWeightedIncludeBothEnds', // Events at both ends of the interval boundaries are included in the event weighted calculation.\n ];\n\n this.noDataReplacement = [\n 'Null', // replace with nulls\n 'Drop', // drop items\n 'Previous', // use previous value if available\n '0', // replace with 0\n 'Keep', // Keep value\n ];\n }\n\n // is selected segment empty\n isValueEmpty(value: PIWebAPISelectableValue | undefined) {\n return !value || !value.value || !value.value.length || value.value === REMOVE_LABEL;\n }\n\n segmentChangeValue = (segments: Array>) => {\n const query = this.props.query;\n this.setState({ segments }, () => this.onChange({ ...query, segments }));\n };\n\n attributeChangeValue = (attributes: Array>) => {\n const query = this.props.query;\n this.setState({ attributes }, () => this.onChange({ ...query, attributes }));\n };\n\n // summary calculation basis change event\n calcBasisValueChanged(segment: SelectableValue) {\n const metricsQuery = this.props.query as PIWebAPIQuery;\n const summary = metricsQuery.summary;\n summary.basis = segment.value?.value;\n this.onChange({ ...metricsQuery, summary });\n }\n // get summary calculation basis user interface segments\n getCalcBasisSegments() {\n const segments = map(this.calculationBasis, (item: string) => {\n let selectableValue: SelectableValue = {\n label: item,\n value: {\n value: item,\n expandable: true,\n },\n };\n return selectableValue;\n });\n return segments;\n }\n\n // no data change event\n calcNoDataValueChanged(segment: SelectableValue) {\n const metricsQuery = this.props.query as PIWebAPIQuery;\n const summary = metricsQuery.summary;\n summary.nodata = segment.value?.value;\n this.onChange({ ...metricsQuery, summary });\n }\n // get no data user interface segments\n getNoDataSegments() {\n const segments = map(this.noDataReplacement, (item: string) => {\n let selectableValue: SelectableValue = {\n label: item,\n value: {\n value: item,\n expandable: true,\n },\n };\n return selectableValue;\n });\n return segments;\n }\n\n // summary query change event\n onSummaryValueChanged(item: SelectableValue, index: number) {\n const summaries = this.state.summaries.slice(0) as Array>;\n summaries[index] = item;\n if (this.isValueEmpty(item.value)) {\n summaries.splice(index, 1);\n }\n this.setState({ summaries }, this.stateCallback);\n }\n // get the list of summaries available\n getSummarySegments() {\n const summaryTypes = filter(this.summaryTypes, (type) => {\n return this.state.summaries.map((s) => s.value?.value).indexOf(type) === -1;\n });\n\n const segments = map(summaryTypes, (item: string) => {\n let selectableValue: SelectableValue = {\n label: item,\n value: {\n value: item,\n expandable: true,\n },\n };\n return selectableValue;\n });\n\n segments.unshift({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n\n return segments;\n }\n\n // remove a summary from the user interface and the query\n removeSummary(part: SelectableValue) {\n const summaries = filter(this.state.summaries, (item: SelectableValue) => {\n return item !== part;\n });\n this.setState({ summaries });\n }\n // add a new summary to the query\n onSummaryAction(item: SelectableValue) {\n const summaries = this.state.summaries.slice(0) as Array>;\n // if value is not empty, add new attribute segment\n if (!this.isValueEmpty(item.value)) {\n let selectableValue: SelectableValue = {\n label: item.label,\n value: {\n value: item.value?.value,\n expandable: true,\n },\n };\n summaries.push(selectableValue);\n }\n this.setState({ summarySegment: {}, summaries }, this.stateCallback);\n }\n\n // remove an attribute from the query\n removeAttribute(part: SelectableValue) {\n const attributes = filter(this.state.attributes, (item: SelectableValue) => {\n return item !== part;\n });\n this.attributeChangeValue(attributes);\n }\n // add an attribute to the query\n onAttributeAction(item: SelectableValue) {\n const { query } = this.props;\n const attributes = this.state.attributes.slice(0);\n // if value is not empty, add new attribute segment\n if (!this.isValueEmpty(item.value)) {\n let selectableValue: SelectableValue = {\n label: item.label,\n value: {\n value: item.value?.value,\n expandable: !query.isPiPoint,\n },\n };\n attributes.push(selectableValue);\n }\n this.attributeChangeValue(attributes);\n }\n\n // pi point change event\n onPiPointChange = (item: SelectableValue, index: number) => {\n let attributes = this.state.attributes.slice(0);\n\n if (item.label === REMOVE_LABEL) {\n remove(attributes, (value, n) => n === index);\n } else {\n // set current value\n attributes[index] = item;\n }\n\n this.checkPiPointSegments(item, attributes);\n };\n // attribute change event\n onAttributeChange = (item: SelectableValue, index: number) => {\n let attributes = this.state.attributes.slice(0);\n\n // ignore if no change\n if (attributes[index].label === item.value?.value) {\n return;\n }\n\n // set current value\n attributes[index] = item;\n\n this.checkAttributeSegments(attributes, this.state.segments);\n };\n // segment change\n onSegmentChange = (item: SelectableValue, index: number) => {\n const { query } = this.props;\n let segments = this.state.segments.slice(0);\n\n // ignore if no change\n if (segments[index].label === item.value?.value) {\n return;\n }\n\n // reset attributes list\n this.setState({ attributes: [] }, () => {\n if (item.label === REMOVE_LABEL) {\n segments = slice(segments, 0, index);\n this.checkAttributeSegments([], segments).then(() => {\n if (segments.length === 0) {\n segments.push({\n label: '',\n });\n } else if (!!segments[segments.length - 1].value?.expandable) {\n segments.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n if (query.isPiPoint) {\n this.piServer = [];\n }\n this.segmentChangeValue(segments);\n });\n return;\n }\n\n // set current value\n segments[index] = item;\n\n // Accept only one PI server\n if (query.isPiPoint) {\n this.piServer.push(item);\n this.segmentChangeValue(segments);\n return;\n }\n\n // changed internal selection\n if (index < segments.length - 1) {\n segments = slice(segments, 0, index + 1);\n }\n this.checkAttributeSegments([], segments).then(() => {\n // add new options\n if (!!item.value?.expandable) {\n segments.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n this.segmentChangeValue(segments);\n });\n });\n };\n\n // get a ui segment for the attributes\n getElementSegments = (\n index: number,\n currentSegment?: Array>\n ): Promise>> => {\n const { datasource, query, data } = this.props;\n const ctrl = this;\n const findQuery = query.isPiPoint\n ? { type: 'dataserver' }\n : {\n path: this.getSegmentPathUpTo(currentSegment ?? this.state.segments.slice(0), index),\n afServerWebId: this.state.segments.length > 0 && this.state.segments[0].value ? this.state.segments[0].value.webId : undefined,\n };\n\n if (!query.isPiPoint) {\n if (datasource.afserver?.name && index === 0) {\n return Promise.resolve([\n {\n label: datasource.afserver.name,\n value: {\n value: datasource.afserver.name,\n expandable: true,\n },\n },\n ]);\n }\n if (datasource.afserver?.name && datasource.afdatabase?.name && index === 1) {\n return Promise.resolve([\n {\n label: datasource.afdatabase.name,\n value: {\n value: datasource.afdatabase.name,\n expandable: true,\n },\n },\n ]);\n }\n }\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: query.isPiPoint }))\n .then((items: any[]) => {\n const altSegments = map(items, (item: any) => {\n let selectableValue: SelectableValue = {\n label: item.text,\n value: {\n webId: item.WebId,\n value: item.text,\n expandable: !query.isPiPoint && item.expandable,\n },\n };\n return selectableValue;\n });\n\n if (altSegments.length === 0) {\n return altSegments;\n }\n\n // add template variables\n const variables = datasource.templateSrv.getVariables();\n each(variables, (variable: TypedVariableModel) => {\n let selectableValue: SelectableValue = {\n label: '${' + variable.name + '}',\n value: {\n type: 'template',\n value: '${' + variable.name + '}',\n expandable: !query.isPiPoint,\n },\n };\n altSegments.unshift(selectableValue);\n });\n\n altSegments.unshift({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n\n return altSegments;\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n return [];\n });\n };\n\n // get the list of attributes for the user interface - PI\n getAttributeSegmentsPI = (attributeText?: string): Promise>> => {\n const { datasource, query, data } = this.props;\n const ctrl = this;\n const findQuery = {\n path: '',\n webId: this.getSelectedPIServer(),\n pointName: (attributeText ?? '') + '*',\n type: 'pipoint',\n };\n let segments: Array> = [];\n segments.push({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: query.isPiPoint }))\n .then((items: any[]) => {\n segments = map(items, (item: any) => {\n let selectableValue: SelectableValue = {\n path: item.Path,\n label: item.text,\n value: {\n value: item.text,\n expandable: false,\n },\n };\n return selectableValue;\n });\n if (!!attributeText && attributeText.length > 0) {\n segments.unshift({\n label: attributeText,\n value: {\n value: attributeText,\n expandable: false,\n },\n });\n }\n // add template variables\n const variables = datasource.templateSrv.getVariables();\n each(variables, (variable: TypedVariableModel) => {\n let selectableValue: SelectableValue = {\n label: '${' + variable.name + '}',\n value: {\n type: 'template',\n value: '${' + variable.name + '}',\n expandable: !query.isPiPoint,\n },\n };\n segments.unshift(selectableValue);\n });\n return segments;\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n return segments;\n });\n };\n\n // get the list of attributes for the user interface - AF\n getAttributeSegmentsAF = (attributeText?: string): Array> => {\n const ctrl = this;\n let segments: Array> = [];\n\n segments.push({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n\n forOwn(ctrl.availableAttributes, (val: any, key: string) => {\n let selectableValue: SelectableValue = {\n label: key,\n value: {\n value: key,\n expandable: true,\n },\n };\n segments.push(selectableValue);\n });\n\n return segments;\n };\n\n // build data from target string\n buildFromTarget = (\n query: PIWebAPIQuery,\n segmentsArray: Array>,\n attributesArray: Array>\n ) => {\n const splitAttributes = query.target!.split(';');\n const splitElements = splitAttributes.length > 0 ? splitAttributes[0].split('\\\\') : [];\n\n if (splitElements.length > 1 || (splitElements.length === 1 && splitElements[0] !== '')) {\n // remove element hierarchy from attribute collection\n splitAttributes.splice(0, 1);\n\n each(splitElements, (item, _) => {\n segmentsArray.push({\n label: item,\n value: {\n type: item.match(/\\${\\w+}/gi) ? 'template' : undefined,\n value: item,\n expandable: true,\n },\n });\n });\n each(splitAttributes, (item, _) => {\n if (item !== '') {\n // set current value\n attributesArray.push({\n label: item,\n value: {\n value: item,\n expandable: false,\n },\n });\n }\n });\n return this.getElementSegments(splitElements.length + 1, segmentsArray).then((elements) => {\n if (elements.length > 0) {\n segmentsArray.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n return segmentsArray;\n });\n }\n return Promise.resolve(segmentsArray);\n };\n\n /**\n * Gets the segment information and parses it to a string.\n *\n * @param {any} index - Last index of segment to use.\n * @returns - AF Path or PI Point name.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n getSegmentPathUpTo(segments: Array>, index: number): string {\n const arr = segments.slice(0, index);\n\n return reduce(\n arr,\n (result: any, segment: SelectableValue) => {\n if (!segment.value) {\n return '';\n }\n if (!segment.value.value?.startsWith('-Select')) {\n return result ? result + '\\\\' + segment.value.value : segment.value.value;\n }\n return result;\n },\n ''\n );\n }\n\n /**\n * Get the current AF Element's child attributes. Validates when the element selection changes.\n *\n * @returns - Collection of attributes.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n checkAttributeSegments(\n attributes: Array>,\n segments: Array>\n ): Promise {\n const { datasource, data } = this.props;\n const ctrl = this;\n const findQuery = {\n path: this.getSegmentPathUpTo(segments.slice(0), segments.length),\n type: 'attributes',\n };\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: false }))\n .then((attributesResponse: any) => {\n const validAttributes: any = {};\n\n each(attributesResponse, (attribute: any) => {\n validAttributes[attribute.Path.substring(attribute.Path.indexOf('|') + 1)] = attribute.WebId;\n });\n\n const filteredAttributes = filter(attributes, (attrib: SelectableValue) => {\n const changedValue = datasource.templateSrv.replace(attrib.value?.value);\n return validAttributes[changedValue] !== undefined;\n });\n\n ctrl.availableAttributes = validAttributes;\n this.attributeChangeValue(filteredAttributes);\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n this.attributeChangeValue(attributes);\n });\n }\n\n /**\n * Get PI points from server.\n *\n * @returns - Collection of attributes.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n checkPiPointSegments(\n attribute: SelectableValue,\n attributes: Array>\n ) {\n const { datasource, data } = this.props;\n const ctrl = this;\n const findQuery = {\n path: attribute.path,\n webId: ctrl.getSelectedPIServer(),\n pointName: attribute.label,\n type: 'pipoint',\n };\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: true }))\n .then(() => {\n ctrl.attributeChangeValue(attributes);\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n ctrl.attributeChangeValue([]);\n });\n }\n\n /**\n * Gets the webid of the current selected pi data server.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n getSelectedPIServer() {\n let webID = '';\n\n this.piServer.forEach((s) => {\n const parts = this.props.query.target!.split(';');\n if (parts.length >= 2) {\n if (parts[0] === s.text) {\n webID = s.WebId;\n return;\n }\n }\n });\n return this.piServer.length > 0 ? this.piServer[0].value?.webId : webID;\n }\n\n /**\n * Queries PI Web API for child elements and attributes when the raw query text editor is changed.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n textEditorChanged() {\n const { query, onChange } = this.props;\n const splitAttributes = query.target!.split(';');\n const splitElements = splitAttributes.length > 0 ? splitAttributes[0].split('\\\\') : [];\n\n let segments: Array> = [];\n let attributes: Array> = [];\n\n if (splitElements.length > 1 || (splitElements.length === 1 && splitElements[0] !== '')) {\n // remove element hierarchy from attribute collection\n splitAttributes.splice(0, 1);\n\n each(splitElements, (item, _) => {\n segments.push({\n label: item,\n value: {\n type: item.match(/\\${\\w+}/gi) ? 'template' : undefined,\n value: item,\n expandable: true,\n },\n });\n });\n each(splitAttributes, function (item, index) {\n if (item !== '') {\n attributes.push({\n label: item,\n value: {\n value: item,\n expandable: false,\n },\n });\n }\n });\n this.getElementSegments(splitElements.length + 1, segments)\n .then((elements) => {\n if (elements.length > 0) {\n segments.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n })\n .then(() => {\n this.updateArray(segments, attributes, this.state.summaries, query.isPiPoint!, () => {\n onChange({ ...query, query: undefined, rawQuery: false });\n });\n });\n } else {\n segments = this.checkAfServer();\n this.updateArray(segments, this.state.attributes, this.state.summaries, query.isPiPoint!, () => {\n this.onChange({\n ...query,\n query: undefined,\n rawQuery: false,\n attributes: this.state.attributes,\n segments: this.state.segments,\n });\n });\n }\n }\n\n /**\n * Check if the AF server and database are configured in the datasoure config.\n *\n * @returns the segments array\n *\n * @memberOf PIWebAPIQueryEditor\n */\n checkAfServer = () => {\n const { datasource } = this.props;\n const segmentsArray = [];\n if (datasource.afserver?.name) {\n segmentsArray.push({\n label: datasource.afserver.name,\n value: {\n value: datasource.afserver.name,\n expandable: true,\n },\n });\n if (datasource.afdatabase?.name) {\n segmentsArray.push({\n label: datasource.afdatabase.name,\n value: {\n value: datasource.afdatabase.name,\n expandable: true,\n },\n });\n }\n segmentsArray.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n } else {\n segmentsArray.push({\n label: '',\n });\n }\n return segmentsArray;\n };\n\n /**\n * Update the internal state of the datasource.\n *\n * @param segmentsArray the segments array to update\n * @param attributesArray the AF attributes array to update\n * @param summariesArray the summaries array to update\n * @param isPiPoint the is PI point flag\n * @param cb optional callback function\n *\n * @memberOf PIWebAPIQueryEditor\n */\n updateArray = (\n segmentsArray: Array>,\n attributesArray: Array>,\n summariesArray: Array>,\n isPiPoint: boolean,\n cb?: (() => void) | undefined\n ) => {\n this.setState(\n {\n segments: segmentsArray,\n attributes: attributesArray,\n summaries: summariesArray,\n isPiPoint,\n },\n () => {\n if (!isPiPoint) {\n this.checkAttributeSegments(attributesArray, this.state.segments).then(() => {\n if (cb) {\n cb();\n }\n });\n }\n }\n );\n };\n\n // React action when component is initialized/updated\n scopedVarsDone = false;\n componentDidMount = () => {\n this.initialLoad(false);\n };\n\n componentDidUpdate = () => {\n const { query } = this.props;\n if (this.props.data?.state === 'Done' && !!this.props.data?.request?.scopedVars && !this.scopedVarsDone) {\n this.scopedVarsDone = true;\n this.initialLoad(!query.isPiPoint);\n }\n };\n\n initialLoad = (force: boolean) => {\n const { query } = this.props;\n const metricsQuery = defaults(query, defaultQuery) as PIWebAPIQuery;\n const { segments, attributes, summary, isPiPoint } = metricsQuery;\n\n let segmentsArray: Array> = force ? [] : segments?.slice(0) ?? [];\n let attributesArray: Array> = force ? [] : attributes?.slice(0) ?? [];\n let summariesArray = summary?.types ?? [];\n\n if (!isPiPoint && segmentsArray.length === 0) {\n if (query.target && query.target.length > 0 && query.target !== ';') {\n attributesArray = [];\n // Build query from target\n this.buildFromTarget(query, segmentsArray, attributesArray)\n .then((_segmentsArray) => {\n this.updateArray(_segmentsArray, attributesArray, summariesArray, false);\n })\n .catch((e) => console.error(e));\n return;\n } else {\n segmentsArray = this.checkAfServer();\n }\n } else if (isPiPoint && segmentsArray.length > 0) {\n this.piServer = segmentsArray;\n }\n this.updateArray(segmentsArray, attributesArray, summariesArray, !!isPiPoint, () => {\n this.onChange(query);\n });\n };\n\n onChange = (query: PIWebAPIQuery) => {\n const { onChange, onRunQuery } = this.props;\n\n query.summary.types = this.state.summaries;\n if (query.rawQuery) {\n query.target = query.query ?? '';\n } else {\n query.elementPath = this.getSegmentPathUpTo(this.state.segments, this.state.segments.length);\n query.target =\n query.elementPath +\n ';' +\n join(\n query.attributes?.map((s) => s.value?.value),\n ';'\n );\n }\n\n onChange(query);\n\n if (query.target && query.target.length > 0) {\n onRunQuery();\n }\n };\n\n stateCallback = () => {\n const query = this.props.query as PIWebAPIQuery;\n this.onChange(query);\n };\n\n onIsPiPointChange = (event: React.SyntheticEvent) => {\n const { query: queryChange } = this.props;\n const isPiPoint = !queryChange.isPiPoint;\n this.setState(\n {\n segments: isPiPoint ? [{ label: '' }] : this.checkAfServer(),\n attributes: [],\n isPiPoint,\n },\n () => {\n this.onChange({\n ...queryChange,\n expression: '',\n attributes: this.state.attributes,\n segments: this.state.segments,\n isPiPoint,\n });\n }\n );\n };\n\n render() {\n const { query: queryProps, onChange, onRunQuery } = this.props;\n const metricsQuery = defaults(queryProps, defaultQuery) as PIWebAPIQuery;\n const {\n useLastValue,\n useUnit,\n interpolate,\n query,\n rawQuery,\n digitalStates,\n enableStreaming,\n recordedValues,\n expression,\n isPiPoint,\n summary,\n display,\n regex,\n } = metricsQuery;\n\n return (\n <>\n {this.props.datasource.piPointConfig && (\n \n \n \n )}\n\n {!!rawQuery && (\n \n \n ) =>\n onChange({ ...metricsQuery, query: event.target.value })\n }\n placeholder=\"enter query\"\n />\n \n this.textEditorChanged()} />\n \n )}\n\n {!rawQuery && (\n <>\n
\n \n {this.state.segments.map((segment: SelectableValue, index: number) => {\n return (\n }\n onChange={(item) => this.onSegmentChange(item, index)}\n loadOptions={(query?: string | undefined) => {\n return this.getElementSegments(index);\n }}\n allowCustomValue\n inputMinWidth={MIN_ELEM_INPUT_WIDTH}\n />\n );\n })}\n \n {!isPiPoint && (\n {\n onChange({ ...metricsQuery, query: metricsQuery.target, rawQuery: value });\n }}\n />\n )}\n \n
\n\n \n {this.state.attributes.map((attribute: SelectableValue, index: number) => {\n if (isPiPoint) {\n return (\n }\n disabled={this.piServer.length === 0}\n onChange={(item) => this.onPiPointChange(item, index)}\n loadOptions={this.getAttributeSegmentsPI}\n reloadOptionsOnChange\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n );\n }\n return (\n }\n disabled={this.state.segments.length <= 2}\n onChange={(item) => this.onAttributeChange(item, index)}\n options={this.getAttributeSegmentsAF()}\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n );\n })}\n\n {isPiPoint && (\n \n }\n disabled={this.piServer.length === 0}\n onChange={this.onAttributeAction}\n loadOptions={this.getAttributeSegmentsPI}\n reloadOptionsOnChange\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n )}\n {!isPiPoint && (\n \n }\n disabled={this.state.segments.length <= 2}\n onChange={this.onAttributeAction}\n options={this.getAttributeSegmentsAF()}\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n )}\n \n \n )}\n\n \n \n \n this.onChange({\n ...metricsQuery,\n useLastValue: { ...useLastValue, enable: !useLastValue.enable },\n })\n }\n />\n \n {this.props.datasource.useUnitConfig && (\n \n \n this.onChange({\n ...metricsQuery,\n useUnit: { ...useUnit, enable: !useUnit.enable },\n })\n }\n />\n \n )}\n {this.props.datasource.useStreaming && (\n \n \n this.onChange({ ...metricsQuery, enableStreaming: { ...enableStreaming, enable: !enableStreaming.enable } })\n }\n />\n \n )}\n \n\n \n \n ) =>\n onChange({ ...metricsQuery, expression: event.target.value })\n }\n placeholder=\"'.'*2\"\n />\n \n \n\n {!useLastValue.enable && (\n <>\n \n \n ) =>\n onChange({\n ...metricsQuery,\n recordedValues: { ...recordedValues, maxNumber: parseInt(event.target.value, 10) },\n })\n }\n type=\"number\"\n placeholder=\"1000\"\n />\n \n \n \n this.onChange({\n ...metricsQuery,\n recordedValues: { ...recordedValues, enable: !recordedValues.enable },\n })\n }\n />\n \n \n \n this.onChange({\n ...metricsQuery,\n digitalStates: { ...digitalStates, enable: !digitalStates.enable },\n })\n }\n />\n \n \n\n \n \n ) =>\n onChange({ ...metricsQuery, interpolate: { ...interpolate, interval: event.target.value } })\n }\n placeholder=\"30s\"\n />\n \n \n \n this.onChange({ ...metricsQuery, interpolate: { ...interpolate, enable: !interpolate.enable } })\n }\n />\n \n \n }\n onChange={this.calcNoDataValueChanged}\n options={this.getNoDataSegments()}\n allowCustomValue\n />\n \n \n\n \n \n ) =>\n onChange({ ...metricsQuery, summary: { ...summary, interval: event.target.value } })\n }\n placeholder=\"30s\"\n />\n \n \n }\n onChange={this.calcBasisValueChanged}\n options={this.getCalcBasisSegments()}\n allowCustomValue\n />\n \n \n \n {this.state.summaries.map((s: SelectableValue, index: number) => {\n return (\n }\n onChange={(item) => this.onSummaryValueChanged(item, index)}\n options={this.getSummarySegments()}\n allowCustomValue\n />\n );\n })}\n \n }\n onChange={this.onSummaryAction}\n options={this.getSummarySegments()}\n allowCustomValue\n />\n \n \n \n \n )}\n\n \n \n ) =>\n onChange({ ...metricsQuery, display: event.target.value })\n }\n placeholder=\"Display\"\n />\n \n \n {\n this.onChange({ ...metricsQuery, regex: { ...regex, enable: !regex.enable } });\n }}\n />\n \n \n ) =>\n onChange({ ...metricsQuery, regex: { ...regex, search: event.target.value } })\n }\n placeholder=\"(.*)\"\n />\n \n \n ) =>\n onChange({ ...metricsQuery, regex: { ...regex, replace: event.target.value } })\n }\n placeholder=\"$1\"\n />\n \n \n \n );\n }\n}\n","import { each, map } from 'lodash';\r\n\r\nimport {\r\n AnnotationQuery,\r\n DataFrame,\r\n FieldConfig,\r\n TimeSeries,\r\n FieldType,\r\n TimeSeriesValue,\r\n TIME_SERIES_VALUE_FIELD_NAME,\r\n TIME_SERIES_TIME_FIELD_NAME,\r\n ArrayVector,\r\n TableData,\r\n MetricFindValue,\r\n Field,\r\n toDataFrame,\r\n} from '@grafana/data';\r\n\r\nimport { PiwebapiElementPath, PiwebapiRsp, PIWebAPIQuery } from 'types';\r\n\r\nexport function parseRawQuery(tr: string): any {\r\n const splitAttributes = tr.split(';');\r\n const splitElements = splitAttributes[0].split('\\\\');\r\n\r\n // remove element hierarchy from attribute collection\r\n splitAttributes.splice(0, 1);\r\n\r\n let attributes: any[] = [];\r\n if (splitElements.length > 1 || (splitElements.length === 1 && splitElements[0] !== '')) {\r\n const elementPath: string = splitElements.join('\\\\');\r\n each(splitAttributes, function (item, index) {\r\n if (item !== '') {\r\n attributes.push({\r\n label: item,\r\n value: {\r\n value: item,\r\n expandable: false,\r\n },\r\n });\r\n }\r\n });\r\n\r\n return { attributes, elementPath };\r\n }\r\n\r\n return { attributes, elementPath: null };\r\n}\r\n\r\nexport function lowerCaseFirstLetter(string: string): string {\r\n return string.charAt(0).toLocaleLowerCase() + string.slice(1);\r\n}\r\n\r\nexport function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame {\r\n const times: number[] = [];\r\n const values: TimeSeriesValue[] = [];\r\n\r\n // Sometimes the points are sent as datapoints\r\n const points = timeSeries.datapoints;\r\n for (const point of points) {\r\n values.push(point[0]);\r\n times.push(point[1] as number);\r\n }\r\n\r\n const fields = [\r\n {\r\n name: TIME_SERIES_TIME_FIELD_NAME,\r\n type: FieldType.time,\r\n config: {},\r\n values: new ArrayVector(times),\r\n },\r\n {\r\n name: timeSeries.target ?? TIME_SERIES_VALUE_FIELD_NAME,\r\n type: FieldType.number,\r\n config: {\r\n unit: timeSeries.unit,\r\n },\r\n values: new ArrayVector(values),\r\n labels: timeSeries.tags,\r\n },\r\n ];\r\n\r\n if (timeSeries.title) {\r\n (fields[1].config as FieldConfig).displayNameFromDS = timeSeries.title;\r\n }\r\n\r\n return {\r\n name: '',\r\n refId: timeSeries.refId,\r\n meta: timeSeries.meta,\r\n fields,\r\n length: values.length,\r\n };\r\n}\r\n\r\n/**\r\n * Builds the Grafana metric segment for use on the query user interface.\r\n *\r\n * @param {any} response - response from PI Web API.\r\n * @returns - Grafana metric segment.\r\n *\r\n * @memberOf PiWebApiDatasource\r\n */\r\nexport function metricQueryTransform(response: PiwebapiRsp[]): MetricFindValue[] {\r\n return map(response, (item) => {\r\n return {\r\n text: item.Name,\r\n expandable:\r\n item.HasChildren === undefined || item.HasChildren === true || (item.Path ?? '').split('\\\\').length <= 3,\r\n HasChildren: item.HasChildren,\r\n Items: item.Items ?? [],\r\n Path: item.Path,\r\n WebId: item.WebId,\r\n } as MetricFindValue;\r\n });\r\n}\r\n\r\n/**\r\n * Check if all items are selected.\r\n *\r\n * @param {any} current the current variable selection\r\n * @return {boolean} true if all value is selected, false otherwise\r\n */\r\nexport function isAllSelected(current: any): boolean {\r\n if (!current) {\r\n return false;\r\n }\r\n if (Array.isArray(current.text)) {\r\n return current.text.indexOf('All') >= 0;\r\n }\r\n return current.text === 'All';\r\n}\r\n\r\nexport function processAnnotationQuery(annon: AnnotationQuery,data: DataFrame[]): DataFrame[] {\r\n let processedFrames: DataFrame[] = [];\r\n \r\n data.forEach((d: DataFrame) => {\r\n d.fields.forEach((f: Field) => {\r\n\r\n // check if the label has been set, if it hasn't been set then the eventframe annotation is not valid. \r\n if (!f.labels) { \r\n return \r\n }\r\n\r\n if (!('eventframe' in f.labels)) {\r\n return;\r\n }\r\n\r\n let attribute = 'attribute' in f.labels\r\n\r\n // Check whether f.values is an array or not to allow for each.\r\n // Check whether f.values is an array or not to allow for each.\r\n if (Array.isArray(f.values)) {\r\n console.log(f.values)\r\n f.values.forEach((value: any) => {\r\n\r\n if (attribute) {\r\n let annotation = value['1'].Content\r\n let valueData: any[] = []\r\n for (let i = 2; i in value; i++) {\r\n valueData.push(value[i].Content.Items)\r\n }\r\n\r\n const processedFrame = convertToTableData(annotation.Items!, valueData).map((r) => {\r\n return toDataFrame(r)});\r\n processedFrames = processedFrames.concat(processedFrame);\r\n } else {\r\n let annotation = value['1'].Content\r\n const processedFrame = convertToTableData(annotation.Items!).map((r) => {\r\n return toDataFrame(r)});\r\n processedFrames = processedFrames.concat(processedFrame);\r\n }\r\n });\r\n } \r\n });\r\n });\r\n return processedFrames;\r\n}\r\n\r\nexport function convertToTableData(items: any[], valueData?: any[]): TableData[] {\r\n console.log(\"items\",items)\r\n const response: TableData[] = items.map((item: any, index: number) => {\r\n console.log(\"item\",item)\r\n const columns = [{ text: 'StartTime' }, { text: 'EndTime' }];\r\n const rows = [item.StartTime, item.EndTime];\r\n if (valueData) {\r\n for (let attributeIndex = 0; attributeIndex < valueData.length; attributeIndex++) {\r\n let attributeData = valueData[attributeIndex]\r\n let eventframeAributeData = attributeData[index].Content.Items\r\n eventframeAributeData.forEach((attribute: any) => {\r\n columns.push({ text: attribute.Name });\r\n rows.push(String(attribute.Value.Value ? attribute.Value.Value.Name || attribute.Value.Value.Value || attribute.Value.Value : ''));\r\n });\r\n }\r\n }\r\n\r\n return {\r\n name: item.Name,\r\n columns,\r\n rows: [rows],\r\n };\r\n });\r\n console.log(\"response\",response)\r\n return response;\r\n}\r\n\r\n/**\r\n * Resolve PIWebAPI response 'value' data to value - timestamp pairs.\r\n *\r\n * @param {any} item - 'Item' object from PIWebAPI\r\n * @param {any} noDataReplacementMode - String state of how to replace 'No Data'\r\n * @param {any} grafanaDataPoint - Single Grafana value pair (value, timestamp).\r\n * @returns grafanaDataPoint - Single Grafana value pair (value, timestamp).\r\n * @returns perviousValue - {any} Grafana value (value only).\r\n *\r\n */\r\nexport function noDataReplace(\r\n item: any,\r\n noDataReplacementMode: any,\r\n grafanaDataPoint: any[]\r\n): {\r\n grafanaDataPoint: any[];\r\n previousValue: any;\r\n drop: boolean;\r\n} {\r\n let previousValue = null;\r\n let drop = false;\r\n if (!item.Good || item.Value === 'No Data' || (item.Value?.Name && item.Value?.Name === 'No Data')) {\r\n if (noDataReplacementMode === 'Drop') {\r\n drop = true;\r\n } else if (noDataReplacementMode === '0') {\r\n grafanaDataPoint[0] = 0;\r\n } else if (noDataReplacementMode === 'Keep') {\r\n // Do nothing keep\r\n } else if (noDataReplacementMode === 'Null') {\r\n grafanaDataPoint[0] = null;\r\n } else if (noDataReplacementMode === 'Previous' && previousValue !== null) {\r\n grafanaDataPoint[0] = previousValue;\r\n }\r\n } else {\r\n previousValue = item.Value;\r\n }\r\n return { grafanaDataPoint, previousValue, drop };\r\n}\r\n\r\n/**\r\n * Check if the value is a number.\r\n *\r\n * @param {any} number the value to check\r\n * @returns {boolean} true if the value is a number, false otherwise\r\n */\r\nexport function checkNumber(number: any): boolean {\r\n return typeof number === 'number' && !Number.isNaN(number) && Number.isFinite(number);\r\n}\r\n\r\n/**\r\n * Returns the last item of the element path.\r\n *\r\n * @param {string} path element path\r\n * @returns {string} last item of the element path\r\n */\r\nexport function getLastPath(path: string): string {\r\n let splitPath = path.split('|');\r\n if (splitPath.length === 0) {\r\n return '';\r\n }\r\n splitPath = splitPath[0].split('\\\\');\r\n return splitPath.length === 0 ? '' : splitPath.pop() ?? '';\r\n}\r\n\r\n/**\r\n * Returns the last item of the element path plus variable.\r\n *\r\n * @param {PiwebapiElementPath[]} elementPathArray array of element paths\r\n * @param {string} path element path\r\n * @returns {string} last item of the element path\r\n */\r\nexport function getPath(elementPathArray: PiwebapiElementPath[], path: string): string {\r\n if (!path || elementPathArray.length === 0) {\r\n return '';\r\n }\r\n const splitStr = getLastPath(path);\r\n const foundElement = elementPathArray.find((e) => path.indexOf(e.path) >= 0)?.variable;\r\n return foundElement ? foundElement + '|' + splitStr : splitStr;\r\n}\r\n\r\n/**\r\n * Replace calculation dot in expression with PI point name.\r\n *\r\n * @param {boolean} replace - is pi point and calculation.\r\n * @param {PiwebapiRsp} webid - Pi web api response object.\r\n * @param {string} url - original url.\r\n * @returns Modified url\r\n */\r\nexport function getFinalUrl(replace: boolean, webid: PiwebapiRsp, url: string) {\r\n const newUrl = replace ? url.replace(/'\\.'/g, `'${webid.Name}'`) : url;\r\n return newUrl;\r\n}\r\n","import React, { memo, useState } from 'react';\n\nimport { AnnotationQuery, QueryEditorProps, SelectableValue } from '@grafana/data';\nimport { AsyncSelect, InlineField, InlineFieldRow, InlineSwitch, Input } from '@grafana/ui';\n\nimport { PiWebAPIDatasource } from 'datasource';\nimport { PIWebAPIDataSourceJsonData, PIWebAPIQuery, PiwebapiRsp } from 'types';\n\nconst SMALL_LABEL_WIDTH = 20;\nconst LABEL_WIDTH = 30;\nconst MIN_INPUT_WIDTH = 50;\n\ntype PiWebAPIQueryEditorProps = QueryEditorProps;\n\ntype Props = PiWebAPIQueryEditorProps & {\n annotation?: AnnotationQuery;\n onAnnotationChange?: (annotation: AnnotationQuery) => void;\n};\n\nexport const PiWebAPIAnnotationsQueryEditor = memo(function PiWebAPIAnnotationQueryEditor(props: Props) {\n const { query, datasource, annotation, onChange, onRunQuery } = props;\n\n const [afWebId, setAfWebId] = useState('');\n const [database, setDatabase] = useState(annotation?.target?.database ?? {});\n\n // this should never happen, but we want to keep typescript happy\n if (annotation === undefined) {\n return null;\n }\n\n const getEventFrames = (): Promise>> => {\n return datasource.getEventFrameTemplates(database?.WebId!).then((templ: PiwebapiRsp[]) => {\n return templ.map((d) => ({ label: d.Name, value: d }));\n });\n };\n\n const getDatabases = (): Promise>> => {\n return datasource.getDatabases(afWebId).then((dbs: PiwebapiRsp[]) => {\n return dbs.map((d) => ({ label: d.Name, value: d }));\n });\n };\n\n const getValue = (key: string) => {\n const query: any = annotation.target as any;\n if (!query || !query[key]) {\n return;\n }\n return { label: query[key].Name, value: query[key] };\n };\n\n datasource.getAssetServer(datasource.afserver.name).then((result) => {\n setAfWebId(result.WebId!);\n });\n\n return (\n <>\n
\n \n \n {\n setDatabase(e.value);\n onChange({ ...query, database: e.value, template: undefined });\n }}\n defaultOptions\n />\n \n \n onChange({ ...query, template: e.value })}\n defaultOptions\n />\n \n \n onChange({ ...query, showEndTime: e.currentTarget.checked })}\n />\n \n \n \n \n onRunQuery()}\n onChange={(e) => onChange({ ...query, categoryName: e.currentTarget.value })}\n placeholder=\"Enter category name\"\n />\n \n \n onRunQuery()}\n onChange={(e) => onChange({ ...query, nameFilter: e.currentTarget.value })}\n placeholder=\"Enter name filter\"\n />\n \n \n \n \n \n onChange({\n ...query,\n regex: { ...query.regex, enable: e.currentTarget.checked },\n })\n }\n />\n \n \n onRunQuery()}\n onChange={(e) =>\n onChange({\n ...query,\n regex: { ...query.regex, search: e.currentTarget.value },\n })\n }\n placeholder=\"(.*)\"\n width={MIN_INPUT_WIDTH}\n />\n \n \n onRunQuery()}\n onChange={(e) =>\n onChange({\n ...query,\n regex: { ...query.regex, replace: e.currentTarget.value },\n })\n }\n placeholder=\"$1\"\n />\n \n \n \n \n \n onChange({\n ...query!,\n attribute: { ...query.attribute, enable: e.currentTarget.checked },\n })\n }\n />\n \n \n onRunQuery()}\n onChange={(e) =>\n onChange({\n ...query!,\n attribute: { ...query.attribute, name: e.currentTarget.value },\n })\n }\n placeholder=\"Enter name\"\n />\n \n \n
\n \n );\n});\n","import { filter, map } from 'lodash';\n\nimport { Observable, of, firstValueFrom } from 'rxjs';\n\nimport {\n DataSourceInstanceSettings,\n MetricFindValue,\n AnnotationQuery,\n ScopedVars,\n AnnotationEvent,\n DataFrame,\n} from '@grafana/data';\nimport { BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, DataSourceWithBackend } from '@grafana/runtime';\n\nimport { PIWebAPIQuery, PIWebAPIDataSourceJsonData, PiDataServer, PiwebapiInternalRsp, PiwebapiRsp } from './types';\nimport { metricQueryTransform } from 'helper';\n\nimport { PiWebAPIAnnotationsQueryEditor } from 'query/AnnotationsQueryEditor';\n\nexport class PiWebAPIDatasource extends DataSourceWithBackend {\n piserver: PiDataServer;\n afserver: PiDataServer;\n afdatabase: PiDataServer;\n piPointConfig: boolean;\n newFormatConfig: boolean;\n useUnitConfig: boolean;\n useExperimental: boolean;\n useStreaming: boolean;\n\n constructor(\n instanceSettings: DataSourceInstanceSettings,\n readonly templateSrv: TemplateSrv = getTemplateSrv(),\n private readonly backendSrv: BackendSrv = getBackendSrv()\n ) {\n super(instanceSettings);\n\n this.piserver = { name: (instanceSettings.jsonData || {}).piserver, webid: undefined };\n this.afserver = { name: (instanceSettings.jsonData || {}).afserver, webid: undefined };\n this.afdatabase = { name: (instanceSettings.jsonData || {}).afdatabase, webid: undefined };\n this.piPointConfig = instanceSettings.jsonData.pipoint || false;\n this.newFormatConfig = instanceSettings.jsonData.newFormat || false;\n this.useUnitConfig = instanceSettings.jsonData.useUnit || false;\n this.useExperimental = instanceSettings.jsonData.useExperimental || false;\n this.useStreaming = instanceSettings.jsonData.useStreaming || false;\n\n this.annotations = {\n QueryEditor: PiWebAPIAnnotationsQueryEditor,\n prepareQuery(anno: AnnotationQuery): PIWebAPIQuery | undefined {\n if (anno.target) {\n anno.target.queryType = 'Annotation';\n anno.target.isAnnotation = true;\n }\n return anno.target;\n },\n processEvents: (\n anno: AnnotationQuery,\n data: DataFrame[]\n ): Observable => {\n return of(this.eventFrameToAnnotation(anno, data));\n },\n };\n\n Promise.all([\n this.getDataServer(this.piserver.name).then((result: PiwebapiRsp) => (this.piserver.webid = result.WebId)),\n this.getAssetServer(this.afserver.name).then((result: PiwebapiRsp) => (this.afserver.webid = result.WebId)),\n this.getDatabase(\n this.afserver.name && this.afdatabase.name ? this.afserver.name + '\\\\' + this.afdatabase.name : undefined\n ).then((result: PiwebapiRsp) => (this.afdatabase.webid = result.WebId)),\n ]);\n }\n\n /**\n * This method overrides the applyTemplateVariables() method from the DataSourceWithBackend class.\n * It is responsible for replacing the template variables in the query configuration prior\n * to sending the query to the backend. Templated variables are not able to be used for alerts\n * or public facing dashboards.\n *\n * @param {PIWebAPIQuery} query - The raw query configuration from the frontend as defined in the query editor.\n * @param {ScopedVars} scopedVars - The template variables that are defined in the query editor and dashboard.\n * @returns - PIWebAPIQuery.\n *\n * @memberOf PiWebApiDatasource\n */\n applyTemplateVariables(query: PIWebAPIQuery, scopedVars: ScopedVars) {\n return {\n ...query,\n target: query.target ? this.templateSrv.replace(query.target, scopedVars) : '',\n };\n }\n\n /**\n * This method does the discovery of the AF Hierarchy and populates the query user interface segments.\n *\n * @param {any} query - Parses the query configuration and builds a PI Web API query.\n * @returns - Segment information.\n *\n * @memberOf PiWebApiDatasource\n */\n metricFindQuery(query: any, queryOptions: any): Promise {\n const ds = this;\n const querydepth = ['servers', 'databases', 'databaseElements', 'elements'];\n if (typeof query === 'string') {\n query = JSON.parse(query as string);\n }\n if (queryOptions.isPiPoint) {\n query.path = this.templateSrv.replace(query.path, queryOptions);\n } else {\n if (query.path === '') {\n query.type = querydepth[0];\n } else {\n query.path = this.templateSrv.replace(query.path, queryOptions); // replace variables in the path\n query.path = query.path.split(';')[0]; // if the attribute is in the path, let's remote it\n if (query.type !== 'attributes') {\n query.type = querydepth[Math.max(0, Math.min(query.path.split('\\\\').length, querydepth.length - 1))];\n }\n }\n query.path = query.path.replace(/\\{([^\\\\])*\\}/gi, (r: string) => r.substring(1, r.length - 2).split(',')[0]);\n }\n\n query.filter = query.filter ?? '*';\n\n if (query.type === 'servers') {\n return ds.afserver?.name\n ? ds\n .getAssetServer(ds.afserver.name)\n .then((result: PiwebapiRsp) => [result])\n .then(metricQueryTransform)\n : ds.getAssetServers().then(metricQueryTransform);\n } else if (query.type === 'databases' && !!query.afServerWebId) {\n return ds.getDatabases(query.afServerWebId, {}).then(metricQueryTransform);\n } else if (query.type === 'databases') {\n return ds\n .getAssetServer(query.path)\n .then((server) => ds.getDatabases(server.WebId ?? '', {}))\n .then(metricQueryTransform);\n } else if (query.type === 'databaseElements') {\n return ds\n .getDatabase(query.path)\n .then((db) =>\n ds.getDatabaseElements(db.WebId ?? '', {\n selectedFields: 'Items.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren',\n })\n )\n .then(metricQueryTransform);\n } else if (query.type === 'elements') {\n return ds\n .getElement(query.path)\n .then((element) =>\n ds.getElements(element.WebId ?? '', {\n selectedFields:\n 'Items.Description%3BItems.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren',\n nameFilter: query.filter,\n })\n )\n .then(metricQueryTransform);\n } else if (query.type === 'attributes') {\n return ds\n .getElement(query.path)\n .then((element) =>\n ds.getAttributes(element.WebId ?? '', {\n searchFullHierarchy: 'true',\n selectedFields:\n 'Items.Type%3BItems.DefaultUnitsName%3BItems.Description%3BItems.WebId%3BItems.Name%3BItems.Path',\n nameFilter: query.filter,\n })\n )\n .then(metricQueryTransform);\n } else if (query.type === 'dataserver') {\n return ds.getDataServers().then(metricQueryTransform);\n } else if (query.type === 'pipoint') {\n return ds.piPointSearch(query.webId, query.pointName).then(metricQueryTransform);\n }\n return Promise.reject('Bad type');\n }\n\n /** PRIVATE SECTION */\n\n /**\n * Localize the eventFrame dataFrame records to Grafana Annotations.\n * @param {any} annon - The annotation object.\n * @param {any} data - The dataframe recrords.\n * @returns - Grafana Annotation\n *\n * @memberOf PiWebApiDatasource\n */\n private eventFrameToAnnotation(annon: AnnotationQuery, data: DataFrame[]): AnnotationEvent[] {\n const annotationOptions = annon.target!;\n const events: AnnotationEvent[] = [];\n const currentLocale = Intl.DateTimeFormat().resolvedOptions().locale;\n\n data.forEach((d: DataFrame) => {\n let values = this.transformDataFrameToMap(d);\n for (let i = 0; i < values['time'].length; i++) {\n // replace Dataframe name using Regex\n let title = values['title'][i];\n if (annotationOptions.regex && annotationOptions.regex.enable) {\n title = title.replace(new RegExp(annotationOptions.regex.search), annotationOptions.regex.replace);\n }\n\n // test if timeEnd is negative and if so, set it to null\n if (values['timeEnd'][i] < 0) {\n values['timeEnd'][i] = null;\n }\n\n // format the text and localize the dates to browser locale\n let text = 'Tag: ' + title;\n if (annotationOptions.attribute && annotationOptions.attribute.enable) {\n text += values['attributeText'][i];\n }\n text += '
Start: ' + new Date(values['time'][i]).toLocaleString(currentLocale) + '
End: ';\n\n if (values['timeEnd'][i]) {\n text += new Date(values['timeEnd'][i]).toLocaleString(currentLocale);\n } else {\n text += 'Eventframe is open';\n }\n\n const event: AnnotationEvent = {\n time: values['time'][i],\n timeEnd: !!annotationOptions.showEndTime ? values['timeEnd'][i] : undefined,\n title: title,\n id: values['id'][i],\n text: text,\n tags: ['OSISoft PI'],\n };\n\n events.push(event);\n }\n });\n return events;\n }\n\n /**\n *\n */\n private transformDataFrameToMap(dataFrame: DataFrame): Record {\n const map: Record = {};\n\n dataFrame.fields.forEach((field) => {\n map[field.name] = field.values.toArray();\n });\n\n return map;\n }\n\n /**\n * Abstraction for calling the PI Web API REST endpoint\n *\n * @param {any} path - the path to append to the base server URL.\n * @returns - The full URL.\n *\n * @memberOf PiWebApiDatasource\n */\n private restGet(path: string): Promise {\n const observable = this.backendSrv.fetch({\n url: `/api/datasources/${this.id}/resources/${path}`,\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n return firstValueFrom(observable).then((response: any) => {\n return response as PiwebapiInternalRsp;\n });\n }\n\n // Get a list of all data (PI) servers\n private getDataServers(): Promise {\n return this.restGet('/dataservers').then((response) => response.data.Items ?? []);\n }\n private getDataServer(name: string | undefined): Promise {\n if (!name) {\n return Promise.resolve({});\n }\n return this.restGet('/dataservers?name=' + name).then((response) => response.data);\n }\n // Get a list of all asset (AF) servers\n private getAssetServers(): Promise {\n return this.restGet('/assetservers').then((response) => response.data.Items ?? []);\n }\n getAssetServer(name: string | undefined): Promise {\n if (!name) {\n return Promise.resolve({});\n }\n return this.restGet('/assetservers?path=\\\\\\\\' + name).then((response) => response.data);\n }\n getDatabase(path: string | undefined): Promise {\n if (!path) {\n return Promise.resolve({});\n }\n return this.restGet('/assetdatabases?path=\\\\\\\\' + path).then((response) => response.data);\n }\n getDatabases(serverId: string, options?: any): Promise {\n if (!serverId) {\n return Promise.resolve([]);\n }\n return this.restGet('/assetservers/' + serverId + '/assetdatabases').then((response) => response.data.Items ?? []);\n }\n getElement(path: string): Promise {\n if (!path) {\n return Promise.resolve({});\n }\n return this.restGet('/elements?path=\\\\\\\\' + path).then((response) => response.data);\n }\n getEventFrameTemplates(databaseId: string): Promise {\n if (!databaseId) {\n return Promise.resolve([]);\n }\n return this.restGet(\n '/assetdatabases/' + databaseId + '/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId'\n ).then((response) => {\n return filter(response.data.Items ?? [], (item) => item.InstanceType === 'EventFrame');\n });\n }\n getElementTemplates(databaseId: string): Promise {\n if (!databaseId) {\n return Promise.resolve([]);\n }\n return this.restGet(\n '/assetdatabases/' + databaseId + '/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId'\n ).then((response) => {\n return filter(response.data.Items ?? [], (item) => item.InstanceType === 'Element');\n });\n }\n\n /**\n * @description\n * Get the child attributes of the current resource.\n * GET attributes/{webId}/attributes\n * @param {string} elementId - The ID of the parent resource. See WebID for more information.\n * @param {Object} options - Query Options\n * @param {string} options.nameFilter - The name query string used for finding attributes. The default is no filter. See Query String for more information.\n * @param {string} options.categoryName - Specify that returned attributes must have this category. The default is no category filter.\n * @param {string} options.templateName - Specify that returned attributes must be members of this template. The default is no template filter.\n * @param {string} options.valueType - Specify that returned attributes' value type must be the given value type. The default is no value type filter.\n * @param {string} options.searchFullHierarchy - Specifies if the search should include attributes nested further than the immediate attributes of the searchRoot. The default is 'false'.\n * @param {string} options.sortField - The field or property of the object used to sort the returned collection. The default is 'Name'.\n * @param {string} options.sortOrder - The order that the returned collection is sorted. The default is 'Ascending'.\n * @param {string} options.startIndex - The starting index (zero based) of the items to be returned. The default is 0.\n * @param {string} options.showExcluded - Specified if the search should include attributes with the Excluded property set. The default is 'false'.\n * @param {string} options.showHidden - Specified if the search should include attributes with the Hidden property set. The default is 'false'.\n * @param {string} options.maxCount - The maximum number of objects to be returned per call (page size). The default is 1000.\n * @param {string} options.selectedFields - List of fields to be returned in the response, separated by semicolons (;). If this parameter is not specified, all available fields will be returned. See Selected Fields for more information.\n */\n private getAttributes(elementId: string, options: any): Promise {\n let querystring =\n '?' +\n map(options, (value, key) => {\n return key + '=' + value;\n }).join('&');\n\n if (querystring === '?') {\n querystring = '';\n }\n\n return this.restGet('/elements/' + elementId + '/attributes' + querystring).then(\n (response) => response.data.Items ?? []\n );\n }\n\n /**\n * @description\n * Retrieve elements based on the specified conditions. By default, this method selects immediate children of the current resource.\n * Users can search for the elements based on specific search parameters. If no parameters are specified in the search, the default values for each parameter will be used and will return the elements that match the default search.\n * GET assetdatabases/{webId}/elements\n * @param {string} databaseId - The ID of the parent resource. See WebID for more information.\n * @param {Object} options - Query Options\n * @param {string} options.webId - The ID of the resource to use as the root of the search. See WebID for more information.\n * @param {string} options.nameFilter - The name query string used for finding objects. The default is no filter. See Query String for more information.\n * @param {string} options.categoryName - Specify that returned elements must have this category. The default is no category filter.\n * @param {string} options.templateName - Specify that returned elements must have this template or a template derived from this template. The default is no template filter.\n * @param {string} options.elementType - Specify that returned elements must have this type. The default type is 'Any'. See Element Type for more information.\n * @param {string} options.searchFullHierarchy - Specifies if the search should include objects nested further than the immediate children of the searchRoot. The default is 'false'.\n * @param {string} options.sortField - The field or property of the object used to sort the returned collection. The default is 'Name'.\n * @param {string} options.sortOrder - The order that the returned collection is sorted. The default is 'Ascending'.\n * @param {number} options.startIndex - The starting index (zero based) of the items to be returned. The default is 0.\n * @param {number} options.maxCount - The maximum number of objects to be returned per call (page size). The default is 1000.\n * @param {string} options.selectedFields - List of fields to be returned in the response, separated by semicolons (;). If this parameter is not specified, all available fields will be returned. See Selected Fields for more information.\n */\n private getDatabaseElements(databaseId: string, options: any): Promise {\n let querystring =\n '?' +\n map(options, (value, key) => {\n return key + '=' + value;\n }).join('&');\n\n if (querystring === '?') {\n querystring = '';\n }\n\n return this.restGet('/assetdatabases/' + databaseId + '/elements' + querystring).then(\n (response) => response.data.Items ?? []\n );\n }\n\n /**\n * @description\n * Retrieve elements based on the specified conditions. By default, this method selects immediate children of the current resource.\n * Users can search for the elements based on specific search parameters. If no parameters are specified in the search, the default values for each parameter will be used and will return the elements that match the default search.\n * GET elements/{webId}/elements\n * @param {string} databaseId - The ID of the resource to use as the root of the search. See WebID for more information.\n * @param {Object} options - Query Options\n * @param {string} options.webId - The ID of the resource to use as the root of the search. See WebID for more information.\n * @param {string} options.nameFilter - The name query string used for finding objects. The default is no filter. See Query String for more information.\n * @param {string} options.categoryName - Specify that returned elements must have this category. The default is no category filter.\n * @param {string} options.templateName - Specify that returned elements must have this template or a template derived from this template. The default is no template filter.\n * @param {string} options.elementType - Specify that returned elements must have this type. The default type is 'Any'. See Element Type for more information.\n * @param {string} options.searchFullHierarchy - Specifies if the search should include objects nested further than the immediate children of the searchRoot. The default is 'false'.\n * @param {string} options.sortField - The field or property of the object used to sort the returned collection. The default is 'Name'.\n * @param {string} options.sortOrder - The order that the returned collection is sorted. The default is 'Ascending'.\n * @param {number} options.startIndex - The starting index (zero based) of the items to be returned. The default is 0.\n * @param {number} options.maxCount - The maximum number of objects to be returned per call (page size). The default is 1000.\n * @param {string} options.selectedFields - List of fields to be returned in the response, separated by semicolons (;). If this parameter is not specified, all available fields will be returned. See Selected Fields for more information.\n */\n private getElements(elementId: string, options: any): Promise {\n let querystring =\n '?' +\n map(options, (value, key) => {\n return key + '=' + value;\n }).join('&');\n\n if (querystring === '?') {\n querystring = '';\n }\n\n return this.restGet('/elements/' + elementId + '/elements' + querystring).then(\n (response) => response.data.Items ?? []\n );\n }\n\n /**\n * Retrieve a list of points on a specified Data Server.\n *\n * @param {string} serverId - The ID of the server. See WebID for more information.\n * @param {string} nameFilter - A query string for filtering by point name. The default is no filter. *, ?, [ab], [!ab]\n */\n private piPointSearch(serverId: string, nameFilter: string): Promise {\n let filter1 = this.templateSrv.replace(nameFilter);\n let filter2 = `${filter1}`;\n let doFilter = false;\n if (filter1 !== nameFilter) {\n const regex = /\\{(\\w|,)+\\}/gs;\n let m;\n while ((m = regex.exec(filter1)) !== null) {\n // This is necessary to avoid infinite loops with zero-width matches\n if (m.index === regex.lastIndex) {\n regex.lastIndex++;\n }\n\n // The result can be accessed through the `m`-variable.\n m.forEach((match, groupIndex) => {\n if (groupIndex === 0) {\n filter1 = filter1.replace(match, match.replace('{', '(').replace('}', ')').replace(',', '|'));\n filter2 = filter2.replace(match, '*');\n doFilter = true;\n }\n });\n }\n }\n return this.restGet('/dataservers/' + serverId + '/points?maxCount=50&nameFilter=' + filter2).then((results) => {\n if (!!results && !!results.data?.Items) {\n return doFilter ? results.data.Items.filter((item) => item.Name?.match(filter1)) : results.data.Items;\n }\n return [];\n });\n }\n}\n","import { DataSourcePlugin } from '@grafana/data';\nimport { PIWebAPIConfigEditor } from './config/ConfigEditor';\nimport { PIWebAPIQueryEditor } from './query/QueryEditor';\nimport { PiWebAPIDatasource } from './datasource';\nimport { PIWebAPIQuery, PIWebAPIDataSourceJsonData } from './types';\n\nexport const plugin = new DataSourcePlugin(\n PiWebAPIDatasource\n)\n .setQueryEditor(PIWebAPIQueryEditor)\n .setConfigEditor(PIWebAPIConfigEditor);\n"],"names":["module","exports","__WEBPACK_EXTERNAL_MODULE__781__","__WEBPACK_EXTERNAL_MODULE__531__","__WEBPACK_EXTERNAL_MODULE__7__","__WEBPACK_EXTERNAL_MODULE__241__","__WEBPACK_EXTERNAL_MODULE__959__","__WEBPACK_EXTERNAL_MODULE__269__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","value","FormField","LegacyForms","coerceOptions","options","jsonData","url","PIWebAPIConfigEditor","PureComponent","render","originalOptions","this","props","div","DataSourceHttpSettings","defaultUrl","dataSourceConfig","onChange","onHttpOptionsChange","showAccessOptions","h3","className","InlineField","label","labelWidth","InlineSwitch","pipoint","onPiPointChange","newFormat","onNewFormatChange","useUnit","onUseUnitChange","useExperimental","onUseExperimentalChange","useStreaming","onUseStreamingChange","inputWidth","onPIServerChange","piserver","placeholder","onAFServerChange","afserver","onAFDatabaseChange","afdatabase","event","onOptionsChange","target","checked","QueryField","tooltip","children","InlineFormLabel","width","QueryRowTerminator","QueryInlineField","QueryEditorRow","QueryRawInlineField","QueryRawEditorRow","defaultQuery","attributes","segments","regex","enable","summary","types","basis","interval","nodata","expression","interpolate","useLastValue","recordedValues","digitalStates","enableStreaming","isPiPoint","QueryEditorModeSwitcher","isRaw","isModalOpen","setModalOpen","useState","useEffect","Button","aria-label","icon","variant","type","onClick","ConfirmModal","isOpen","title","body","confirmText","dismissText","onConfirm","onDismiss","LABEL_WIDTH","MIN_ATTR_INPUT_WIDTH","REMOVE_LABEL","CustomLabelComponent","Icon","name","PIWebAPIQueryEditor","isValueEmpty","length","calcBasisValueChanged","segment","metricsQuery","query","getCalcBasisSegments","map","calculationBasis","item","expandable","calcNoDataValueChanged","getNoDataSegments","noDataReplacement","onSummaryValueChanged","index","summaries","state","slice","splice","setState","stateCallback","getSummarySegments","summaryTypes","filter","s","indexOf","unshift","removeSummary","part","onSummaryAction","selectableValue","push","summarySegment","removeAttribute","attributeChangeValue","onAttributeAction","getSegmentPathUpTo","arr","reduce","result","startsWith","checkAttributeSegments","data","datasource","ctrl","findQuery","path","metricFindQuery","assign","request","scopedVars","then","attributesResponse","validAttributes","each","attribute","Path","substring","WebId","filteredAttributes","attrib","changedValue","templateSrv","replace","availableAttributes","catch","err","error","message","checkPiPointSegments","webId","getSelectedPIServer","pointName","webID","piServer","forEach","parts","split","text","textEditorChanged","splitAttributes","splitElements","_","match","getElementSegments","elements","updateArray","rawQuery","checkAfServer","queryProps","onRunQuery","defaults","display","piPointConfig","onIsPiPointChange","InlineFieldRow","grow","Input","onBlur","SegmentAsync","Component","onSegmentChange","loadOptions","allowCustomValue","inputMinWidth","disabled","getAttributeSegmentsPI","reloadOptionsOnChange","Segment","onAttributeChange","getAttributeSegmentsAF","attributeSegment","useUnitConfig","maxNumber","parseInt","search","constructor","super","calculationBasisSegment","noDataReplacementSegment","segmentChangeValue","remove","currentSegment","afServerWebId","Promise","resolve","items","altSegments","variables","getVariables","variable","attributeText","forOwn","val","buildFromTarget","segmentsArray","attributesArray","summariesArray","cb","scopedVarsDone","componentDidMount","initialLoad","componentDidUpdate","force","_segmentsArray","e","console","elementPath","join","queryChange","bind","metricQueryTransform","response","Name","HasChildren","Items","PiWebAPIAnnotationsQueryEditor","memo","annotation","afWebId","setAfWebId","database","setDatabase","getValue","getAssetServer","AsyncSelect","getDatabases","dbs","loadingMessage","template","defaultOptions","getEventFrameTemplates","templ","showEndTime","currentTarget","categoryName","nameFilter","PiWebAPIDatasource","DataSourceWithBackend","applyTemplateVariables","queryOptions","ds","querydepth","JSON","parse","Math","max","min","getAssetServers","server","getDatabase","db","getDatabaseElements","selectedFields","getElement","element","getElements","getAttributes","searchFullHierarchy","getDataServers","piPointSearch","reject","annon","annotationOptions","events","currentLocale","Intl","DateTimeFormat","resolvedOptions","locale","values","transformDataFrameToMap","i","RegExp","Date","toLocaleString","time","timeEnd","id","tags","dataFrame","fields","field","toArray","observable","backendSrv","fetch","method","headers","firstValueFrom","restGet","getDataServer","serverId","databaseId","InstanceType","getElementTemplates","elementId","querystring","filter1","filter2","doFilter","m","exec","lastIndex","groupIndex","results","instanceSettings","getTemplateSrv","getBackendSrv","newFormatConfig","webid","annotations","QueryEditor","prepareQuery","anno","queryType","isAnnotation","processEvents","of","eventFrameToAnnotation","all","plugin","DataSourcePlugin","setQueryEditor","setConfigEditor"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"module.js","mappings":"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","sources":["webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"@grafana/data\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"@grafana/runtime\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"@grafana/ui\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"lodash\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"react\"","webpack://gridprotectionalliance-osisoftpi-datasource/external amd \"rxjs\"","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/bootstrap","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/compat get default export","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/define property getters","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/hasOwnProperty shorthand","webpack://gridprotectionalliance-osisoftpi-datasource/webpack/runtime/make namespace object","webpack://gridprotectionalliance-osisoftpi-datasource/./config/ConfigEditor.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./components/Forms.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./types.ts","webpack://gridprotectionalliance-osisoftpi-datasource/./components/QueryEditorModeSwitcher.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./query/QueryEditor.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./helper.ts","webpack://gridprotectionalliance-osisoftpi-datasource/./query/AnnotationsQueryEditor.tsx","webpack://gridprotectionalliance-osisoftpi-datasource/./datasource.ts","webpack://gridprotectionalliance-osisoftpi-datasource/./module.ts"],"sourcesContent":["module.exports = __WEBPACK_EXTERNAL_MODULE__781__;","module.exports = __WEBPACK_EXTERNAL_MODULE__531__;","module.exports = __WEBPACK_EXTERNAL_MODULE__7__;","module.exports = __WEBPACK_EXTERNAL_MODULE__241__;","module.exports = __WEBPACK_EXTERNAL_MODULE__959__;","module.exports = __WEBPACK_EXTERNAL_MODULE__269__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import React, { ChangeEvent, PureComponent } from 'react';\nimport { LegacyForms, DataSourceHttpSettings, InlineField, InlineSwitch } from '@grafana/ui';\nimport { DataSourcePluginOptionsEditorProps, DataSourceJsonData, DataSourceSettings } from '@grafana/data';\nimport { PIWebAPIDataSourceJsonData } from '../types';\n\nconst { FormField } = LegacyForms;\n\ninterface Props extends DataSourcePluginOptionsEditorProps {}\n\nconst coerceOptions = (\n options: DataSourceSettings\n): DataSourceSettings => {\n return {\n ...options,\n jsonData: {\n ...options.jsonData,\n url: options.url,\n },\n };\n};\n\ninterface State {}\n\nexport class PIWebAPIConfigEditor extends PureComponent {\n onPIServerChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n piserver: event.target.value,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onAFServerChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n afserver: event.target.value,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onAFDatabaseChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n afdatabase: event.target.value,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onHttpOptionsChange = (options: DataSourceSettings) => {\n const { onOptionsChange } = this.props;\n onOptionsChange(coerceOptions(options));\n };\n\n onPiPointChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n piserver: event.target.checked ? options.jsonData.piserver : '',\n pipoint: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onNewFormatChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n newFormat: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onUseUnitChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n useUnit: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onUseExperimentalChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n useExperimental : event.target.checked,\n useStreaming : event.target.checked ? options.jsonData.useStreaming : false,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n onUseStreamingChange = (event: ChangeEvent) => {\n const { onOptionsChange, options } = this.props;\n const jsonData = {\n ...options.jsonData,\n useStreaming: event.target.checked,\n };\n onOptionsChange({ ...options, jsonData });\n };\n\n render() {\n const { options: originalOptions } = this.props;\n const options = coerceOptions(originalOptions);\n\n return (\n
\n \n\n

Custom Configuration

\n\n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n {options.jsonData.useExperimental && (\n
\n \n \n \n
\n )}\n
\n\n

PI/AF Connection Details

\n\n
\n {options.jsonData.pipoint && (\n
\n \n
\n )}\n
\n \n
\n
\n \n
\n
\n
\n );\n }\n}\n","import React, { InputHTMLAttributes, FunctionComponent } from 'react';\nimport { InlineFormLabel } from '@grafana/ui';\n\nexport interface Props extends InputHTMLAttributes {\n label: string;\n tooltip?: string;\n labelWidth?: number;\n children?: React.ReactNode;\n queryEditor?: JSX.Element;\n}\n\nexport const QueryField: FunctionComponent> = ({ label, labelWidth = 12, tooltip, children }) => (\n <>\n \n {label}\n \n {children}\n \n);\n\nexport const QueryRowTerminator = () => {\n return (\n
\n
\n
\n );\n};\n\nexport const QueryInlineField = ({ ...props }) => {\n return (\n \n \n \n );\n};\n\nexport const QueryEditorRow = (props: Partial) => {\n return (\n
\n {props.children}\n \n
\n );\n};\n\nexport const QueryRawInlineField = ({ ...props }) => {\n return (\n \n \n \n );\n};\n\nexport const QueryRawEditorRow = (props: Partial) => {\n return <>{props.children};\n};\n","import { DataQuery } from '@grafana/schema';\nimport { DataSourceJsonData } from '@grafana/data';\n\nexport interface PiwebapiElementPath {\n path: string;\n variable: string;\n}\n\nexport interface PiwebapiInternalRsp {\n data: PiwebapiRsp;\n status: number;\n url: string;\n}\n\nexport interface PiwebapiRsp {\n Name?: string;\n InstanceType?: string;\n Items?: PiwebapiRsp[];\n WebId?: string;\n HasChildren?: boolean;\n Type?: string;\n DefaultUnitsName?: string;\n Description?: string;\n Path?: string;\n}\n\nexport interface PiDataServer {\n name: string | undefined;\n webid: string | undefined;\n}\n\nexport interface PIWebAPISelectableValue {\n webId?: string;\n value?: string;\n type?: string;\n expandable?: boolean;\n}\n\nexport interface PIWebAPIAnnotationsQuery extends DataQuery {\n target: string;\n}\n\nexport interface PIWebAPIQuery extends DataQuery {\n target?: string;\n elementPath?: string;\n attributes?: any[];\n segments?: any[];\n isPiPoint?: boolean;\n isAnnotation?: boolean;\n webid?: string;\n webids?: string[];\n display?: any;\n interpolate?: any;\n recordedValues?: any;\n digitalStates?: any;\n enableStreaming: any;\n useLastValue?: any;\n useUnit?: any;\n regex?: any;\n summary?: any;\n expression?: string;\n rawQuery?: boolean;\n query?: string;\n // annotations items\n database?: PiwebapiRsp;\n template?: PiwebapiRsp;\n showEndTime?: boolean;\n attribute?: any;\n nameFilter?: string;\n categoryName?: string;\n}\n\nexport const defaultQuery: Partial = {\n target: ';',\n attributes: [],\n segments: [],\n regex: { enable: false },\n summary: { types: [], basis: 'EventWeighted', interval: '', nodata: 'Null' },\n expression: '',\n interpolate: { enable: false },\n useLastValue: { enable: false },\n recordedValues: { enable: false },\n digitalStates: { enable: false },\n enableStreaming: { enable: false },\n useUnit: { enable: false },\n isPiPoint: false,\n};\n\n/**\n * These are options configured for each DataSource instance\n */\nexport interface PIWebAPIDataSourceJsonData extends DataSourceJsonData {\n url?: string;\n access?: string;\n piserver?: string;\n afserver?: string;\n afdatabase?: string;\n pipoint?: boolean;\n newFormat?: boolean;\n useUnit?: boolean;\n useExperimental?: boolean;\n useStreaming?: boolean;\n}\n\n/**\n * Value that is used in the backend, but never sent over HTTP to the frontend\n */\nexport interface PIWebAPISecureJsonData {\n apiKey?: string;\n}\n","import React, { useEffect, useState } from 'react';\nimport { Button, ConfirmModal } from '@grafana/ui';\n\ntype Props = {\n isRaw: boolean;\n onChange: (newIsRaw: boolean) => void;\n};\n\nexport const QueryEditorModeSwitcher = ({ isRaw, onChange }: Props): JSX.Element => {\n const [isModalOpen, setModalOpen] = useState(false);\n\n useEffect(() => {\n // if the isRaw changes, we hide the modal\n setModalOpen(false);\n }, [isRaw]);\n\n if (isRaw) {\n return (\n <>\n {\n // we show the are-you-sure modal\n setModalOpen(true);\n }}\n >\n {\n onChange(false);\n }}\n onDismiss={() => {\n setModalOpen(false);\n }}\n />\n \n );\n } else {\n return (\n {\n onChange(true);\n }}\n >\n );\n }\n};\n","import { each, filter, forOwn, join, reduce, map, slice, remove, defaults } from 'lodash';\n\nimport React, { PureComponent, ChangeEvent } from 'react';\nimport { Icon, InlineField, InlineFieldRow, InlineSwitch, Input, SegmentAsync, Segment } from '@grafana/ui';\nimport { QueryEditorProps, SelectableValue, TypedVariableModel } from '@grafana/data';\n\nimport { PiWebAPIDatasource } from '../datasource';\nimport { QueryInlineField, QueryRawInlineField, QueryRowTerminator } from '../components/Forms';\nimport { PIWebAPISelectableValue, PIWebAPIDataSourceJsonData, PIWebAPIQuery, defaultQuery } from '../types';\nimport { QueryEditorModeSwitcher } from 'components/QueryEditorModeSwitcher';\n\nconst LABEL_WIDTH = 24;\nconst MIN_ELEM_INPUT_WIDTH = 200;\nconst MIN_ATTR_INPUT_WIDTH = 250;\n\ninterface State {\n isPiPoint: boolean;\n segments: Array>;\n attributes: Array>;\n summaries: Array>;\n attributeSegment: SelectableValue;\n summarySegment: SelectableValue;\n calculationBasisSegment: SelectableValue;\n noDataReplacementSegment: SelectableValue;\n}\n\ntype Props = QueryEditorProps;\n\nconst REMOVE_LABEL = '-REMOVE-';\n\nconst CustomLabelComponent = (props: any) => {\n if (props.value) {\n return (\n
\n {props.label ?? '--no label--'}\n
\n );\n }\n return (\n \n \n \n );\n};\n\nexport class PIWebAPIQueryEditor extends PureComponent {\n error: any;\n piServer: any[] = [];\n availableAttributes: any = {};\n summaryTypes: string[];\n calculationBasis: string[];\n noDataReplacement: string[];\n state: State = {\n isPiPoint: false,\n segments: [],\n attributes: [],\n summaries: [],\n attributeSegment: {},\n summarySegment: {},\n calculationBasisSegment: {},\n noDataReplacementSegment: {},\n };\n\n constructor(props: any) {\n super(props);\n this.onSegmentChange = this.onSegmentChange.bind(this);\n this.calcBasisValueChanged = this.calcBasisValueChanged.bind(this);\n this.calcNoDataValueChanged = this.calcNoDataValueChanged.bind(this);\n this.onSummaryAction = this.onSummaryAction.bind(this);\n this.onSummaryValueChanged = this.onSummaryValueChanged.bind(this);\n this.onAttributeAction = this.onAttributeAction.bind(this);\n this.onAttributeChange = this.onAttributeChange.bind(this);\n\n this.summaryTypes = [\n // 'None', // A summary type is not specified.\n 'Total', // A totalization over the time range.\n 'Average', // The average value over the time range.\n 'Minimum', // The minimum value over the time range.\n 'Maximum', // The maximum value over the time range.\n 'Range', // The range value over the time range (minimum-maximum).\n 'StdDev', // The standard deviation over the time range.\n 'PopulationStdDev', // The population standard deviation over the time range.\n 'Count', // The sum of event count over the time range when calculation basis is event weighted. The sum of event time duration over the time range when calculation basis is time weighted.\n 'PercentGood', // Percent of data with good value during the calculation period. For time weighted calculations, the percentage is based on time. For event weighted calculations, the percent is based on event count.\n 'All', // A convenience for requesting all available summary calculations.\n 'AllForNonNumeric', // A convenience for requesting all available summary calculations for non-numeric data.\n ];\n\n this.calculationBasis = [\n 'TimeWeighted', // Weight the values in the calculation by the time over which they apply. Interpolation is based on whether the attribute is stepped. Interpolated events are generated at the boundaries if necessary.\n 'EventWeighted', // Evaluate values with equal weighting for each event. No interpolation is done. There must be at least one event within the time range to perform a successful calculation. Two events are required for standard deviation. In handling events at the boundary of the calculation, the AFSDK uses following rules:\n 'TimeWeightedContinuous', // Apply weighting as in TimeWeighted, but do all interpolation between values as if they represent continuous data, (standard interpolation) regardless of whether the attribute is stepped.\n 'TimeWeightedDiscrete', // Apply weighting as in TimeWeighted but interpolation between values is performed as if they represent discrete, unrelated values (stair step plot) regardless of the attribute is stepped.\n 'EventWeightedExcludeMostRecentEvent', // The calculation behaves the same as _EventWeighted_, except in the handling of events at the boundary of summary intervals in a multiple intervals calculation. Use this option to prevent events at the intervals boundary from being double count at both intervals. With this option, events at the end time (most recent time) of an interval is not used in that interval.\n 'EventWeightedExcludeEarliestEvent', // Similar to the option _EventWeightedExcludeMostRecentEvent_. Events at the start time(earliest time) of an interval is not used in that interval.\n 'EventWeightedIncludeBothEnds', // Events at both ends of the interval boundaries are included in the event weighted calculation.\n ];\n\n this.noDataReplacement = [\n 'Null', // replace with nulls\n 'Drop', // drop items\n 'Previous', // use previous value if available\n '0', // replace with 0\n 'Keep', // Keep value\n ];\n }\n\n // is selected segment empty\n isValueEmpty(value: PIWebAPISelectableValue | undefined) {\n return !value || !value.value || !value.value.length || value.value === REMOVE_LABEL;\n }\n\n segmentChangeValue = (segments: Array>) => {\n const query = this.props.query;\n this.setState({ segments }, () => this.onChange({ ...query, segments }));\n };\n\n attributeChangeValue = (attributes: Array>) => {\n const query = this.props.query;\n this.setState({ attributes }, () => this.onChange({ ...query, attributes }));\n };\n\n // summary calculation basis change event\n calcBasisValueChanged(segment: SelectableValue) {\n const metricsQuery = this.props.query as PIWebAPIQuery;\n const summary = metricsQuery.summary;\n summary.basis = segment.value?.value;\n this.onChange({ ...metricsQuery, summary });\n }\n // get summary calculation basis user interface segments\n getCalcBasisSegments() {\n const segments = map(this.calculationBasis, (item: string) => {\n let selectableValue: SelectableValue = {\n label: item,\n value: {\n value: item,\n expandable: true,\n },\n };\n return selectableValue;\n });\n return segments;\n }\n\n // no data change event\n calcNoDataValueChanged(segment: SelectableValue) {\n const metricsQuery = this.props.query as PIWebAPIQuery;\n const summary = metricsQuery.summary;\n summary.nodata = segment.value?.value;\n this.onChange({ ...metricsQuery, summary });\n }\n // get no data user interface segments\n getNoDataSegments() {\n const segments = map(this.noDataReplacement, (item: string) => {\n let selectableValue: SelectableValue = {\n label: item,\n value: {\n value: item,\n expandable: true,\n },\n };\n return selectableValue;\n });\n return segments;\n }\n\n // summary query change event\n onSummaryValueChanged(item: SelectableValue, index: number) {\n const summaries = this.state.summaries.slice(0) as Array>;\n summaries[index] = item;\n if (this.isValueEmpty(item.value)) {\n summaries.splice(index, 1);\n }\n this.setState({ summaries }, this.stateCallback);\n }\n // get the list of summaries available\n getSummarySegments() {\n const summaryTypes = filter(this.summaryTypes, (type) => {\n return this.state.summaries.map((s) => s.value?.value).indexOf(type) === -1;\n });\n\n const segments = map(summaryTypes, (item: string) => {\n let selectableValue: SelectableValue = {\n label: item,\n value: {\n value: item,\n expandable: true,\n },\n };\n return selectableValue;\n });\n\n segments.unshift({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n\n return segments;\n }\n\n // remove a summary from the user interface and the query\n removeSummary(part: SelectableValue) {\n const summaries = filter(this.state.summaries, (item: SelectableValue) => {\n return item !== part;\n });\n this.setState({ summaries });\n }\n // add a new summary to the query\n onSummaryAction(item: SelectableValue) {\n const summaries = this.state.summaries.slice(0) as Array>;\n // if value is not empty, add new attribute segment\n if (!this.isValueEmpty(item.value)) {\n let selectableValue: SelectableValue = {\n label: item.label,\n value: {\n value: item.value?.value,\n expandable: true,\n },\n };\n summaries.push(selectableValue);\n }\n this.setState({ summarySegment: {}, summaries }, this.stateCallback);\n }\n\n // remove an attribute from the query\n removeAttribute(part: SelectableValue) {\n const attributes = filter(this.state.attributes, (item: SelectableValue) => {\n return item !== part;\n });\n this.attributeChangeValue(attributes);\n }\n // add an attribute to the query\n onAttributeAction(item: SelectableValue) {\n const { query } = this.props;\n const attributes = this.state.attributes.slice(0);\n // if value is not empty, add new attribute segment\n if (!this.isValueEmpty(item.value)) {\n let selectableValue: SelectableValue = {\n label: item.label,\n value: {\n value: item.value?.value,\n expandable: !query.isPiPoint,\n },\n };\n attributes.push(selectableValue);\n }\n this.attributeChangeValue(attributes);\n }\n\n // pi point change event\n onPiPointChange = (item: SelectableValue, index: number) => {\n let attributes = this.state.attributes.slice(0);\n\n if (item.label === REMOVE_LABEL) {\n remove(attributes, (value, n) => n === index);\n } else {\n // set current value\n attributes[index] = item;\n }\n\n this.checkPiPointSegments(item, attributes);\n };\n // attribute change event\n onAttributeChange = (item: SelectableValue, index: number) => {\n let attributes = this.state.attributes.slice(0);\n\n // ignore if no change\n if (attributes[index].label === item.value?.value) {\n return;\n }\n\n // set current value\n attributes[index] = item;\n\n this.checkAttributeSegments(attributes, this.state.segments);\n };\n // segment change\n onSegmentChange = (item: SelectableValue, index: number) => {\n const { query } = this.props;\n let segments = this.state.segments.slice(0);\n\n // ignore if no change\n if (segments[index].label === item.value?.value) {\n return;\n }\n\n // reset attributes list\n this.setState({ attributes: [] }, () => {\n if (item.label === REMOVE_LABEL) {\n segments = slice(segments, 0, index);\n this.checkAttributeSegments([], segments).then(() => {\n if (segments.length === 0) {\n segments.push({\n label: '',\n });\n } else if (!!segments[segments.length - 1].value?.expandable) {\n segments.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n if (query.isPiPoint) {\n this.piServer = [];\n }\n this.segmentChangeValue(segments);\n });\n return;\n }\n\n // set current value\n segments[index] = item;\n\n // Accept only one PI server\n if (query.isPiPoint) {\n this.piServer.push(item);\n this.segmentChangeValue(segments);\n return;\n }\n\n // changed internal selection\n if (index < segments.length - 1) {\n segments = slice(segments, 0, index + 1);\n }\n this.checkAttributeSegments([], segments).then(() => {\n // add new options\n if (!!item.value?.expandable) {\n segments.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n this.segmentChangeValue(segments);\n });\n });\n };\n\n // get a ui segment for the attributes\n getElementSegments = (\n index: number,\n currentSegment?: Array>\n ): Promise>> => {\n const { datasource, query, data } = this.props;\n const ctrl = this;\n const findQuery = query.isPiPoint\n ? { type: 'dataserver' }\n : {\n path: this.getSegmentPathUpTo(currentSegment ?? this.state.segments.slice(0), index),\n afServerWebId: this.state.segments.length > 0 && this.state.segments[0].value ? this.state.segments[0].value.webId : undefined,\n };\n\n if (!query.isPiPoint) {\n if (datasource.afserver?.name && index === 0) {\n return Promise.resolve([\n {\n label: datasource.afserver.name,\n value: {\n value: datasource.afserver.name,\n expandable: true,\n },\n },\n ]);\n }\n if (datasource.afserver?.name && datasource.afdatabase?.name && index === 1) {\n return Promise.resolve([\n {\n label: datasource.afdatabase.name,\n value: {\n value: datasource.afdatabase.name,\n expandable: true,\n },\n },\n ]);\n }\n }\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: query.isPiPoint }))\n .then((items: any[]) => {\n const altSegments = map(items, (item: any) => {\n let selectableValue: SelectableValue = {\n label: item.text,\n value: {\n webId: item.WebId,\n value: item.text,\n expandable: !query.isPiPoint && item.expandable,\n },\n };\n return selectableValue;\n });\n\n if (altSegments.length === 0) {\n return altSegments;\n }\n\n // add template variables\n const variables = datasource.templateSrv.getVariables();\n each(variables, (variable: TypedVariableModel) => {\n let selectableValue: SelectableValue = {\n label: '${' + variable.name + '}',\n value: {\n type: 'template',\n value: '${' + variable.name + '}',\n expandable: !query.isPiPoint,\n },\n };\n altSegments.unshift(selectableValue);\n });\n\n altSegments.unshift({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n\n return altSegments;\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n return [];\n });\n };\n\n // get the list of attributes for the user interface - PI\n getAttributeSegmentsPI = (attributeText?: string): Promise>> => {\n const { datasource, query, data } = this.props;\n const ctrl = this;\n const findQuery = {\n path: '',\n webId: this.getSelectedPIServer(),\n pointName: (attributeText ?? '') + '*',\n type: 'pipoint',\n };\n let segments: Array> = [];\n segments.push({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: query.isPiPoint }))\n .then((items: any[]) => {\n segments = map(items, (item: any) => {\n let selectableValue: SelectableValue = {\n path: item.Path,\n label: item.text,\n value: {\n value: item.text,\n expandable: false,\n },\n };\n return selectableValue;\n });\n if (!!attributeText && attributeText.length > 0) {\n segments.unshift({\n label: attributeText,\n value: {\n value: attributeText,\n expandable: false,\n },\n });\n }\n // add template variables\n const variables = datasource.templateSrv.getVariables();\n each(variables, (variable: TypedVariableModel) => {\n let selectableValue: SelectableValue = {\n label: '${' + variable.name + '}',\n value: {\n type: 'template',\n value: '${' + variable.name + '}',\n expandable: !query.isPiPoint,\n },\n };\n segments.unshift(selectableValue);\n });\n return segments;\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n return segments;\n });\n };\n\n // get the list of attributes for the user interface - AF\n getAttributeSegmentsAF = (attributeText?: string): Array> => {\n const ctrl = this;\n let segments: Array> = [];\n\n segments.push({\n label: REMOVE_LABEL,\n value: {\n value: REMOVE_LABEL,\n },\n });\n\n forOwn(ctrl.availableAttributes, (val: any, key: string) => {\n let selectableValue: SelectableValue = {\n label: key,\n value: {\n value: key,\n expandable: true,\n },\n };\n segments.push(selectableValue);\n });\n\n return segments;\n };\n\n // build data from target string\n buildFromTarget = (\n query: PIWebAPIQuery,\n segmentsArray: Array>,\n attributesArray: Array>\n ) => {\n const splitAttributes = query.target!.split(';');\n const splitElements = splitAttributes.length > 0 ? splitAttributes[0].split('\\\\') : [];\n\n if (splitElements.length > 1 || (splitElements.length === 1 && splitElements[0] !== '')) {\n // remove element hierarchy from attribute collection\n splitAttributes.splice(0, 1);\n\n each(splitElements, (item, _) => {\n segmentsArray.push({\n label: item,\n value: {\n type: item.match(/\\${\\w+}/gi) ? 'template' : undefined,\n value: item,\n expandable: true,\n },\n });\n });\n each(splitAttributes, (item, _) => {\n if (item !== '') {\n // set current value\n attributesArray.push({\n label: item,\n value: {\n value: item,\n expandable: false,\n },\n });\n }\n });\n return this.getElementSegments(splitElements.length + 1, segmentsArray).then((elements) => {\n if (elements.length > 0) {\n segmentsArray.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n return segmentsArray;\n });\n }\n return Promise.resolve(segmentsArray);\n };\n\n /**\n * Gets the segment information and parses it to a string.\n *\n * @param {any} index - Last index of segment to use.\n * @returns - AF Path or PI Point name.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n getSegmentPathUpTo(segments: Array>, index: number): string {\n const arr = segments.slice(0, index);\n\n return reduce(\n arr,\n (result: any, segment: SelectableValue) => {\n if (!segment.value) {\n return '';\n }\n if (!segment.value.value?.startsWith('-Select')) {\n return result ? result + '\\\\' + segment.value.value : segment.value.value;\n }\n return result;\n },\n ''\n );\n }\n\n /**\n * Get the current AF Element's child attributes. Validates when the element selection changes.\n *\n * @returns - Collection of attributes.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n checkAttributeSegments(\n attributes: Array>,\n segments: Array>\n ): Promise {\n const { datasource, data } = this.props;\n const ctrl = this;\n const findQuery = {\n path: this.getSegmentPathUpTo(segments.slice(0), segments.length),\n type: 'attributes',\n };\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: false }))\n .then((attributesResponse: any) => {\n const validAttributes: any = {};\n\n each(attributesResponse, (attribute: any) => {\n validAttributes[attribute.Path.substring(attribute.Path.indexOf('|') + 1)] = attribute.WebId;\n });\n\n const filteredAttributes = filter(attributes, (attrib: SelectableValue) => {\n const changedValue = datasource.templateSrv.replace(attrib.value?.value);\n return validAttributes[changedValue] !== undefined;\n });\n\n ctrl.availableAttributes = validAttributes;\n this.attributeChangeValue(filteredAttributes);\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n this.attributeChangeValue(attributes);\n });\n }\n\n /**\n * Get PI points from server.\n *\n * @returns - Collection of attributes.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n checkPiPointSegments(\n attribute: SelectableValue,\n attributes: Array>\n ) {\n const { datasource, data } = this.props;\n const ctrl = this;\n const findQuery = {\n path: attribute.path,\n webId: ctrl.getSelectedPIServer(),\n pointName: attribute.label,\n type: 'pipoint',\n };\n return datasource\n .metricFindQuery(findQuery, Object.assign(data?.request?.scopedVars ?? {}, { isPiPoint: true }))\n .then(() => {\n ctrl.attributeChangeValue(attributes);\n })\n .catch((err: any) => {\n ctrl.error = err.message || 'Failed to issue metric query';\n ctrl.attributeChangeValue([]);\n });\n }\n\n /**\n * Gets the webid of the current selected pi data server.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n getSelectedPIServer() {\n let webID = '';\n\n this.piServer.forEach((s) => {\n const parts = this.props.query.target!.split(';');\n if (parts.length >= 2) {\n if (parts[0] === s.text) {\n webID = s.WebId;\n return;\n }\n }\n });\n return this.piServer.length > 0 ? this.piServer[0].value?.webId : webID;\n }\n\n /**\n * Queries PI Web API for child elements and attributes when the raw query text editor is changed.\n *\n * @memberOf PIWebAPIQueryEditor\n */\n textEditorChanged() {\n const { query, onChange } = this.props;\n const splitAttributes = query.target!.split(';');\n const splitElements = splitAttributes.length > 0 ? splitAttributes[0].split('\\\\') : [];\n\n let segments: Array> = [];\n let attributes: Array> = [];\n\n if (splitElements.length > 1 || (splitElements.length === 1 && splitElements[0] !== '')) {\n // remove element hierarchy from attribute collection\n splitAttributes.splice(0, 1);\n\n each(splitElements, (item, _) => {\n segments.push({\n label: item,\n value: {\n type: item.match(/\\${\\w+}/gi) ? 'template' : undefined,\n value: item,\n expandable: true,\n },\n });\n });\n each(splitAttributes, function (item, index) {\n if (item !== '') {\n attributes.push({\n label: item,\n value: {\n value: item,\n expandable: false,\n },\n });\n }\n });\n this.getElementSegments(splitElements.length + 1, segments)\n .then((elements) => {\n if (elements.length > 0) {\n segments.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n }\n })\n .then(() => {\n this.updateArray(segments, attributes, this.state.summaries, query.isPiPoint!, () => {\n onChange({ ...query, query: undefined, rawQuery: false });\n });\n });\n } else {\n segments = this.checkAfServer();\n this.updateArray(segments, this.state.attributes, this.state.summaries, query.isPiPoint!, () => {\n this.onChange({\n ...query,\n query: undefined,\n rawQuery: false,\n attributes: this.state.attributes,\n segments: this.state.segments,\n });\n });\n }\n }\n\n /**\n * Check if the AF server and database are configured in the datasoure config.\n *\n * @returns the segments array\n *\n * @memberOf PIWebAPIQueryEditor\n */\n checkAfServer = () => {\n const { datasource } = this.props;\n const segmentsArray = [];\n if (datasource.afserver?.name) {\n segmentsArray.push({\n label: datasource.afserver.name,\n value: {\n value: datasource.afserver.name,\n expandable: true,\n },\n });\n if (datasource.afdatabase?.name) {\n segmentsArray.push({\n label: datasource.afdatabase.name,\n value: {\n value: datasource.afdatabase.name,\n expandable: true,\n },\n });\n }\n segmentsArray.push({\n label: 'Select Element',\n value: {\n value: '-Select Element-',\n },\n });\n } else {\n segmentsArray.push({\n label: '',\n });\n }\n return segmentsArray;\n };\n\n /**\n * Update the internal state of the datasource.\n *\n * @param segmentsArray the segments array to update\n * @param attributesArray the AF attributes array to update\n * @param summariesArray the summaries array to update\n * @param isPiPoint the is PI point flag\n * @param cb optional callback function\n *\n * @memberOf PIWebAPIQueryEditor\n */\n updateArray = (\n segmentsArray: Array>,\n attributesArray: Array>,\n summariesArray: Array>,\n isPiPoint: boolean,\n cb?: (() => void) | undefined\n ) => {\n this.setState(\n {\n segments: segmentsArray,\n attributes: attributesArray,\n summaries: summariesArray,\n isPiPoint,\n },\n () => {\n if (!isPiPoint) {\n this.checkAttributeSegments(attributesArray, this.state.segments).then(() => {\n if (cb) {\n cb();\n }\n });\n }\n }\n );\n };\n\n // React action when component is initialized/updated\n scopedVarsDone = false;\n componentDidMount = () => {\n this.initialLoad(false);\n };\n\n componentDidUpdate = () => {\n const { query } = this.props;\n if (this.props.data?.state === 'Done' && !!this.props.data?.request?.scopedVars && !this.scopedVarsDone) {\n this.scopedVarsDone = true;\n this.initialLoad(!query.isPiPoint);\n }\n };\n\n initialLoad = (force: boolean) => {\n const { query } = this.props;\n const metricsQuery = defaults(query, defaultQuery) as PIWebAPIQuery;\n const { segments, attributes, summary, isPiPoint } = metricsQuery;\n\n let segmentsArray: Array> = force ? [] : segments?.slice(0) ?? [];\n let attributesArray: Array> = force ? [] : attributes?.slice(0) ?? [];\n let summariesArray = summary?.types ?? [];\n\n if (!isPiPoint && segmentsArray.length === 0) {\n if (query.target && query.target.length > 0 && query.target !== ';') {\n attributesArray = [];\n // Build query from target\n this.buildFromTarget(query, segmentsArray, attributesArray)\n .then((_segmentsArray) => {\n this.updateArray(_segmentsArray, attributesArray, summariesArray, false);\n })\n .catch((e) => console.error(e));\n return;\n } else {\n segmentsArray = this.checkAfServer();\n }\n } else if (isPiPoint && segmentsArray.length > 0) {\n this.piServer = segmentsArray;\n }\n this.updateArray(segmentsArray, attributesArray, summariesArray, !!isPiPoint, () => {\n this.onChange(query);\n });\n };\n\n onChange = (query: PIWebAPIQuery) => {\n const { onChange, onRunQuery } = this.props;\n\n query.summary.types = this.state.summaries;\n if (query.rawQuery) {\n query.target = query.query ?? '';\n } else {\n query.elementPath = this.getSegmentPathUpTo(this.state.segments, this.state.segments.length);\n query.target =\n query.elementPath +\n ';' +\n join(\n query.attributes?.map((s) => s.value?.value),\n ';'\n );\n }\n\n onChange(query);\n\n if (query.target && query.target.length > 0) {\n onRunQuery();\n }\n };\n\n stateCallback = () => {\n const query = this.props.query as PIWebAPIQuery;\n this.onChange(query);\n };\n\n onIsPiPointChange = (event: React.SyntheticEvent) => {\n const { query: queryChange } = this.props;\n const isPiPoint = !queryChange.isPiPoint;\n this.setState(\n {\n segments: isPiPoint ? [{ label: '' }] : this.checkAfServer(),\n attributes: [],\n isPiPoint,\n },\n () => {\n this.onChange({\n ...queryChange,\n expression: '',\n attributes: this.state.attributes,\n segments: this.state.segments,\n isPiPoint,\n });\n }\n );\n };\n\n render() {\n const { query: queryProps, onChange, onRunQuery } = this.props;\n const metricsQuery = defaults(queryProps, defaultQuery) as PIWebAPIQuery;\n const {\n useLastValue,\n useUnit,\n interpolate,\n query,\n rawQuery,\n digitalStates,\n enableStreaming,\n recordedValues,\n expression,\n isPiPoint,\n summary,\n display,\n regex,\n } = metricsQuery;\n\n return (\n <>\n {this.props.datasource.piPointConfig && (\n \n \n \n )}\n\n {!!rawQuery && (\n \n \n ) =>\n onChange({ ...metricsQuery, query: event.target.value })\n }\n placeholder=\"enter query\"\n />\n \n this.textEditorChanged()} />\n \n )}\n\n {!rawQuery && (\n <>\n
\n \n {this.state.segments.map((segment: SelectableValue, index: number) => {\n return (\n }\n onChange={(item) => this.onSegmentChange(item, index)}\n loadOptions={(query?: string | undefined) => {\n return this.getElementSegments(index);\n }}\n allowCustomValue\n inputMinWidth={MIN_ELEM_INPUT_WIDTH}\n />\n );\n })}\n \n {!isPiPoint && (\n {\n onChange({ ...metricsQuery, query: metricsQuery.target, rawQuery: value });\n }}\n />\n )}\n \n
\n\n \n {this.state.attributes.map((attribute: SelectableValue, index: number) => {\n if (isPiPoint) {\n return (\n }\n disabled={this.piServer.length === 0}\n onChange={(item) => this.onPiPointChange(item, index)}\n loadOptions={this.getAttributeSegmentsPI}\n reloadOptionsOnChange\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n );\n }\n return (\n }\n disabled={this.state.segments.length <= 2}\n onChange={(item) => this.onAttributeChange(item, index)}\n options={this.getAttributeSegmentsAF()}\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n );\n })}\n\n {isPiPoint && (\n \n }\n disabled={this.piServer.length === 0}\n onChange={this.onAttributeAction}\n loadOptions={this.getAttributeSegmentsPI}\n reloadOptionsOnChange\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n )}\n {!isPiPoint && (\n \n }\n disabled={this.state.segments.length <= 2}\n onChange={this.onAttributeAction}\n options={this.getAttributeSegmentsAF()}\n allowCustomValue\n inputMinWidth={MIN_ATTR_INPUT_WIDTH}\n />\n )}\n \n \n )}\n\n \n \n \n this.onChange({\n ...metricsQuery,\n useLastValue: { ...useLastValue, enable: !useLastValue.enable },\n })\n }\n />\n \n {this.props.datasource.useUnitConfig && (\n \n \n this.onChange({\n ...metricsQuery,\n useUnit: { ...useUnit, enable: !useUnit.enable },\n })\n }\n />\n \n )}\n {this.props.datasource.useStreaming && (\n \n \n this.onChange({ ...metricsQuery, enableStreaming: { ...enableStreaming, enable: !enableStreaming.enable } })\n }\n />\n \n )}\n \n\n \n \n ) =>\n onChange({ ...metricsQuery, expression: event.target.value })\n }\n placeholder=\"'.'*2\"\n />\n \n \n\n {!useLastValue.enable && (\n <>\n \n \n ) =>\n onChange({\n ...metricsQuery,\n recordedValues: { ...recordedValues, maxNumber: parseInt(event.target.value, 10) },\n })\n }\n type=\"number\"\n placeholder=\"1000\"\n />\n \n \n \n this.onChange({\n ...metricsQuery,\n recordedValues: { ...recordedValues, enable: !recordedValues.enable },\n })\n }\n />\n \n \n \n this.onChange({\n ...metricsQuery,\n digitalStates: { ...digitalStates, enable: !digitalStates.enable },\n })\n }\n />\n \n \n\n \n \n ) =>\n onChange({ ...metricsQuery, interpolate: { ...interpolate, interval: event.target.value } })\n }\n placeholder=\"30s\"\n />\n \n \n \n this.onChange({ ...metricsQuery, interpolate: { ...interpolate, enable: !interpolate.enable } })\n }\n />\n \n \n }\n onChange={this.calcNoDataValueChanged}\n options={this.getNoDataSegments()}\n allowCustomValue\n />\n \n \n\n \n \n ) =>\n onChange({ ...metricsQuery, summary: { ...summary, interval: event.target.value } })\n }\n placeholder=\"30s\"\n />\n \n \n }\n onChange={this.calcBasisValueChanged}\n options={this.getCalcBasisSegments()}\n allowCustomValue\n />\n \n \n \n {this.state.summaries.map((s: SelectableValue, index: number) => {\n return (\n }\n onChange={(item) => this.onSummaryValueChanged(item, index)}\n options={this.getSummarySegments()}\n allowCustomValue\n />\n );\n })}\n \n }\n onChange={this.onSummaryAction}\n options={this.getSummarySegments()}\n allowCustomValue\n />\n \n \n \n \n )}\n\n \n \n ) =>\n onChange({ ...metricsQuery, display: event.target.value })\n }\n placeholder=\"Display\"\n />\n \n \n {\n this.onChange({ ...metricsQuery, regex: { ...regex, enable: !regex.enable } });\n }}\n />\n \n \n ) =>\n onChange({ ...metricsQuery, regex: { ...regex, search: event.target.value } })\n }\n placeholder=\"(.*)\"\n />\n \n \n ) =>\n onChange({ ...metricsQuery, regex: { ...regex, replace: event.target.value } })\n }\n placeholder=\"$1\"\n />\n \n \n \n );\n }\n}\n","import { each, map } from 'lodash';\r\n\r\nimport {\r\n AnnotationQuery,\r\n DataFrame,\r\n TableData,\r\n MetricFindValue,\r\n Field,\r\n toDataFrame,\r\n} from '@grafana/data';\r\n\r\nimport { PiwebapiElementPath, PiwebapiRsp, PIWebAPIQuery } from 'types';\r\n\r\nexport function parseRawQuery(tr: string): any {\r\n const splitAttributes = tr.split(';');\r\n const splitElements = splitAttributes[0].split('\\\\');\r\n\r\n // remove element hierarchy from attribute collection\r\n splitAttributes.splice(0, 1);\r\n\r\n let attributes: any[] = [];\r\n if (splitElements.length > 1 || (splitElements.length === 1 && splitElements[0] !== '')) {\r\n const elementPath: string = splitElements.join('\\\\');\r\n each(splitAttributes, function (item, index) {\r\n if (item !== '') {\r\n attributes.push({\r\n label: item,\r\n value: {\r\n value: item,\r\n expandable: false,\r\n },\r\n });\r\n }\r\n });\r\n\r\n return { attributes, elementPath };\r\n }\r\n\r\n return { attributes, elementPath: null };\r\n}\r\n\r\nexport function lowerCaseFirstLetter(string: string): string {\r\n return string.charAt(0).toLocaleLowerCase() + string.slice(1);\r\n}\r\n\r\n/**\r\n * Builds the Grafana metric segment for use on the query user interface.\r\n *\r\n * @param {any} response - response from PI Web API.\r\n * @returns - Grafana metric segment.\r\n *\r\n * @memberOf PiWebApiDatasource\r\n */\r\nexport function metricQueryTransform(response: PiwebapiRsp[]): MetricFindValue[] {\r\n return map(response, (item) => {\r\n return {\r\n text: item.Name,\r\n expandable:\r\n item.HasChildren === undefined || item.HasChildren === true || (item.Path ?? '').split('\\\\').length <= 3,\r\n HasChildren: item.HasChildren,\r\n Items: item.Items ?? [],\r\n Path: item.Path,\r\n WebId: item.WebId,\r\n } as MetricFindValue;\r\n });\r\n}\r\n\r\n/**\r\n * Check if all items are selected.\r\n *\r\n * @param {any} current the current variable selection\r\n * @return {boolean} true if all value is selected, false otherwise\r\n */\r\nexport function isAllSelected(current: any): boolean {\r\n if (!current) {\r\n return false;\r\n }\r\n if (Array.isArray(current.text)) {\r\n return current.text.indexOf('All') >= 0;\r\n }\r\n return current.text === 'All';\r\n}\r\n\r\nexport function processAnnotationQuery(annon: AnnotationQuery,data: DataFrame[]): DataFrame[] {\r\n let processedFrames: DataFrame[] = [];\r\n \r\n data.forEach((d: DataFrame) => {\r\n d.fields.forEach((f: Field) => {\r\n\r\n // check if the label has been set, if it hasn't been set then the eventframe annotation is not valid. \r\n if (!f.labels) { \r\n return \r\n }\r\n\r\n if (!('eventframe' in f.labels)) {\r\n return;\r\n }\r\n\r\n let attribute = 'attribute' in f.labels\r\n\r\n // Check whether f.values is an array or not to allow for each.\r\n // Check whether f.values is an array or not to allow for each.\r\n if (Array.isArray(f.values)) {\r\n f.values.forEach((value: any) => {\r\n\r\n if (attribute) {\r\n let annotation = value['1'].Content\r\n let valueData: any[] = []\r\n for (let i = 2; i in value; i++) {\r\n valueData.push(value[i].Content.Items)\r\n }\r\n\r\n const processedFrame = convertToTableData(annotation.Items!, valueData).map((r) => {\r\n return toDataFrame(r)});\r\n processedFrames = processedFrames.concat(processedFrame);\r\n } else {\r\n let annotation = value['1'].Content\r\n const processedFrame = convertToTableData(annotation.Items!).map((r) => {\r\n return toDataFrame(r)});\r\n processedFrames = processedFrames.concat(processedFrame);\r\n }\r\n });\r\n } \r\n });\r\n });\r\n return processedFrames;\r\n}\r\n\r\nexport function convertToTableData(items: any[], valueData?: any[]): TableData[] {\r\n const response: TableData[] = items.map((item: any, index: number) => {\r\n const columns = [{ text: 'StartTime' }, { text: 'EndTime' }];\r\n const rows = [item.StartTime, item.EndTime];\r\n if (valueData) {\r\n for (let attributeIndex = 0; attributeIndex < valueData.length; attributeIndex++) {\r\n let attributeData = valueData[attributeIndex]\r\n let eventframeAributeData = attributeData[index].Content.Items\r\n eventframeAributeData.forEach((attribute: any) => {\r\n columns.push({ text: attribute.Name });\r\n rows.push(String(attribute.Value.Value ? attribute.Value.Value.Name || attribute.Value.Value.Value || attribute.Value.Value : ''));\r\n });\r\n }\r\n }\r\n\r\n return {\r\n name: item.Name,\r\n columns,\r\n rows: [rows],\r\n };\r\n });\r\n return response;\r\n}\r\n\r\n/**\r\n * Resolve PIWebAPI response 'value' data to value - timestamp pairs.\r\n *\r\n * @param {any} item - 'Item' object from PIWebAPI\r\n * @param {any} noDataReplacementMode - String state of how to replace 'No Data'\r\n * @param {any} grafanaDataPoint - Single Grafana value pair (value, timestamp).\r\n * @returns grafanaDataPoint - Single Grafana value pair (value, timestamp).\r\n * @returns perviousValue - {any} Grafana value (value only).\r\n *\r\n */\r\nexport function noDataReplace(\r\n item: any,\r\n noDataReplacementMode: any,\r\n grafanaDataPoint: any[]\r\n): {\r\n grafanaDataPoint: any[];\r\n previousValue: any;\r\n drop: boolean;\r\n} {\r\n let previousValue = null;\r\n let drop = false;\r\n if (!item.Good || item.Value === 'No Data' || (item.Value?.Name && item.Value?.Name === 'No Data')) {\r\n if (noDataReplacementMode === 'Drop') {\r\n drop = true;\r\n } else if (noDataReplacementMode === '0') {\r\n grafanaDataPoint[0] = 0;\r\n } else if (noDataReplacementMode === 'Keep') {\r\n // Do nothing keep\r\n } else if (noDataReplacementMode === 'Null') {\r\n grafanaDataPoint[0] = null;\r\n } else if (noDataReplacementMode === 'Previous' && previousValue !== null) {\r\n grafanaDataPoint[0] = previousValue;\r\n }\r\n } else {\r\n previousValue = item.Value;\r\n }\r\n return { grafanaDataPoint, previousValue, drop };\r\n}\r\n\r\n/**\r\n * Check if the value is a number.\r\n *\r\n * @param {any} number the value to check\r\n * @returns {boolean} true if the value is a number, false otherwise\r\n */\r\nexport function checkNumber(number: any): boolean {\r\n return typeof number === 'number' && !Number.isNaN(number) && Number.isFinite(number);\r\n}\r\n\r\n/**\r\n * Returns the last item of the element path.\r\n *\r\n * @param {string} path element path\r\n * @returns {string} last item of the element path\r\n */\r\nexport function getLastPath(path: string): string {\r\n let splitPath = path.split('|');\r\n if (splitPath.length === 0) {\r\n return '';\r\n }\r\n splitPath = splitPath[0].split('\\\\');\r\n return splitPath.length === 0 ? '' : splitPath.pop() ?? '';\r\n}\r\n\r\n/**\r\n * Returns the last item of the element path plus variable.\r\n *\r\n * @param {PiwebapiElementPath[]} elementPathArray array of element paths\r\n * @param {string} path element path\r\n * @returns {string} last item of the element path\r\n */\r\nexport function getPath(elementPathArray: PiwebapiElementPath[], path: string): string {\r\n if (!path || elementPathArray.length === 0) {\r\n return '';\r\n }\r\n const splitStr = getLastPath(path);\r\n const foundElement = elementPathArray.find((e) => path.indexOf(e.path) >= 0)?.variable;\r\n return foundElement ? foundElement + '|' + splitStr : splitStr;\r\n}\r\n\r\n/**\r\n * Replace calculation dot in expression with PI point name.\r\n *\r\n * @param {boolean} replace - is pi point and calculation.\r\n * @param {PiwebapiRsp} webid - Pi web api response object.\r\n * @param {string} url - original url.\r\n * @returns Modified url\r\n */\r\nexport function getFinalUrl(replace: boolean, webid: PiwebapiRsp, url: string) {\r\n const newUrl = replace ? url.replace(/'\\.'/g, `'${webid.Name}'`) : url;\r\n return newUrl;\r\n}\r\n","import React, { memo, useState } from 'react';\n\nimport { AnnotationQuery, QueryEditorProps, SelectableValue } from '@grafana/data';\nimport { AsyncSelect, InlineField, InlineFieldRow, InlineSwitch, Input } from '@grafana/ui';\n\nimport { PiWebAPIDatasource } from 'datasource';\nimport { PIWebAPIDataSourceJsonData, PIWebAPIQuery, PiwebapiRsp } from 'types';\n\nconst SMALL_LABEL_WIDTH = 20;\nconst LABEL_WIDTH = 30;\nconst MIN_INPUT_WIDTH = 50;\n\ntype PiWebAPIQueryEditorProps = QueryEditorProps;\n\ntype Props = PiWebAPIQueryEditorProps & {\n annotation?: AnnotationQuery;\n onAnnotationChange?: (annotation: AnnotationQuery) => void;\n};\n\nexport const PiWebAPIAnnotationsQueryEditor = memo(function PiWebAPIAnnotationQueryEditor(props: Props) {\n const { query, datasource, annotation, onChange, onRunQuery } = props;\n\n const [afWebId, setAfWebId] = useState('');\n const [database, setDatabase] = useState(annotation?.target?.database ?? {});\n\n // this should never happen, but we want to keep typescript happy\n if (annotation === undefined) {\n return null;\n }\n\n const getEventFrames = (): Promise>> => {\n return datasource.getEventFrameTemplates(database?.WebId!).then((templ: PiwebapiRsp[]) => {\n return templ.map((d) => ({ label: d.Name, value: d }));\n });\n };\n\n const getDatabases = (): Promise>> => {\n return datasource.getDatabases(afWebId).then((dbs: PiwebapiRsp[]) => {\n return dbs.map((d) => ({ label: d.Name, value: d }));\n });\n };\n\n const getValue = (key: string) => {\n const query: any = annotation.target as any;\n if (!query || !query[key]) {\n return;\n }\n return { label: query[key].Name, value: query[key] };\n };\n\n datasource.getAssetServer(datasource.afserver.name).then((result) => {\n setAfWebId(result.WebId!);\n });\n\n return (\n <>\n
\n \n \n {\n setDatabase(e.value);\n onChange({ ...query, database: e.value, template: undefined });\n }}\n defaultOptions\n />\n \n \n onChange({ ...query, template: e.value })}\n defaultOptions\n />\n \n \n onChange({ ...query, showEndTime: e.currentTarget.checked })}\n />\n \n \n \n \n onRunQuery()}\n onChange={(e) => onChange({ ...query, categoryName: e.currentTarget.value })}\n placeholder=\"Enter category name\"\n />\n \n \n onRunQuery()}\n onChange={(e) => onChange({ ...query, nameFilter: e.currentTarget.value })}\n placeholder=\"Enter name filter\"\n />\n \n \n \n \n \n onChange({\n ...query,\n regex: { ...query.regex, enable: e.currentTarget.checked },\n })\n }\n />\n \n \n onRunQuery()}\n onChange={(e) =>\n onChange({\n ...query,\n regex: { ...query.regex, search: e.currentTarget.value },\n })\n }\n placeholder=\"(.*)\"\n width={MIN_INPUT_WIDTH}\n />\n \n \n onRunQuery()}\n onChange={(e) =>\n onChange({\n ...query,\n regex: { ...query.regex, replace: e.currentTarget.value },\n })\n }\n placeholder=\"$1\"\n />\n \n \n \n \n \n onChange({\n ...query!,\n attribute: { ...query.attribute, enable: e.currentTarget.checked },\n })\n }\n />\n \n \n onRunQuery()}\n onChange={(e) =>\n onChange({\n ...query!,\n attribute: { ...query.attribute, name: e.currentTarget.value },\n })\n }\n placeholder=\"Enter name\"\n />\n \n \n
\n \n );\n});\n","import { filter, map } from 'lodash';\n\nimport { Observable, of, firstValueFrom } from 'rxjs';\n\nimport {\n DataSourceInstanceSettings,\n MetricFindValue,\n AnnotationQuery,\n ScopedVars,\n AnnotationEvent,\n DataFrame,\n DataQueryRequest,\n DataQueryResponse,\n} from '@grafana/data';\nimport { BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, DataSourceWithBackend } from '@grafana/runtime';\n\nimport { PIWebAPIQuery, PIWebAPIDataSourceJsonData, PiDataServer, PiwebapiInternalRsp, PiwebapiRsp } from './types';\nimport { metricQueryTransform, parseRawQuery } from 'helper';\n\nimport { PiWebAPIAnnotationsQueryEditor } from 'query/AnnotationsQueryEditor';\n\nexport class PiWebAPIDatasource extends DataSourceWithBackend {\n piserver: PiDataServer;\n afserver: PiDataServer;\n afdatabase: PiDataServer;\n piPointConfig: boolean;\n newFormatConfig: boolean;\n useUnitConfig: boolean;\n useExperimental: boolean;\n useStreaming: boolean;\n\n constructor(\n instanceSettings: DataSourceInstanceSettings,\n readonly templateSrv: TemplateSrv = getTemplateSrv(),\n private readonly backendSrv: BackendSrv = getBackendSrv()\n ) {\n super(instanceSettings);\n\n this.piserver = { name: (instanceSettings.jsonData || {}).piserver, webid: undefined };\n this.afserver = { name: (instanceSettings.jsonData || {}).afserver, webid: undefined };\n this.afdatabase = { name: (instanceSettings.jsonData || {}).afdatabase, webid: undefined };\n this.piPointConfig = instanceSettings.jsonData.pipoint || false;\n this.newFormatConfig = instanceSettings.jsonData.newFormat || false;\n this.useUnitConfig = instanceSettings.jsonData.useUnit || false;\n this.useExperimental = instanceSettings.jsonData.useExperimental || false;\n this.useStreaming = instanceSettings.jsonData.useStreaming || false;\n\n this.annotations = {\n QueryEditor: PiWebAPIAnnotationsQueryEditor,\n prepareQuery(anno: AnnotationQuery): PIWebAPIQuery | undefined {\n if (anno.target) {\n anno.target.queryType = 'Annotation';\n anno.target.isAnnotation = true;\n }\n return anno.target;\n },\n processEvents: (\n anno: AnnotationQuery,\n data: DataFrame[]\n ): Observable => {\n return of(this.eventFrameToAnnotation(anno, data));\n },\n };\n\n Promise.all([\n this.getDataServer(this.piserver.name).then((result: PiwebapiRsp) => (this.piserver.webid = result.WebId)),\n this.getAssetServer(this.afserver.name).then((result: PiwebapiRsp) => (this.afserver.webid = result.WebId)),\n this.getDatabase(\n this.afserver.name && this.afdatabase.name ? this.afserver.name + '\\\\' + this.afdatabase.name : undefined\n ).then((result: PiwebapiRsp) => (this.afdatabase.webid = result.WebId)),\n ]);\n }\n\n /**\n * This method overrides the applyTemplateVariables() method from the DataSourceWithBackend class.\n * It is responsible for replacing the template variables in the query configuration prior\n * to sending the query to the backend. Templated variables are not able to be used for alerts\n * or public facing dashboards.\n *\n * @param {PIWebAPIQuery} query - The raw query configuration from the frontend as defined in the query editor.\n * @param {ScopedVars} scopedVars - The template variables that are defined in the query editor and dashboard.\n * @returns - PIWebAPIQuery.\n *\n * @memberOf PiWebApiDatasource\n */\n applyTemplateVariables(query: PIWebAPIQuery, scopedVars: ScopedVars) {\n return {\n ...query,\n target: query.target ? this.templateSrv.replace(query.target, scopedVars) : '',\n };\n }\n\n query(options: DataQueryRequest): Observable {\n if (options.targets.length === 1 && !!options.targets[0].isAnnotation) {\n return super.query(options);\n }\n\n const query = this.buildQueryParameters(options);\n if (query.targets.length <= 0) {\n return of({ data: [] });\n }\n \n return super.query(query);\n }\n\n /**\n * This method does the discovery of the AF Hierarchy and populates the query user interface segments.\n *\n * @param {any} query - Parses the query configuration and builds a PI Web API query.\n * @returns - Segment information.\n *\n * @memberOf PiWebApiDatasource\n */\n metricFindQuery(query: any, queryOptions: any): Promise {\n const ds = this;\n const querydepth = ['servers', 'databases', 'databaseElements', 'elements'];\n if (typeof query === 'string') {\n query = JSON.parse(query as string);\n }\n if (queryOptions.isPiPoint) {\n query.path = this.templateSrv.replace(query.path, queryOptions);\n } else {\n if (query.path === '') {\n query.type = querydepth[0];\n } else {\n query.path = this.templateSrv.replace(query.path, queryOptions); // replace variables in the path\n query.path = query.path.split(';')[0]; // if the attribute is in the path, let's remote it\n if (query.type !== 'attributes') {\n query.type = querydepth[Math.max(0, Math.min(query.path.split('\\\\').length, querydepth.length - 1))];\n }\n }\n query.path = query.path.replace(/\\{([^\\\\])*\\}/gi, (r: string) => r.substring(1, r.length - 2).split(',')[0]);\n }\n\n query.filter = query.filter ?? '*';\n\n if (query.type === 'servers') {\n return ds.afserver?.name\n ? ds\n .getAssetServer(ds.afserver.name)\n .then((result: PiwebapiRsp) => [result])\n .then(metricQueryTransform)\n : ds.getAssetServers().then(metricQueryTransform);\n } else if (query.type === 'databases' && !!query.afServerWebId) {\n return ds.getDatabases(query.afServerWebId, {}).then(metricQueryTransform);\n } else if (query.type === 'databases') {\n return ds\n .getAssetServer(query.path)\n .then((server) => ds.getDatabases(server.WebId ?? '', {}))\n .then(metricQueryTransform);\n } else if (query.type === 'databaseElements') {\n return ds\n .getDatabase(query.path)\n .then((db) =>\n ds.getDatabaseElements(db.WebId ?? '', {\n selectedFields: 'Items.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren',\n })\n )\n .then(metricQueryTransform);\n } else if (query.type === 'elements') {\n return ds\n .getElement(query.path)\n .then((element) =>\n ds.getElements(element.WebId ?? '', {\n selectedFields:\n 'Items.Description%3BItems.WebId%3BItems.Name%3BItems.Items%3BItems.Path%3BItems.HasChildren',\n nameFilter: query.filter,\n })\n )\n .then(metricQueryTransform);\n } else if (query.type === 'attributes') {\n return ds\n .getElement(query.path)\n .then((element) =>\n ds.getAttributes(element.WebId ?? '', {\n searchFullHierarchy: 'true',\n selectedFields:\n 'Items.Type%3BItems.DefaultUnitsName%3BItems.Description%3BItems.WebId%3BItems.Name%3BItems.Path',\n nameFilter: query.filter,\n })\n )\n .then(metricQueryTransform);\n } else if (query.type === 'dataserver') {\n return ds.getDataServers().then(metricQueryTransform);\n } else if (query.type === 'pipoint') {\n return ds.piPointSearch(query.webId, query.pointName).then(metricQueryTransform);\n }\n return Promise.reject('Bad type');\n }\n\n /** PRIVATE SECTION */\n\n /**\n * Builds the PIWebAPI query parameters.\n *\n * @param {any} options - Grafana query and panel options.\n * @returns - PIWebAPI query parameters.\n *\n * @memberOf PiWebApiDatasource\n */\n private buildQueryParameters(options: DataQueryRequest) {\n options.targets = filter(options.targets, (target) => {\n if (!target || !target.target || target.attributes?.length === 0 || target.target === ';' || !!target.hide) {\n return false;\n }\n return !target.target.startsWith('Select AF');\n });\n\n options.targets = map(options.targets, (target) => {\n if (!!target.rawQuery && !!target.target) {\n const { attributes, elementPath } = parseRawQuery(this.templateSrv.replace(target.target, options.scopedVars));\n target.attributes = attributes;\n target.elementPath = elementPath;\n }\n const tar = {\n enableStreaming: target.enableStreaming,\n target: this.templateSrv.replace(target.elementPath, options.scopedVars),\n elementPath: this.templateSrv.replace(target.elementPath, options.scopedVars),\n attributes: map(target.attributes, (att) =>\n this.templateSrv.replace(att.value?.value || att, options.scopedVars)\n ),\n isAnnotation: !!target.isAnnotation,\n segments: map(target.segments, (att) => this.templateSrv.replace(att.value?.value, options.scopedVars)),\n display: !!target.display ? this.templateSrv.replace(target.display, options.scopedVars) : undefined,\n refId: target.refId,\n hide: target.hide,\n interpolate: target.interpolate || { enable: false },\n useLastValue: target.useLastValue || { enable: false },\n useUnit: target.useUnit || { enable: false },\n recordedValues: target.recordedValues || { enable: false },\n digitalStates: target.digitalStates || { enable: false },\n webid: target.webid ?? '',\n webids: target.webids || [],\n regex: target.regex || { enable: false },\n expression: target.expression || '',\n summary: target.summary || { types: [] },\n startTime: options.range.from,\n endTime: options.range.to,\n isPiPoint: !!target.isPiPoint,\n scopedVars: options.scopedVars,\n };\n\n if (tar.expression) {\n tar.expression = this.templateSrv.replace(tar.expression, options.scopedVars);\n }\n\n if (tar.summary.types !== undefined) {\n tar.summary.types = filter(tar.summary.types, (item) => {\n return item !== undefined && item !== null && item !== '';\n });\n }\n \n return tar;\n });\n\n return options;\n }\n\n /**\n * Localize the eventFrame dataFrame records to Grafana Annotations.\n * @param {any} annon - The annotation object.\n * @param {any} data - The dataframe recrords.\n * @returns - Grafana Annotation\n *\n * @memberOf PiWebApiDatasource\n */\n private eventFrameToAnnotation(annon: AnnotationQuery, data: DataFrame[]): AnnotationEvent[] {\n const annotationOptions = annon.target!;\n const events: AnnotationEvent[] = [];\n const currentLocale = Intl.DateTimeFormat().resolvedOptions().locale;\n\n data.forEach((d: DataFrame) => {\n let values = this.transformDataFrameToMap(d);\n for (let i = 0; i < values['time'].length; i++) {\n // replace Dataframe name using Regex\n let title = values['title'][i];\n if (annotationOptions.regex && annotationOptions.regex.enable) {\n title = title.replace(new RegExp(annotationOptions.regex.search), annotationOptions.regex.replace);\n }\n\n // test if timeEnd is negative and if so, set it to null\n if (values['timeEnd'][i] < 0) {\n values['timeEnd'][i] = null;\n }\n\n // format the text and localize the dates to browser locale\n let text = 'Tag: ' + title;\n if (annotationOptions.attribute && annotationOptions.attribute.enable) {\n text += values['attributeText'][i];\n }\n text += '
Start: ' + new Date(values['time'][i]).toLocaleString(currentLocale) + '
End: ';\n\n if (values['timeEnd'][i]) {\n text += new Date(values['timeEnd'][i]).toLocaleString(currentLocale);\n } else {\n text += 'Eventframe is open';\n }\n\n const event: AnnotationEvent = {\n time: values['time'][i],\n timeEnd: !!annotationOptions.showEndTime ? values['timeEnd'][i] : undefined,\n title: title,\n id: values['id'][i],\n text: text,\n tags: ['OSISoft PI'],\n };\n\n events.push(event);\n }\n });\n return events;\n }\n\n /**\n *\n */\n private transformDataFrameToMap(dataFrame: DataFrame): Record {\n const map: Record = {};\n\n dataFrame.fields.forEach((field) => {\n map[field.name] = field.values.toArray();\n });\n\n return map;\n }\n\n /**\n * Abstraction for calling the PI Web API REST endpoint\n *\n * @param {any} path - the path to append to the base server URL.\n * @returns - The full URL.\n *\n * @memberOf PiWebApiDatasource\n */\n private restGet(path: string): Promise {\n const observable = this.backendSrv.fetch({\n url: `/api/datasources/${this.id}/resources${path}`,\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n return firstValueFrom(observable).then((response: any) => {\n return response as PiwebapiInternalRsp;\n });\n }\n\n // Get a list of all data (PI) servers\n private getDataServers(): Promise {\n return this.restGet('/dataservers').then((response) => response.data.Items ?? []);\n }\n private getDataServer(name: string | undefined): Promise {\n if (!name) {\n return Promise.resolve({});\n }\n return this.restGet('/dataservers?name=' + name).then((response) => response.data);\n }\n // Get a list of all asset (AF) servers\n private getAssetServers(): Promise {\n return this.restGet('/assetservers').then((response) => response.data.Items ?? []);\n }\n getAssetServer(name: string | undefined): Promise {\n if (!name) {\n return Promise.resolve({});\n }\n return this.restGet('/assetservers?path=\\\\\\\\' + name).then((response) => response.data);\n }\n getDatabase(path: string | undefined): Promise {\n if (!path) {\n return Promise.resolve({});\n }\n return this.restGet('/assetdatabases?path=\\\\\\\\' + path).then((response) => response.data);\n }\n getDatabases(serverId: string, options?: any): Promise {\n if (!serverId) {\n return Promise.resolve([]);\n }\n return this.restGet('/assetservers/' + serverId + '/assetdatabases').then((response) => response.data.Items ?? []);\n }\n getElement(path: string): Promise {\n if (!path) {\n return Promise.resolve({});\n }\n return this.restGet('/elements?path=\\\\\\\\' + path).then((response) => response.data);\n }\n getEventFrameTemplates(databaseId: string): Promise {\n if (!databaseId) {\n return Promise.resolve([]);\n }\n return this.restGet(\n '/assetdatabases/' + databaseId + '/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId'\n ).then((response) => {\n return filter(response.data.Items ?? [], (item) => item.InstanceType === 'EventFrame');\n });\n }\n getElementTemplates(databaseId: string): Promise {\n if (!databaseId) {\n return Promise.resolve([]);\n }\n return this.restGet(\n '/assetdatabases/' + databaseId + '/elementtemplates?selectedFields=Items.InstanceType%3BItems.Name%3BItems.WebId'\n ).then((response) => {\n return filter(response.data.Items ?? [], (item) => item.InstanceType === 'Element');\n });\n }\n\n /**\n * @description\n * Get the child attributes of the current resource.\n * GET attributes/{webId}/attributes\n * @param {string} elementId - The ID of the parent resource. See WebID for more information.\n * @param {Object} options - Query Options\n * @param {string} options.nameFilter - The name query string used for finding attributes. The default is no filter. See Query String for more information.\n * @param {string} options.categoryName - Specify that returned attributes must have this category. The default is no category filter.\n * @param {string} options.templateName - Specify that returned attributes must be members of this template. The default is no template filter.\n * @param {string} options.valueType - Specify that returned attributes' value type must be the given value type. The default is no value type filter.\n * @param {string} options.searchFullHierarchy - Specifies if the search should include attributes nested further than the immediate attributes of the searchRoot. The default is 'false'.\n * @param {string} options.sortField - The field or property of the object used to sort the returned collection. The default is 'Name'.\n * @param {string} options.sortOrder - The order that the returned collection is sorted. The default is 'Ascending'.\n * @param {string} options.startIndex - The starting index (zero based) of the items to be returned. The default is 0.\n * @param {string} options.showExcluded - Specified if the search should include attributes with the Excluded property set. The default is 'false'.\n * @param {string} options.showHidden - Specified if the search should include attributes with the Hidden property set. The default is 'false'.\n * @param {string} options.maxCount - The maximum number of objects to be returned per call (page size). The default is 1000.\n * @param {string} options.selectedFields - List of fields to be returned in the response, separated by semicolons (;). If this parameter is not specified, all available fields will be returned. See Selected Fields for more information.\n */\n private getAttributes(elementId: string, options: any): Promise {\n let querystring =\n '?' +\n map(options, (value, key) => {\n return key + '=' + value;\n }).join('&');\n\n if (querystring === '?') {\n querystring = '';\n }\n\n return this.restGet('/elements/' + elementId + '/attributes' + querystring).then(\n (response) => response.data.Items ?? []\n );\n }\n\n /**\n * @description\n * Retrieve elements based on the specified conditions. By default, this method selects immediate children of the current resource.\n * Users can search for the elements based on specific search parameters. If no parameters are specified in the search, the default values for each parameter will be used and will return the elements that match the default search.\n * GET assetdatabases/{webId}/elements\n * @param {string} databaseId - The ID of the parent resource. See WebID for more information.\n * @param {Object} options - Query Options\n * @param {string} options.webId - The ID of the resource to use as the root of the search. See WebID for more information.\n * @param {string} options.nameFilter - The name query string used for finding objects. The default is no filter. See Query String for more information.\n * @param {string} options.categoryName - Specify that returned elements must have this category. The default is no category filter.\n * @param {string} options.templateName - Specify that returned elements must have this template or a template derived from this template. The default is no template filter.\n * @param {string} options.elementType - Specify that returned elements must have this type. The default type is 'Any'. See Element Type for more information.\n * @param {string} options.searchFullHierarchy - Specifies if the search should include objects nested further than the immediate children of the searchRoot. The default is 'false'.\n * @param {string} options.sortField - The field or property of the object used to sort the returned collection. The default is 'Name'.\n * @param {string} options.sortOrder - The order that the returned collection is sorted. The default is 'Ascending'.\n * @param {number} options.startIndex - The starting index (zero based) of the items to be returned. The default is 0.\n * @param {number} options.maxCount - The maximum number of objects to be returned per call (page size). The default is 1000.\n * @param {string} options.selectedFields - List of fields to be returned in the response, separated by semicolons (;). If this parameter is not specified, all available fields will be returned. See Selected Fields for more information.\n */\n private getDatabaseElements(databaseId: string, options: any): Promise {\n let querystring =\n '?' +\n map(options, (value, key) => {\n return key + '=' + value;\n }).join('&');\n\n if (querystring === '?') {\n querystring = '';\n }\n\n return this.restGet('/assetdatabases/' + databaseId + '/elements' + querystring).then(\n (response) => response.data.Items ?? []\n );\n }\n\n /**\n * @description\n * Retrieve elements based on the specified conditions. By default, this method selects immediate children of the current resource.\n * Users can search for the elements based on specific search parameters. If no parameters are specified in the search, the default values for each parameter will be used and will return the elements that match the default search.\n * GET elements/{webId}/elements\n * @param {string} databaseId - The ID of the resource to use as the root of the search. See WebID for more information.\n * @param {Object} options - Query Options\n * @param {string} options.webId - The ID of the resource to use as the root of the search. See WebID for more information.\n * @param {string} options.nameFilter - The name query string used for finding objects. The default is no filter. See Query String for more information.\n * @param {string} options.categoryName - Specify that returned elements must have this category. The default is no category filter.\n * @param {string} options.templateName - Specify that returned elements must have this template or a template derived from this template. The default is no template filter.\n * @param {string} options.elementType - Specify that returned elements must have this type. The default type is 'Any'. See Element Type for more information.\n * @param {string} options.searchFullHierarchy - Specifies if the search should include objects nested further than the immediate children of the searchRoot. The default is 'false'.\n * @param {string} options.sortField - The field or property of the object used to sort the returned collection. The default is 'Name'.\n * @param {string} options.sortOrder - The order that the returned collection is sorted. The default is 'Ascending'.\n * @param {number} options.startIndex - The starting index (zero based) of the items to be returned. The default is 0.\n * @param {number} options.maxCount - The maximum number of objects to be returned per call (page size). The default is 1000.\n * @param {string} options.selectedFields - List of fields to be returned in the response, separated by semicolons (;). If this parameter is not specified, all available fields will be returned. See Selected Fields for more information.\n */\n private getElements(elementId: string, options: any): Promise {\n let querystring =\n '?' +\n map(options, (value, key) => {\n return key + '=' + value;\n }).join('&');\n\n if (querystring === '?') {\n querystring = '';\n }\n\n return this.restGet('/elements/' + elementId + '/elements' + querystring).then(\n (response) => response.data.Items ?? []\n );\n }\n\n /**\n * Retrieve a list of points on a specified Data Server.\n *\n * @param {string} serverId - The ID of the server. See WebID for more information.\n * @param {string} nameFilter - A query string for filtering by point name. The default is no filter. *, ?, [ab], [!ab]\n */\n private piPointSearch(serverId: string, nameFilter: string): Promise {\n let filter1 = this.templateSrv.replace(nameFilter);\n let filter2 = `${filter1}`;\n let doFilter = false;\n if (filter1 !== nameFilter) {\n const regex = /\\{(\\w|,)+\\}/gs;\n let m;\n while ((m = regex.exec(filter1)) !== null) {\n // This is necessary to avoid infinite loops with zero-width matches\n if (m.index === regex.lastIndex) {\n regex.lastIndex++;\n }\n\n // The result can be accessed through the `m`-variable.\n m.forEach((match, groupIndex) => {\n if (groupIndex === 0) {\n filter1 = filter1.replace(match, match.replace('{', '(').replace('}', ')').replace(',', '|'));\n filter2 = filter2.replace(match, '*');\n doFilter = true;\n }\n });\n }\n }\n return this.restGet('/dataservers/' + serverId + '/points?maxCount=50&nameFilter=' + filter2).then((results) => {\n if (!!results && !!results.data?.Items) {\n return doFilter ? results.data.Items.filter((item) => item.Name?.match(filter1)) : results.data.Items;\n }\n return [];\n });\n }\n}\n","import { DataSourcePlugin } from '@grafana/data';\nimport { PIWebAPIConfigEditor } from './config/ConfigEditor';\nimport { PIWebAPIQueryEditor } from './query/QueryEditor';\nimport { PiWebAPIDatasource } from './datasource';\nimport { PIWebAPIQuery, PIWebAPIDataSourceJsonData } from './types';\n\nexport const plugin = new DataSourcePlugin(\n PiWebAPIDatasource\n)\n .setQueryEditor(PIWebAPIQueryEditor)\n .setConfigEditor(PIWebAPIConfigEditor);\n"],"names":["module","exports","__WEBPACK_EXTERNAL_MODULE__781__","__WEBPACK_EXTERNAL_MODULE__531__","__WEBPACK_EXTERNAL_MODULE__7__","__WEBPACK_EXTERNAL_MODULE__241__","__WEBPACK_EXTERNAL_MODULE__959__","__WEBPACK_EXTERNAL_MODULE__269__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","value","FormField","LegacyForms","coerceOptions","options","jsonData","url","PIWebAPIConfigEditor","PureComponent","render","originalOptions","this","props","div","DataSourceHttpSettings","defaultUrl","dataSourceConfig","onChange","onHttpOptionsChange","showAccessOptions","h3","className","InlineField","label","labelWidth","InlineSwitch","pipoint","onPiPointChange","newFormat","onNewFormatChange","useUnit","onUseUnitChange","useExperimental","onUseExperimentalChange","useStreaming","onUseStreamingChange","inputWidth","onPIServerChange","piserver","placeholder","onAFServerChange","afserver","onAFDatabaseChange","afdatabase","event","onOptionsChange","target","checked","QueryField","tooltip","children","InlineFormLabel","width","QueryRowTerminator","QueryInlineField","QueryEditorRow","QueryRawInlineField","QueryRawEditorRow","defaultQuery","attributes","segments","regex","enable","summary","types","basis","interval","nodata","expression","interpolate","useLastValue","recordedValues","digitalStates","enableStreaming","isPiPoint","QueryEditorModeSwitcher","isRaw","isModalOpen","setModalOpen","useState","useEffect","Button","aria-label","icon","variant","type","onClick","ConfirmModal","isOpen","title","body","confirmText","dismissText","onConfirm","onDismiss","LABEL_WIDTH","MIN_ATTR_INPUT_WIDTH","REMOVE_LABEL","CustomLabelComponent","Icon","name","PIWebAPIQueryEditor","isValueEmpty","length","calcBasisValueChanged","segment","metricsQuery","query","getCalcBasisSegments","map","calculationBasis","item","expandable","calcNoDataValueChanged","getNoDataSegments","noDataReplacement","onSummaryValueChanged","index","summaries","state","slice","splice","setState","stateCallback","getSummarySegments","summaryTypes","filter","s","indexOf","unshift","removeSummary","part","onSummaryAction","selectableValue","push","summarySegment","removeAttribute","attributeChangeValue","onAttributeAction","getSegmentPathUpTo","arr","reduce","result","startsWith","checkAttributeSegments","data","datasource","ctrl","findQuery","path","metricFindQuery","assign","request","scopedVars","then","attributesResponse","validAttributes","each","attribute","Path","substring","WebId","filteredAttributes","attrib","changedValue","templateSrv","replace","availableAttributes","catch","err","error","message","checkPiPointSegments","webId","getSelectedPIServer","pointName","webID","piServer","forEach","parts","split","text","textEditorChanged","splitAttributes","splitElements","_","match","getElementSegments","elements","updateArray","rawQuery","checkAfServer","queryProps","onRunQuery","defaults","display","piPointConfig","onIsPiPointChange","InlineFieldRow","grow","Input","onBlur","SegmentAsync","Component","onSegmentChange","loadOptions","allowCustomValue","inputMinWidth","disabled","getAttributeSegmentsPI","reloadOptionsOnChange","Segment","onAttributeChange","getAttributeSegmentsAF","attributeSegment","useUnitConfig","maxNumber","parseInt","search","constructor","super","calculationBasisSegment","noDataReplacementSegment","segmentChangeValue","remove","currentSegment","afServerWebId","Promise","resolve","items","altSegments","variables","getVariables","variable","attributeText","forOwn","val","buildFromTarget","segmentsArray","attributesArray","summariesArray","cb","scopedVarsDone","componentDidMount","initialLoad","componentDidUpdate","force","_segmentsArray","e","console","elementPath","join","queryChange","bind","metricQueryTransform","response","Name","HasChildren","Items","PiWebAPIAnnotationsQueryEditor","memo","annotation","afWebId","setAfWebId","database","setDatabase","getValue","getAssetServer","AsyncSelect","getDatabases","dbs","loadingMessage","template","defaultOptions","getEventFrameTemplates","templ","showEndTime","currentTarget","categoryName","nameFilter","PiWebAPIDatasource","DataSourceWithBackend","applyTemplateVariables","targets","isAnnotation","buildQueryParameters","of","queryOptions","ds","querydepth","JSON","parse","Math","max","min","getAssetServers","server","getDatabase","db","getDatabaseElements","selectedFields","getElement","element","getElements","getAttributes","searchFullHierarchy","getDataServers","piPointSearch","reject","hide","tr","parseRawQuery","tar","att","refId","webid","webids","startTime","range","from","endTime","to","annon","annotationOptions","events","currentLocale","Intl","DateTimeFormat","resolvedOptions","locale","values","transformDataFrameToMap","i","RegExp","Date","toLocaleString","time","timeEnd","id","tags","dataFrame","fields","field","toArray","observable","backendSrv","fetch","method","headers","firstValueFrom","restGet","getDataServer","serverId","databaseId","InstanceType","getElementTemplates","elementId","querystring","filter1","filter2","doFilter","m","exec","lastIndex","groupIndex","results","instanceSettings","getTemplateSrv","getBackendSrv","newFormatConfig","annotations","QueryEditor","prepareQuery","anno","queryType","processEvents","eventFrameToAnnotation","all","plugin","DataSourcePlugin","setQueryEditor","setConfigEditor"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/plugin.json b/dist/plugin.json index 4123ac3..8a81f5b 100644 --- a/dist/plugin.json +++ b/dist/plugin.json @@ -40,7 +40,7 @@ {"name": "Annotations Editor", "path": "img/annotations.png"} ], "version": "5.0.0-alpha", - "updated": "2024-03-05" + "updated": "2024-04-02" }, "dependencies": { "grafanaDependency": ">=9.3.0", diff --git a/package.json b/package.json index 0550be8..a240c4b 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,15 @@ "start": "yarn watch", "test": "jest --watch --onlyChanged", "test:ci": "jest --passWithNoTests --maxWorkers 4", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "build:backend": "mage -v build:linux && mage -v build:windows && mage -v build:darwin" }, "author": "GridProtectionAlliance", "license": "Apache-2.0", "devDependencies": { "@babel/core": "^7.21.4", - "@grafana/e2e": "10.3.3", - "@grafana/e2e-selectors": "10.3.3", + "@grafana/e2e": "latest", + "@grafana/e2e-selectors": "latest", "@grafana/eslint-config": "^6.0.0", "@grafana/tsconfig": "^1.2.0-rc1", "@swc/core": "^1.3.90", @@ -63,7 +64,7 @@ "@emotion/css": "11.10.6", "@grafana/data": "latest", "@grafana/runtime": "latest", - "@grafana/schema": "10.3.3", + "@grafana/schema": "latest", "@grafana/ui": "latest", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/pkg/main.go b/pkg/main.go index cd494f5..15ec3ac 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -18,7 +18,7 @@ func main() { // ID). When datasource configuration changed Dispose method will be called and // new datasource instance created using NewSampleDatasource factory. if err := datasource.Manage("gridprotectionalliance-osisoftpi-datasource", plugin.NewPIWebAPIDatasource, datasource.ManageOpts{}); err != nil { - log.DefaultLogger.Error(err.Error()) + log.DefaultLogger.Error("Manage", "Plugin", err.Error()) os.Exit(1) } } diff --git a/pkg/plugin/annotation_query.go b/pkg/plugin/annotation_query.go index d4d4dab..ace7d12 100644 --- a/pkg/plugin/annotation_query.go +++ b/pkg/plugin/annotation_query.go @@ -14,7 +14,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/data" ) -func (d Datasource) processAnnotationQuery(ctx context.Context, query backend.DataQuery) PiProcessedAnnotationQuery { +func (d *Datasource) processAnnotationQuery(ctx context.Context, query backend.DataQuery) PiProcessedAnnotationQuery { var ProcessedQuery PiProcessedAnnotationQuery var PiAnnotationQuery PIAnnotationQuery @@ -73,7 +73,7 @@ func (d Datasource) processAnnotationQuery(ctx context.Context, query backend.Da return ProcessedQuery } -func (q *PiProcessedAnnotationQuery) getTimeRangeURIComponent() string { +func (q PiProcessedAnnotationQuery) getTimeRangeURIComponent() string { return "&startTime=" + q.TimeRange.From.UTC().Format(time.RFC3339) + "&endTime=" + q.TimeRange.To.UTC().Format(time.RFC3339) } diff --git a/pkg/plugin/datasource.go b/pkg/plugin/datasource.go index 0d24371..56e3632 100644 --- a/pkg/plugin/datasource.go +++ b/pkg/plugin/datasource.go @@ -39,7 +39,7 @@ func NewPIWebAPIDatasource(settings backend.DataSourceInstanceSettings) (instanc var dataSourceOptions PIWebAPIDataSourceJsonData err := json.Unmarshal(settings.JSONData, &dataSourceOptions) if err != nil { - panic(err) + return nil, fmt.Errorf("http Unmarshal: %w", err) } opts, err := settings.HTTPClientOptions() @@ -54,9 +54,9 @@ func NewPIWebAPIDatasource(settings backend.DataSourceInstanceSettings) (instanc webIDCache := newWebIDCache() - // Create a new scheduler that will be used to clean the webIDCache every 5 minutes. + // Create a new scheduler that will be used to clean the webIDCache every 60 minutes. scheduler := gocron.NewScheduler(time.UTC) - scheduler.Every(5).Minute().Do(cleanWebIDCache, webIDCache) + scheduler.Every(1).Hour().Do(cleanWebIDCache, webIDCache) scheduler.StartAsync() ds := &Datasource{ @@ -65,16 +65,22 @@ func NewPIWebAPIDatasource(settings backend.DataSourceInstanceSettings) (instanc webIDCache: webIDCache, scheduler: scheduler, websocketConnectionsMutex: &sync.Mutex{}, - sendersByWebIDMutex: &sync.Mutex{}, + datasourceMutex: &sync.Mutex{}, channelConstruct: make(map[string]StreamChannelConstruct), websocketConnections: make(map[string]*websocket.Conn), sendersByWebID: make(map[string]map[*backend.StreamSender]bool), streamChannels: make(map[string]chan []byte), dataSourceOptions: &dataSourceOptions, + initalTime: time.Now(), + totalCalls: 0, + callRate: 0.0, } // Create a new query mux and assign it to the datasource. ds.queryMux = ds.newQueryMux() + + backend.Logger.Info("NewPIWebAPIDatasource Created") + return ds, nil } @@ -85,6 +91,34 @@ func (d *Datasource) Dispose() { d.httpClient.CloseIdleConnections() } +func (d *Datasource) updateRate() { + d.datasourceMutex.Lock() + /// show stats + modCall := d.totalCalls % 50 + if modCall == 0 { + backend.Logger.Info("Processing QueryTSData End", "CallRate", d.callRate) + } + + // update data + d.totalCalls += 1 + d.callRate = float64(d.totalCalls) / float64(time.Now().Unix()-d.initalTime.Unix()) + + // backpressure + if d.callRate > 500 { + time.Sleep(time.Duration(d.callRate) * time.Millisecond) + backend.Logger.Info("Processing QueryTSData BackPressure", "CallRate", d.callRate) + } + + // reset every 5 minutes + if time.Since(d.initalTime).Seconds() > 30 { + d.initalTime = time.Now() + d.totalCalls = 1 + d.callRate = float64(d.totalCalls) / float64(time.Now().Unix()-d.initalTime.Unix()) + backend.Logger.Info("Processing QueryTSData ResetTime", "CallRate", d.callRate) + } + d.datasourceMutex.Unlock() +} + // newQueryMux creates a new query mux used for routing queries to the correct handler. func (d *Datasource) newQueryMux() *datasource.QueryTypeMux { mux := datasource.NewQueryTypeMux() @@ -112,23 +146,19 @@ func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataReques // TODO: Missing functionality: Add Replace Bad Values // QueryTSData is called by Grafana when a user executes a time series data query. func (d *Datasource) QueryTSData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { - // //TODO: Remove this debug information // jsonReq, err := json.Marshal(req) // if err != nil { // return nil, fmt.Errorf("error marshaling QueryDataRequest: %v", err) // } - - // backend.Logger.Info("QueryDataRequest: ", string(jsonReq)) + // backend.Logger.Info("QueryDataRequest: ", "REQUEST", string(jsonReq)) // end remove this debug information - processedPIWebAPIQueries := make(map[string][]PiProcessedQuery) datasourceUID := req.PluginContext.DataSourceInstanceSettings.UID // Process queries generic query objects and turn them into a suitable format for the PI Web API for _, q := range req.Queries { - backend.Logger.Info("Processing Query", "RefID", q.RefID) - processedPIWebAPIQueries[q.RefID] = d.processQuery(ctx, q, datasourceUID) + processedPIWebAPIQueries[q.RefID] = d.processQuery(q, datasourceUID) } // Send the queries to the PI Web API @@ -137,6 +167,9 @@ func (d *Datasource) QueryTSData(ctx context.Context, req *backend.QueryDataRequ // Convert the PI Web API response into Grafana frames response := d.processBatchtoFrames(processedQueries_temp) + // Update rate and do backpressure + d.updateRate() + return response, nil } @@ -162,7 +195,7 @@ func (d *Datasource) QueryAnnotations(ctx context.Context, req *backend.QueryDat ), ) - backend.Logger.Info("Processing Annotation Query", "RefID", q.RefID) + // backend.Logger.Info("Processing Annotation Query", "RefID", q.RefID) // Process the annotation query request, extracting only the useful information ProcessedAnnotationQuery := d.processAnnotationQuery(ctx, q) span.AddEvent("Completed processing annotation query request") @@ -174,7 +207,7 @@ func (d *Datasource) QueryAnnotations(ctx context.Context, req *backend.QueryDat if len(ProcessedAnnotationQuery.Attributes) > 0 { attributeURLs, err := ProcessedAnnotationQuery.getEventFrameAttributeQueryURL() if err != nil { - backend.Logger.Error("Error getting attribute URLs", "Error", err) + return nil, fmt.Errorf("error getting attribute URLs: %w", err) } batchReq = d.buildAnnotationBatch(url, attributeURLs...) } else { @@ -192,8 +225,7 @@ func (d *Datasource) QueryAnnotations(ctx context.Context, req *backend.QueryDat annotationFrame, err := convertAnnotationResponseToFrame(ProcessedAnnotationQuery.RefID, r, ProcessedAnnotationQuery.AttributesEnabled) if err != nil { - backend.Logger.Error("error converting response to frame: %w", err) - continue + return nil, fmt.Errorf("error converting response to frame: %w", err) } span.AddEvent("Converted response to Grafana frame") diff --git a/pkg/plugin/datasource_models.go b/pkg/plugin/datasource_models.go index b45c4b1..1a28b7b 100644 --- a/pkg/plugin/datasource_models.go +++ b/pkg/plugin/datasource_models.go @@ -3,6 +3,7 @@ package plugin import ( "net/http" "sync" + "time" "github.com/go-co-op/gocron" "github.com/gorilla/websocket" @@ -17,13 +18,16 @@ type Datasource struct { httpClient *http.Client webIDCache WebIDCache channelConstruct map[string]StreamChannelConstruct + datasourceMutex *sync.Mutex scheduler *gocron.Scheduler websocketConnectionsMutex *sync.Mutex - sendersByWebIDMutex *sync.Mutex websocketConnections map[string]*websocket.Conn sendersByWebID map[string]map[*backend.StreamSender]bool streamChannels map[string]chan []byte dataSourceOptions *PIWebAPIDataSourceJsonData + initalTime time.Time + totalCalls int + callRate float64 } type PIWebAPIDataSourceJsonData struct { diff --git a/pkg/plugin/helpers.go b/pkg/plugin/helpers.go index 9289132..1d60862 100644 --- a/pkg/plugin/helpers.go +++ b/pkg/plugin/helpers.go @@ -4,33 +4,81 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "io" "net/http" "reflect" + "strings" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" ) +func replaceAccentsWithEscape(s string) string { + // Define a mapping of accents to their corresponding escape sequences + accentMap := map[rune]string{ + 'á': "%C3%A1", 'à': "%C3%A0", 'â': "%C3%A2", 'ä': "%C3%A4", 'ã': "%C3%A3", 'å': "%C3%A5", + 'é': "%C3%A9", 'è': "%C3%A8", 'ê': "%C3%AA", 'ë': "%C3%AB", + 'í': "%C3%AD", 'ì': "%C3%AC", 'î': "%C3%AE", 'ï': "%C3%AF", + 'ó': "%C3%B3", 'ò': "%C3%B2", 'ô': "%C3%B4", 'ö': "%C3%B6", 'õ': "%C3%B5", 'ø': "%C3%B8", + 'ú': "%C3%BA", 'ù': "%C3%B9", 'û': "%C3%BB", 'ü': "%C3%BC", + 'ñ': "%C3%B1", + 'ç': "%C3%A7", + 'Á': "%C3%81", 'À': "%C3%80", 'Â': "%C3%82", 'Ä': "%C3%84", 'Ã': "%C3%83", 'Å': "%C3%85", + 'É': "%C3%89", 'È': "%C3%88", 'Ê': "%C3%8A", 'Ë': "%C3%8B", + 'Í': "%C3%8D", 'Ì': "%C3%8C", 'Î': "%C3%8E", 'Ï': "%C3%8F", + 'Ó': "%C3%93", 'Ò': "%C3%92", 'Ô': "%C3%94", 'Ö': "%C3%96", 'Õ': "%C3%95", 'Ø': "%C3%98", + 'Ú': "%C3%9A", 'Ù': "%C3%99", 'Û': "%C3%9B", 'Ü': "%C3%9C", + 'Ñ': "%C3%91", + 'Ç': "%C3%87", + '|': "%7C", '(': "%28", ')': "%29", + } + + var result strings.Builder + + // Iterate over each character in the input string + for _, char := range s { + // Check if the character is an accent + if escapeSeq, ok := accentMap[char]; ok { + // If it is, append the escape sequence to the result + result.WriteString(escapeSeq) + } else { + // If it's not an accent, append the character as it is + result.WriteRune(char) + } + } + + return strings.ReplaceAll(result.String(), ` `, `%20`) +} + // apiGet performs a GET request against the PI Web API. It returns the response body as a byte slice. // If the request fails, an error is returned. -func (d *Datasource) apiGet(ctx context.Context, path string) ([]byte, error) { - uri := d.settings.URL + path - req, err := http.NewRequest(http.MethodGet, uri, nil) +func (d Datasource) apiGet(ctx context.Context, path string) ([]byte, error) { + var uri = d.settings.URL + var pathEscaped = replaceAccentsWithEscape(path) + if strings.HasSuffix(uri, "/") { + uri = uri + pathEscaped + } else { + uri = uri + "/" + pathEscaped + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } - resp, err := d.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() + + if resp.StatusCode >= 300 { + return nil, fmt.Errorf("request failed, status: %v", resp.Status) + } + body, err := io.ReadAll(resp.Body) if err != nil { + backend.Logger.Error("API Get request failed", "error", err, "uri", uri, "body", string(body)) return nil, err } return body, nil @@ -39,20 +87,27 @@ func (d *Datasource) apiGet(ctx context.Context, path string) ([]byte, error) { // apiBatchRequest performs a batch request against the PI Web API. It returns the response body as a byte slice. // If the request fails, an error is returned. func (d *Datasource) apiBatchRequest(ctx context.Context, BatchSubRequests interface{}) ([]byte, error) { - uri := d.settings.URL + "/batch" + var uri = d.settings.URL + if strings.HasSuffix(uri, "/") { + uri = uri + "batch" + } else { + uri = uri + "/batch" + } + jsonValue, err := json.Marshal(BatchSubRequests) if err != nil { - return nil, err + backend.Logger.Error("Batch request create", "error", err) + return nil, fmt.Errorf("request failed. parsing request") } req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, bytes.NewBuffer(jsonValue)) if err != nil { backend.Logger.Error("Batch request create", "error", err) - return nil, err + return nil, fmt.Errorf("request failed") } - req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", "Grafana/grafana-osisoft") req.Header.Set("X-Requested-With", "message/http") req.Header.Set("X-PIWEBAPI-HTTP-METHOD", "GET") req.Header.Set("X-PIWEBAPI-RESOURCE-ADDRESS", uri) @@ -60,14 +115,24 @@ func (d *Datasource) apiBatchRequest(ctx context.Context, BatchSubRequests inter resp, err := d.httpClient.Do(req) if err != nil { backend.Logger.Error("Batch request do", "error", err) - return nil, err + return nil, fmt.Errorf("request timeout") } - defer resp.Body.Close() + + defer func() { + err := resp.Body.Close() + if err != nil { + backend.Logger.Error("Batch request failed. body closed", "error", err, "uri", uri) + } + }() body, err := io.ReadAll(resp.Body) if err != nil { - backend.Logger.Error("Batch request failed", "error", err, "uri", uri, "body", string(body)) - return nil, err + backend.Logger.Error("Batch request failed", "error", err, "uri", uri) + return nil, fmt.Errorf("request failed. failed reading response body") + } + + if resp.StatusCode != http.StatusMultiStatus { + return nil, fmt.Errorf("request failed, status: %v", resp.Status) } return body, nil @@ -229,240 +294,175 @@ func parseTimestampValue(val reflect.Value) (reflect.Value, error) { return reflect.Value{}, fmt.Errorf("timestamp value must be a string") } - ts := getTimeStamp(val) + ts, err := getTimeStamp(val) + // If time.Parse returns an error, return the error immediately. + if err != nil { + return reflect.Value{}, fmt.Errorf("error parsing timestamp value: %v", err) + } + + // Return an error if the timestamp value is invalid. + if !ts.IsValid() { + return reflect.Value{}, fmt.Errorf("error parsing timestamp value: invalid timestamp") + } + if ts.Kind() == reflect.Interface && ts.Interface() != nil { - return reflect.Value{}, fmt.Errorf("error parsing timestamp value: %v", ts.Interface()) + return reflect.Value{}, fmt.Errorf("error parsing timestamp value") } return ts, nil } -// TODO: Code simplification: Determine if we should create metadata here. -// if we passed in the entire query we could do that. -// if we pass a pointer to the webid cache and the webid that might simplify things +func updateBadData(index int, fp FrameProcessed, timestamp time.Time, noDataReplace string) FrameProcessed { + // reflect + zeroVal := reflect.Zero(fp.sliceType.Elem()) + valuesValue := reflect.ValueOf(fp.values) + // update + switch noDataReplace { + case "Null": + fp.timestamps = append(fp.timestamps, timestamp) + fp.badValues = append(fp.badValues, index) + fp.values = reflect.Append(valuesValue, zeroVal).Interface() + case "Keep": + fp.timestamps = append(fp.timestamps, timestamp) + fp.values = reflect.Append(valuesValue, zeroVal).Interface() + case "0": + fp.timestamps = append(fp.timestamps, timestamp) + fp.values = reflect.Append(valuesValue, zeroVal).Interface() + case "Previous": + fp.timestamps = append(fp.timestamps, timestamp) + fp.values = reflect.Append(valuesValue, fp.prevVal).Interface() + case "Drop": + default: + fp.timestamps = append(fp.timestamps, timestamp) + fp.badValues = append(fp.badValues, index) + fp.values = reflect.Append(valuesValue, zeroVal).Interface() + } + backend.Logger.Debug("Update bad data", "no_replace", noDataReplace, "zero", zeroVal.Interface()) + return fp +} -// TODO: Code cleanup: handle this directly using slices of pointers. -// TODO: Missing functionality: Add support for replacing bad data. -func convertProcessedQueryToDataFrame(processedQuery PiProcessedQuery, d *Datasource, SummaryType string) (*data.Frame, error) { - var labels map[string]string +func compatible(actual reflect.Type, expected reflect.Type) bool { + a := actual.Kind() + k := expected.Kind() + return (k == reflect.Int || k == reflect.Int32 || + k == reflect.Int64 || k == reflect.Float32 || + k == reflect.Float64) && (a == reflect.Int || a == reflect.Int32 || + a == reflect.Int64 || a == reflect.Float32 || + a == reflect.Float64) +} + +func convertItemsToDataFrame(processedQuery *PiProcessedQuery, d *Datasource, SummaryType string) (*data.Frame, error) { + items := *processedQuery.Response.getItems(SummaryType) webID := processedQuery.WebID includeMetaData := processedQuery.UseUnit - items := *processedQuery.Response.getItems(SummaryType) - SliceType := d.getTypeForWebID(webID) - digitalState := d.getDigitalStateForWebID(webID) + digitalStates := processedQuery.DigitalStates + noDataReplace := processedQuery.getSummaryNoDataReplace() + + digitalStateValues := make([]string, 0) + sliceType := d.getTypeForWebID(webID) + + fP := FrameProcessed{ + sliceType: sliceType, + prevVal: reflect.Zero(sliceType), + values: reflect.MakeSlice(reflect.SliceOf(sliceType.Elem()), 0, 0).Interface(), + badValues: make([]int, 0), + timestamps: make([]time.Time, 0), + } + + // get frame name + frameName := getDataLabel(d.isUsingNewFormat(), processedQuery, d.getPointTypeForWebID(webID), SummaryType) + + var labels map[string]string + var digitalState = d.getDigitalStateForWebID(webID) - frameName := getDataLabel(d.isUsingNewFormat(), &processedQuery, d.getPointTypeForWebID(webID), SummaryType) dataFrameName := frameName["name"] + frame := data.NewFrame(dataFrameName) + if d.isUsingNewFormat() { labels = frameName } - frame := data.NewFrame(dataFrameName) - - var timestamps []time.Time - badValues := make([]int, 0) - var values any - values = reflect.MakeSlice(reflect.SliceOf(SliceType.Elem()), 0, 0).Interface() - digitalStateValues := make([]int32, 0) for i, item := range items { - var val reflect.Value - val = reflect.ValueOf(item.Value) - - //handle value being a timestamp, the PIWab API returns a timestamp as a string - // we need to convert it to a time.Time - if SliceType == reflect.TypeOf([]time.Time{}) { - var err error - val, err = parseTimestampValue(val) - if err != nil { - continue - } - } - - // if the value is valid, get the underlying value - // we need to complete both checks to prevent a panic on a null value - if val.IsValid() && val.Kind() == reflect.Ptr { - val = val.Elem() - } - - if !val.IsValid() { - timestamps = append(timestamps, item.Timestamp) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() + if item.Value == nil { + backend.Logger.Warn("item.Value is nil", "value", item.Value, "item", item) + fP = updateBadData(i, fP, item.Timestamp, noDataReplace) continue } - // if the value isn't good, or is not the same type as the slice, - // add it to the list of bad values and nullify later - //TODO we should make this pattern match the query options - if val.Type().Kind() != SliceType.Elem().Kind() || digitalState || !item.isGood() { - - timestamps = append(timestamps, item.Timestamp) - if digitalState { - var pds PointDigitalState - if b, err := json.Marshal(item.Value); err == nil { - if err := json.Unmarshal(b, &pds); err != nil { - backend.Logger.Error("Error unmarshalling digital state", err) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() - continue - } - pdsValue := reflect.ValueOf(pds.Name) - values = reflect.Append(reflect.ValueOf(values), pdsValue).Interface() - digitalStateValues = append(digitalStateValues, int32(pds.Value)) - continue - } else { - backend.Logger.Error("Error unmarshalling digital state", err) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() - continue - } - } + fP.val = reflect.ValueOf(item.Value) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() + if !fP.val.IsValid() { + backend.Logger.Warn("Is Not Valid", "value", item.Value, "item", item) + fP = updateBadData(i, fP, item.Timestamp, noDataReplace) continue } - timestamps = append(timestamps, item.Timestamp) - values = reflect.Append(reflect.ValueOf(values), val).Interface() - } - - // Convert the slice of values to a slice of pointers to the values - // This is so that we can nullify the values that are "bad" - // "Bad" values are values such as system type values that cannot be represented - // in the slice type, or values that are not "good" - valuepointers := convertSliceToPointers(values, badValues) - - timeField := data.NewField("time", nil, timestamps) - valueField := data.NewField(dataFrameName, labels, valuepointers) - - fieldConfig := &data.FieldConfig{} - - if includeMetaData { - fieldConfig.Unit = d.getUnitsForWebID(webID) - fieldConfig.Description = d.getDescriptionForWebID(webID) - } - - valueField.SetConfig(fieldConfig) - - frame.Fields = append(frame.Fields, - timeField, - valueField, - ) - - if digitalState { - frame.Fields = append(frame.Fields, - data.NewField(frameName["name"]+".Value", frameName, digitalStateValues), - ) - } - // create a metadata struct for the frame so we can set it later. - frame.Meta = &data.FrameMeta{} - return frame, nil -} - -// TODO: FIXME: Remove this function once replaced -func convertItemsToDataFrame(frameName map[string]string, items []PiBatchContentItem, d *Datasource, webID string, includeMetaData bool, digitalStates bool) (*data.Frame, error) { - var labels map[string]string - - dataFrameName := frameName["name"] - if d.isUsingNewFormat() { - labels = frameName - } - - frame := data.NewFrame(dataFrameName) - SliceType := d.getTypeForWebID(webID) - digitalState := d.getDigitalStateForWebID(webID) - - var timestamps []time.Time - badValues := make([]int, 0) - var values any - values = reflect.MakeSlice(reflect.SliceOf(SliceType.Elem()), 0, 0).Interface() - digitalStateValues := make([]int32, 0) - - for i, item := range items { - var val reflect.Value - val = reflect.ValueOf(item.Value) + // if the value is valid, get the underlying value + // we need to complete both checks to prevent a panic on a null value + if fP.val.IsValid() && fP.val.Kind() == reflect.Ptr { + fP.val = fP.val.Elem() + } - //handle value being a timestamp, the PIWab API returns a timestamp as a string + // handle value being a timestamp, the PIWab API returns a timestamp as a string // we need to convert it to a time.Time - if SliceType == reflect.TypeOf([]time.Time{}) { + if fP.sliceType == reflect.TypeOf([]time.Time{}) { var err error - val, err = parseTimestampValue(val) + fP.val, err = parseTimestampValue(fP.val) if err != nil { + backend.Logger.Error("Error parseTimestampValue", "error", err.Error(), "kind", fP.val.Kind().String(), "item", item) continue } } - // if the value is valid, get the underlying value - // we need to complete both checks to prevent a panic on a null value - if val.IsValid() && val.Kind() == reflect.Ptr { - val = val.Elem() - } - - if !val.IsValid() { - timestamps = append(timestamps, item.Timestamp) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() - backend.Logger.Warn("Is Not Valid") - continue - } + backend.Logger.Debug("Converting Items to Data frame", "item", item) // if the value isn't good, or is not the same type as the slice, // add it to the list of bad values and nullify later //TODO we should make this pattern match the query options - if val.Type().Kind() != SliceType.Elem().Kind() || digitalState || !item.isGood() { - timestamps = append(timestamps, item.Timestamp) - if digitalState { - var pds PointDigitalState - if b, err := json.Marshal(item.Value); err == nil { - if err := json.Unmarshal(b, &pds); err != nil { - backend.Logger.Error("Error unmarshalling digital state", err) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() - continue - } - pdsValue := reflect.ValueOf(pds.Name) - values = reflect.Append(reflect.ValueOf(values), pdsValue).Interface() - digitalStateValues = append(digitalStateValues, int32(pds.Value)) - continue + _, digitalState := item.Value.(map[string]interface{}) + if !item.isGood() { + fP = updateBadData(i, fP, item.Timestamp, noDataReplace) + } else if digitalState { + var pds PointDigitalState + if b, err := json.Marshal(item.Value); err == nil { + if err := json.Unmarshal(b, &pds); err == nil { + fP.timestamps = append(fP.timestamps, item.Timestamp) + digitalStateValues = append(digitalStateValues, pds.Name) + pdsValue := reflect.ValueOf(pds.Value) + itemValue := pdsValue.Convert(fP.sliceType.Elem()) + fP.values = reflect.Append(reflect.ValueOf(fP.values), itemValue).Interface() + fP.prevVal = itemValue } else { + // should not happen backend.Logger.Error("Error unmarshalling digital state", err) - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() - continue + fP = updateBadData(i, fP, item.Timestamp, noDataReplace) } + } else { + // should not happen + backend.Logger.Error("Error unmarshalling digital state", err) + fP = updateBadData(i, fP, item.Timestamp, noDataReplace) } - - badValues = append(badValues, i) - zeroVal := reflect.Zero(SliceType.Elem()) - valuesValue := reflect.ValueOf(values) - values = reflect.Append(valuesValue, zeroVal).Interface() - continue + } else if fP.val.Type().Kind() != fP.sliceType.Elem().Kind() { + backend.Logger.Warn("Mismatch type", "ValKind", fP.val.Type().String(), "Val", fP.val.Interface(), "SliceKind", fP.sliceType.Elem().String(), "item", item) + if compatible(fP.val.Type(), fP.sliceType.Elem()) { + fP.timestamps = append(fP.timestamps, item.Timestamp) + fP.values = reflect.Append(reflect.ValueOf(fP.values), fP.val.Convert(fP.sliceType.Elem())).Interface() + fP.prevVal = fP.val + } else { + fP = updateBadData(i, fP, item.Timestamp, noDataReplace) + } + } else { + fP.timestamps = append(fP.timestamps, item.Timestamp) + fP.values = reflect.Append(reflect.ValueOf(fP.values), fP.val).Interface() + fP.prevVal = fP.val } - - timestamps = append(timestamps, item.Timestamp) - values = reflect.Append(reflect.ValueOf(values), val).Interface() } // Convert the slice of values to a slice of pointers to the values // This is so that we can nullify the values that are "bad" // "Bad" values are values such as system type values that cannot be represented // in the slice type, or values that are not "good" - valuepointers := convertSliceToPointers(values, badValues) + valuepointers := convertSliceToPointers(fP.values, fP.badValues) fieldConfig := &data.FieldConfig{} if includeMetaData { @@ -470,8 +470,8 @@ func convertItemsToDataFrame(frameName map[string]string, items []PiBatchContent fieldConfig.Description = d.getDescriptionForWebID(webID) } - timeField := data.NewField("time", nil, timestamps) - if (digitalState && digitalStates) || !digitalState { + timeField := data.NewField("time", nil, fP.timestamps) + if !digitalState || !digitalStates { valueField := data.NewField(dataFrameName, labels, valuepointers) frame.Fields = append(frame.Fields, timeField, @@ -485,15 +485,16 @@ func convertItemsToDataFrame(frameName map[string]string, items []PiBatchContent valueField, ) } + // create a metadata struct for the frame so we can set it later. frame.Meta = &data.FrameMeta{} return frame, nil } -func getTimeStamp(input reflect.Value) reflect.Value { +func getTimeStamp(input reflect.Value) (reflect.Value, error) { if input.Kind() != reflect.String { // return an error value if the input is not a string - return reflect.ValueOf(errors.New("input is not a string")) + return reflect.Value{}, fmt.Errorf("input is not a string") } // parse the timestamp string @@ -501,9 +502,9 @@ func getTimeStamp(input reflect.Value) reflect.Value { timestamp, err := time.Parse(timeLayout, input.String()) if err != nil { // return an error value if the timestamp string is invalid - return reflect.ValueOf(err) + return reflect.Value{}, err } // return a reflect.Value of type time.Time - return reflect.ValueOf(timestamp) + return reflect.ValueOf(timestamp), nil } diff --git a/pkg/plugin/steam.go b/pkg/plugin/steam.go index 22292a7..3598c7a 100644 --- a/pkg/plugin/steam.go +++ b/pkg/plugin/steam.go @@ -19,6 +19,7 @@ type StreamChannelConstruct struct { WebID string IntervalNanoSeconds int64 tagLabel string + query *PiProcessedQuery } type StreamingResponse struct { @@ -71,7 +72,7 @@ func (d *Datasource) PublishStream(ctx context.Context, req *backend.PublishStre func (d *Datasource) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error { errChan := make(chan error) - backend.Logger.Info("Streaming: RunStream called", "Path", req.Path) + // backend.Logger.Info("Streaming: RunStream called", "Path", req.Path) // run each channel subscription in a goroutine. go d.subscribeToWebsocketChannel(ctx, req.Path, sender, errChan) return <-errChan @@ -88,6 +89,7 @@ func (d *Datasource) subscribeToWebsocketChannel(ctx context.Context, Path strin } WebID := d.channelConstruct[Path].WebID TagLabel := d.channelConstruct[Path].tagLabel + Query := d.channelConstruct[Path].query // Get or create a websocket connection for the given WebID // try to reuse an existing connection if one exists, and create a new one if it doesn't @@ -103,7 +105,7 @@ func (d *Datasource) subscribeToWebsocketChannel(ctx context.Context, Path strin // run the send stream messages as a routine, use the context so that the routine // will be cancelled when the context is cancelled - go d.sendStreamMessagesToSender(ctx, WebID, sender, Path, errchan, d.streamChannels[WebID], TagLabel) + go d.sendStreamMessagesToSender(ctx, WebID, sender, Path, errchan, d.streamChannels[WebID], TagLabel, Query) } func (d *Datasource) getOrCreateWebsocketConnection(ctx context.Context, WebID string) error { @@ -129,13 +131,13 @@ func (d *Datasource) getOrCreateWebsocketConnection(ctx context.Context, WebID s go d.readWebsocketMessages(conn, WebID, streamChannel) } else { - backend.Logger.Info("Streaming: Reusing existing websocket connection", "WebID", WebID) + // backend.Logger.Info("Streaming: Reusing existing websocket connection", "WebID", WebID) } return nil } func (d *Datasource) createWebsocketConnection(WebID string) (*websocket.Conn, error) { - backend.Logger.Info("Streaming: Creating new websocket connection", "WebID", WebID) + // backend.Logger.Info("Streaming: Creating new websocket connection", "WebID", WebID) if WebID == "" { backend.Logger.Error("Streaming: WebID is empty") return nil, errors.New("WebID is empty") @@ -166,13 +168,13 @@ func (d *Datasource) readWebsocketMessages(conn *websocket.Conn, WebID string, s if !ok { return } - backend.Logger.Info("Error reading websocket message, closing all associated streams:", "err", err) - d.sendersByWebIDMutex.Lock() + // backend.Logger.Info("Error reading websocket message, closing all associated streams:", "err", err) + d.datasourceMutex.Lock() delete(d.websocketConnections, WebID) for s := range d.sendersByWebID[WebID] { delete(d.sendersByWebID[WebID], s) } - d.sendersByWebIDMutex.Unlock() + d.datasourceMutex.Unlock() conn.Close() return } @@ -182,11 +184,11 @@ func (d *Datasource) readWebsocketMessages(conn *websocket.Conn, WebID string, s // FIXME there is a bug here that causes the stream sender to not pick up all events func (d *Datasource) sendStreamMessagesToSender(ctx context.Context, WebID string, senders *backend.StreamSender, - Path string, errchan chan error, streamChannel chan []byte, TagLabel string) { + Path string, errchan chan error, streamChannel chan []byte, TagLabel string, query *PiProcessedQuery) { for { select { case <-ctx.Done(): - backend.Logger.Info("Streaming: sendStreamMessagesToSenders: Context done, checking for orphaned web sockets", "Path", Path) + // backend.Logger.Info("Streaming: sendStreamMessagesToSenders: Context done, checking for orphaned web sockets", "Path", Path) d.removeStreamSender(WebID, senders) d.checkForOrphanedWebSockets(WebID) // FIXME: this needs to be re-added. Removed for testing other bug fixes. @@ -195,7 +197,7 @@ func (d *Datasource) sendStreamMessagesToSender(ctx context.Context, WebID strin return case message, ok := <-streamChannel: if !ok { - backend.Logger.Info("Streaming: Channel closed, checking for orphaned web sockets") + // backend.Logger.Info("Streaming: Channel closed, checking for orphaned web sockets") errchan <- errors.New("streaming: Channel closed") d.removeStreamSender(WebID, senders) d.checkForOrphanedWebSockets(WebID) @@ -204,44 +206,39 @@ func (d *Datasource) sendStreamMessagesToSender(ctx context.Context, WebID strin frame := StreamingResponse{} err := json.Unmarshal(message, &frame) if err != nil { - backend.Logger.Info("Error unmarshalling message:", err) + // backend.Logger.Info("Error unmarshalling message:", err) continue } - items := frame.getItems() - - frameName := map[string]string{ - "name": TagLabel, - } - framedata, _ := convertItemsToDataFrame(frameName, *items, d, WebID, false, false) - d.sendersByWebIDMutex.Lock() + framedata, _ := convertItemsToDataFrame(query, d, "") + d.datasourceMutex.Lock() specsender := d.sendersByWebID[WebID] for s := range specsender { err := s.SendFrame(framedata, data.IncludeDataOnly) if err != nil { - backend.Logger.Info("Error sending frame:", err) - backend.Logger.Info("Streaming: Removing sender for closed channel") + // backend.Logger.Info("Error sending frame:", err) + // backend.Logger.Info("Streaming: Removing sender for closed channel") d.removeStreamSender(WebID, senders) remainingSenders := d.hasSendersForWebID(WebID) - backend.Logger.Info("Streaming: Checking if there are additional senders for WebID", "WebID", WebID, "remainingSenders", remainingSenders) + // backend.Logger.Info("Streaming: Checking if there are additional senders for WebID", "WebID", WebID, "remainingSenders", remainingSenders) if !remainingSenders { - backend.Logger.Info("Streaming: Closing websocket connection for WebID", "WebID", WebID) + // backend.Logger.Info("Streaming: Closing websocket connection for WebID", "WebID", WebID) d.checkForOrphanedWebSockets(WebID) } errchan <- errors.New("streaming: Channel closed") return } } - d.sendersByWebIDMutex.Unlock() + d.datasourceMutex.Unlock() } } } func (d *Datasource) checkForOrphanedWebSockets(WebID string) { - d.sendersByWebIDMutex.Lock() + d.datasourceMutex.Lock() d.websocketConnectionsMutex.Lock() // Check if the WebID has any senders if len(d.sendersByWebID[WebID]) == 0 { - backend.Logger.Info("Streaming: Closing orphaned web socket for WebID", "WebID", WebID) + // backend.Logger.Info("Streaming: Closing orphaned web socket for WebID", "WebID", WebID) // we remove the websocket connection and then close it // this allows us to gracefully handle the error in the readWebsocketMessages function ws := d.websocketConnections[WebID] @@ -249,23 +246,23 @@ func (d *Datasource) checkForOrphanedWebSockets(WebID string) { delete(d.streamChannels, WebID) ws.Close() } - d.sendersByWebIDMutex.Unlock() + d.datasourceMutex.Unlock() d.websocketConnectionsMutex.Unlock() } func (d *Datasource) addStreamSender(WebID string, sender *backend.StreamSender) { - d.sendersByWebIDMutex.Lock() + d.datasourceMutex.Lock() if _, ok := d.sendersByWebID[WebID]; !ok { d.sendersByWebID[WebID] = make(map[*backend.StreamSender]bool) } d.sendersByWebID[WebID][sender] = true - d.sendersByWebIDMutex.Unlock() + d.datasourceMutex.Unlock() } func (d *Datasource) removeStreamSender(WebID string, sender *backend.StreamSender) { - d.sendersByWebIDMutex.Lock() + d.datasourceMutex.Lock() delete(d.sendersByWebID[WebID], sender) - d.sendersByWebIDMutex.Unlock() + d.datasourceMutex.Unlock() } func (d *Datasource) hasSendersForWebID(WebID string) bool { diff --git a/pkg/plugin/timeseries_query.go b/pkg/plugin/timeseries_query.go index 7d5d4a5..cf7d3cb 100644 --- a/pkg/plugin/timeseries_query.go +++ b/pkg/plugin/timeseries_query.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "net/http" "net/url" "regexp" "strconv" @@ -15,15 +16,20 @@ import ( ) type BatchSubRequest struct { - Method string `json:"Method"` - Resource string `json:"Resource"` + Method string `json:"Method"` + Resource string `json:"Resource"` + ParentIds []string `json:"ParentIds"` + Parameters []string `json:"Parameters"` + Headers map[string]string `json:"Headers"` } +type BatchSubRequestMap map[string]BatchSubRequest + // processQuery is the main function for processing queries. It takes a query and returns a slice of PiProcessedQuery // that contains batched queries that are ready to be sent to the PI Web API. // If there is an error, the error is set in the PiProcessedQuery and the slice is returned, the error propogates through // the rest of the processing chain such that a dataframe with metadata is returned to the user to provide feedback to the user. -func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, datasourceUID string) []PiProcessedQuery { +func (d *Datasource) processQuery(query backend.DataQuery, datasourceUID string) []PiProcessedQuery { var ProcessedQuery []PiProcessedQuery var PiQuery Query @@ -32,9 +38,8 @@ func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, d tempJson, err := json.Marshal(query) if err != nil { log.DefaultLogger.Error("Error marshalling query", "error", err) - piQuery := PiProcessedQuery{ - Error: err, + Error: fmt.Errorf("error while processing the query"), } ProcessedQuery = append(ProcessedQuery, piQuery) return ProcessedQuery @@ -43,9 +48,8 @@ func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, d err = json.Unmarshal(tempJson, &PiQuery) if err != nil { log.DefaultLogger.Error("Error unmarshalling query", "error", err) - piQuery := PiProcessedQuery{ - Error: err, + Error: fmt.Errorf("error while processing the query"), } ProcessedQuery = append(ProcessedQuery, piQuery) return ProcessedQuery @@ -68,7 +72,6 @@ func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, d // Upon creating a dashboard the initial query will be empty, so we need to check for that to avoid errors // if the query is empty, we'll return a PiProcessedQuery with an error set. - err = PiQuery.isValidQuery() if err != nil { piQuery := PiProcessedQuery{ @@ -80,15 +83,13 @@ func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, d // At this point we expect that the query is valid, so we can start processing it. // the queries are may contain multiple targets, so we need to loop through them - backend.Logger.Info("Processing Query", "Target", PiQuery.Pi.Target) - - for _, targetBasePath := range PiQuery.Pi.getTargetBasePaths() { - backend.Logger.Info("Processing Query", "targetBasePath", targetBasePath) - for _, target := range PiQuery.Pi.getTargets() { - fullTargetPath := targetBasePath + PiQuery.Pi.getTargetPathSeparator() + target - //create a processed query for the target + for i, targetBasePath := range PiQuery.Pi.getTargetBasePaths() { + for j, attribute := range PiQuery.Pi.Attributes { + fullTargetPath := targetBasePath + PiQuery.Pi.getTargetPathSeparator() + attribute + backend.Logger.Debug("Process Query", "Summary", PiQuery.Pi.Summary) + // Create a processed query for the target piQuery := PiProcessedQuery{ - Label: target, + Label: attribute, UID: datasourceUID, IntervalNanoSeconds: PiQuery.Interval, IsPIPoint: PiQuery.Pi.IsPiPoint, @@ -96,30 +97,52 @@ func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, d FullTargetPath: fullTargetPath, UseUnit: UseUnits, DigitalStates: DigitalStates, + Display: PiQuery.Pi.Display, Regex: PiQuery.Pi.Regex, + Summary: PiQuery.Pi.Summary, + Index: (j + 1) + 100*(i+1), } - // Get the WebID for the target - WebID, err := d.getWebID(ctx, fullTargetPath, PiQuery.Pi.IsPiPoint) - // If there is an error getting the WebID, set the error and move to next target - if err != nil { - log.DefaultLogger.Error("Error getting WebID", "error", err) - piQuery.Error = err - ProcessedQuery = append(ProcessedQuery, piQuery) - continue - } + WebID := d.getCachedWebID(fullTargetPath) - piQuery.WebID = WebID.WebID + // initialize maps + piQuery.BatchRequest = make(map[string]BatchSubRequest) - //Create the subrequest for the overall batch request - batchSubRequest := BatchSubRequest{ - Method: "GET", - Resource: d.settings.URL + PiQuery.getQueryBaseURL() + "&webid=" + WebID.WebID, + var baseUrl = d.settings.URL + if !strings.HasSuffix(baseUrl, "/") { + baseUrl = baseUrl + "/" + } + dataId := fmt.Sprintf("%s_Req%d", query.RefID, 1000+piQuery.Index) + if WebID != nil && WebID.WebID != "" { + piQuery.WebID = WebID.WebID + // DATA FETCH + batchSubRequest := BatchSubRequest{ + Method: "GET", + Resource: baseUrl + PiQuery.getQueryBaseURL() + "&webid=" + WebID.WebID, + Headers: map[string]string{ + "Asset-Path": fullTargetPath, + }, + } + piQuery.BatchRequest[dataId] = batchSubRequest + } else { + parentId := fmt.Sprintf("%s_Req%d", query.RefID, piQuery.Index) + parameter := "$." + parentId + ".Content.WebId" + // WEBID FETCH + var batchSubRequest = BatchSubRequest{ + Method: "GET", + Resource: baseUrl + d.getRequestWebId(fullTargetPath, piQuery.IsPIPoint), + } + piQuery.BatchRequest[parentId] = batchSubRequest + // DATA FETCH + batchSubRequest = BatchSubRequest{ + Method: "GET", + ParentIds: []string{parentId}, + Parameters: []string{parameter}, + Resource: baseUrl + PiQuery.getQueryBaseURL() + "&webId={0}", + } + piQuery.Resource = batchSubRequest.Resource + piQuery.BatchRequest[dataId] = batchSubRequest } - - backend.Logger.Info("Processing Query", "batchSubRequest", batchSubRequest) - - piQuery.BatchRequest = batchSubRequest ProcessedQuery = append(ProcessedQuery, piQuery) } @@ -127,78 +150,131 @@ func (d Datasource) processQuery(ctx context.Context, query backend.DataQuery, d return ProcessedQuery } -func (d Datasource) batchRequest(ctx context.Context, PIWebAPIQueries map[string][]PiProcessedQuery) map[string][]PiProcessedQuery { - for RefID, processed := range PIWebAPIQueries { - batchRequest := make(map[string]BatchSubRequest) - backend.Logger.Info("Processing batch request", "RefID", RefID, "batchRequest", batchRequest) - +func (d *Datasource) batchRequest(ctx context.Context, PIWebAPIQueries map[string][]PiProcessedQuery) map[string][]PiProcessedQuery { + batchRequest := make(map[string]BatchSubRequest) + for _, processedQuery := range PIWebAPIQueries { // create a map of the batch requests. This allows us to map the response back to the original query - for i, p := range processed { - if p.Error != nil { + for _, piQuery := range processedQuery { + if piQuery.Error != nil { continue } - batchRequest[fmt.Sprint(i)] = p.BatchRequest + for key, request := range piQuery.BatchRequest { + batchRequest[key] = request + } } + } - // request the data from the PI Web API - r, err := d.apiBatchRequest(ctx, batchRequest) + // request the data from the PI Web API + batchRequestResponse, err := d.apiBatchRequest(ctx, batchRequest) - // if we get an error back from the PI Web API, we set the error in the PiProcessedQuery and break out of the loop - if err != nil { - log.DefaultLogger.Error("Error in batch request", "RefID", RefID, "error", err) - for i := range processed { - PIWebAPIQueries[RefID][i].Error = err + // process response + if err != nil { + // d.datasourceMutex.Lock() + for RefID, processedQuery := range PIWebAPIQueries { + backend.Logger.Error("Error in batch request", "RefID", RefID, "error", err) + for i := range processedQuery { + PIWebAPIQueries[RefID][i].Error = fmt.Errorf("error during query: %s", err.Error()) } - continue } + // d.datasourceMutex.Unlock() + return PIWebAPIQueries + } - tempresponse := make(map[int]PIBatchResponse) - err = json.Unmarshal(r, &tempresponse) - if err != nil { - // This likely means that the PI Web API returned an error, - // so we'll set the error in the PiProcessedQuery and break out of the loop - log.DefaultLogger.Error("Error unmarshaling batch response", "RefID", RefID, "error", err) - for i := range processed { - PIWebAPIQueries[RefID][i].Error = err + tempresponse := make(map[string]PIBatchResponse) + err = json.Unmarshal(batchRequestResponse, &tempresponse) + if err != nil { + // d.datasourceMutex.Lock() + for RefID, processedQuery := range PIWebAPIQueries { + backend.Logger.Error("Error in Unmarshal", "RefID", RefID, "error", err, "tempresponse", tempresponse) + for i := range processedQuery { + PIWebAPIQueries[RefID][i].Error = fmt.Errorf("error during query. bad response format") } - continue } + // d.datasourceMutex.Unlock() + return PIWebAPIQueries + } + // d.datasourceMutex.Lock() + for RefID, processedQuery := range PIWebAPIQueries { // map the response back to the original query - for i := range processed { - PIWebAPIQueries[RefID][i].Response = tempresponse[i].Content + for i, query := range processedQuery { + // WEBID + var key = fmt.Sprintf("%s_Req%d", RefID, query.Index) + WebIdData, ok := tempresponse[key] + if ok { + if WebIdData.Status == http.StatusOK { + PIWebAPIQueries[RefID][i].WebID = d.saveWebID(WebIdData.Content, query.FullTargetPath, query.IsPIPoint) + } else { + jWebIdData, err := json.Marshal(WebIdData.Content) + if err != nil { + PIWebAPIQueries[RefID][i].Error = err + continue + } + var errorResponse PiBatchDataError + err = json.Unmarshal(jWebIdData, &errorResponse) + if err != nil { + PIWebAPIQueries[RefID][i].Error = err + continue + } + PIWebAPIQueries[RefID][i].Error = fmt.Errorf("api error %d - %s", WebIdData.Status, errorResponse.Error.Errors[0]) + continue + } + } + // DATA + key = fmt.Sprintf("%s_Req%d", RefID, 1000+query.Index) + ResponseData, ok := tempresponse[key] + if ok { + if ResponseData.Status == http.StatusOK { + PIWebAPIQueries[RefID][i].Response = ResponseData.Content.(PiBatchData) + } else { + jResponseData, err := json.Marshal(ResponseData.Content) + if err != nil { + PIWebAPIQueries[RefID][i].Error = err + continue + } + var errorResponse PiBatchDataError + err = json.Unmarshal(jResponseData, &errorResponse) + if err != nil { + PIWebAPIQueries[RefID][i].Error = err + continue + } + PIWebAPIQueries[RefID][i].Error = fmt.Errorf("api error %d - %s", ResponseData.Status, errorResponse.Error.Errors[0]) + } + } else { + PIWebAPIQueries[RefID][i].Error = fmt.Errorf("error finding key %s in response", key) + } } } + // d.datasourceMutex.Unlock() + return PIWebAPIQueries } -func (d Datasource) processBatchtoFrames(processedQuery map[string][]PiProcessedQuery) *backend.QueryDataResponse { +func (d *Datasource) processBatchtoFrames(processedQuery map[string][]PiProcessedQuery) *backend.QueryDataResponse { response := backend.NewQueryDataResponse() for RefID, query := range processedQuery { var subResponse backend.DataResponse - for i, q := range query { - backend.Logger.Info("Processing query", "RefID", RefID, "QueryIndex", i) - + for _, q := range query { // if there is an error in the query, we set the error in the subresponse and break out of the loop returning the error. if q.Error != nil { - backend.Logger.Error("Error processing query", "RefID", RefID, "QueryIndex", i, "error", q.Error) + backend.Logger.Error("Error processing query", "RefID", RefID, "query", q) subResponse.Error = q.Error break } for _, SummaryType := range *q.Response.getSummaryTypes() { - frameName := getDataLabel(d.isUsingNewFormat(), &q, d.getPointTypeForWebID(q.WebID), SummaryType) - frame, err := convertItemsToDataFrame(frameName, *q.Response.getItems(SummaryType), &d, q.WebID, q.UseUnit, q.DigitalStates) + frame, err := convertItemsToDataFrame(&q, d, SummaryType) // if there is an error on a single frame we set metadata and continue to the next frame if err != nil { - backend.Logger.Error("Error processing query", "RefID", RefID, "QueryIndex", i, "error", err.Error) + backend.Logger.Error("Error processing convertItemsToDataFrame", "RefID", RefID, "query", q) + subResponse.Error = q.Error continue } frame.RefID = RefID - frame.Meta.ExecutedQueryString = q.BatchRequest.Resource + frame.Meta.ExecutedQueryString = q.Resource // TODO: enable streaming // If the query is streamable, then we need to set the channel URI @@ -213,7 +289,8 @@ func (d Datasource) processBatchtoFrames(processedQuery map[string][]PiProcessed channel := StreamChannelConstruct{ WebID: q.WebID, IntervalNanoSeconds: q.IntervalNanoSeconds, - tagLabel: frame.Name, + tagLabel: q.Label, + query: &q, } d.channelConstruct[channeluuid.String()] = channel frame.Meta.Channel = channelURI @@ -267,7 +344,6 @@ func getDataLabel(useNewFormat bool, q *PiProcessedQuery, pointType string, summ "type": pointType + summaryNewFormat, } } - } else { // Old format returns just the tag/attribute name frameLabel = map[string]string{ @@ -278,9 +354,11 @@ func getDataLabel(useNewFormat bool, q *PiProcessedQuery, pointType string, summ // Use ReplaceAllString to replace all instances of the search pattern with the replacement string // FIXME: This is working, but graph panels seem to not render the trend. if q.isRegexQuery() { - backend.Logger.Info("Replacing string", "search", *q.Regex.Search, "replace", *q.Regex.Replace) regex := regexp.MustCompile(*q.Regex.Search) frameLabel["name"] = regex.ReplaceAllString(frameLabel["name"], *q.Regex.Replace) + } else if q.Display != nil && strings.TrimSpace(*q.Display) != "" { + // Old format with display name + frameLabel["name"] = strings.TrimSpace(*q.Display) } return frameLabel } @@ -408,27 +486,21 @@ func (q *PIWebAPIQuery) getTargetBasePaths() []string { if q.Target == nil { return []string{} } - - semiIndex := strings.Index(*q.Target, ";") - basePath := "" - if semiIndex == -1 { - basePath = *q.Target - } else { - basePath = (*q.Target)[:semiIndex] - } + basePath := q.getBasePath() // Find and process a pattern like {,< variable2>,..., } startIndex := strings.Index(basePath, "{") endIndex := strings.Index(basePath, "}") if startIndex != -1 && endIndex != -1 && startIndex < endIndex { - prefix := basePath[:startIndex] + globalPrefix := basePath[:startIndex] + globalSuffix := basePath[endIndex+1:] suffixes := basePath[startIndex+1 : endIndex] suffixList := strings.Split(suffixes, ",") basePaths := make([]string, 0, len(suffixList)) for _, suffix := range suffixList { - basePaths = append(basePaths, prefix+strings.TrimSpace(suffix)) + basePaths = append(basePaths, globalPrefix+strings.TrimSpace(suffix)+globalSuffix) } return basePaths } @@ -507,7 +579,7 @@ func (q *Query) getMaxDataPoints() int { func (q Query) getQueryBaseURL() string { var uri string if q.Pi.isExpression() { - uri += "/calculation" + uri += "calculation" if q.Pi.isUseLastValue() { uri += "/times?time=" + q.getTimeRangeURIToComponent() } else { @@ -527,7 +599,7 @@ func (q Query) getQueryBaseURL() string { } uri += "&expression=" + url.QueryEscape(q.Pi.Expression) } else { - uri += "/streamsets" + uri += "streamsets" if q.Pi.isUseLastValue() { uri += "/value?time=" + q.getTimeRangeURIToComponent() } else { diff --git a/pkg/plugin/timeseries_query_models.go b/pkg/plugin/timeseries_query_models.go index 6d4a7ed..31b9fa2 100644 --- a/pkg/plugin/timeseries_query_models.go +++ b/pkg/plugin/timeseries_query_models.go @@ -2,6 +2,7 @@ package plugin import ( "fmt" + "reflect" "time" ) @@ -88,30 +89,18 @@ func (q *PiProcessedQuery) isSummary() bool { return *q.Summary.Basis != "" && len(*q.Summary.Types) > 0 } -func (q *PiProcessedQuery) getSummaryNoDataReplace() (*string, error) { - var response = "" +func (q *PiProcessedQuery) getSummaryNoDataReplace() string { if q.Summary == nil { - return nil, fmt.Errorf("no summary found in query") - } - if q.Summary.Types == nil { - return nil, fmt.Errorf("no summary type found in query") + return "" } if q.Summary.Nodata == nil { - response = "Drop" - return &response, nil + return "" } - response = *q.Summary.Nodata - return &response, nil + return *q.Summary.Nodata } type PIWebAPIQuery struct { - Attributes []struct { - Label string `json:"label"` - Value struct { - Expandable bool `json:"expandable"` - Value string `json:"value"` - } `json:"value"` - } `json:"attributes"` + Attributes []string `json:"attributes"` Datasource struct { Type string `json:"type"` UID string `json:"uid"` @@ -139,19 +128,13 @@ type PIWebAPIQuery struct { Enable *bool `json:"enable"` MaxNumber *int `json:"maxNumber"` } `json:"recordedValues"` - RefID *string `json:"refId"` - Regex *Regex `json:"regex"` - Segments *[]struct { - Label *string `json:"label"` - Value *struct { - Expandable *bool `json:"expandable"` - Value *string `json:"value"` - WebID *string `json:"webId"` - } `json:"value"` - } `json:"segments"` - Summary *QuerySummary `json:"summary"` - Target *string `json:"target"` - UseUnit *struct { + RefID *string `json:"refId"` + Regex *Regex `json:"regex"` + Segments *[]string `json:"segments"` + Summary *QuerySummary `json:"summary"` + Target *string `json:"target"` + Display *string `json:"display"` + UseUnit *struct { Enable *bool `json:"enable"` } `json:"useUnit"` } @@ -179,20 +162,32 @@ type Regex struct { Replace *string `json:"replace"` } +type FrameProcessed struct { + val reflect.Value + prevVal reflect.Value + values any + timestamps []time.Time + badValues []int + sliceType reflect.Type +} + type PiProcessedQuery struct { - Label string `json:"Label"` - WebID string `json:"WebID"` - UID string `json:"-"` - IntervalNanoSeconds int64 `json:"IntervalNanoSeconds"` - IsPIPoint bool `json:"IsPiPoint"` - Streamable bool `json:"isStreamable"` - FullTargetPath string `json:"FullTargetPath"` - ResponseUnits string `json:"ResponseUnits"` - BatchRequest BatchSubRequest `json:"BatchRequest"` - Response PiBatchData `json:"ResponseData"` - UseUnit bool `json:"UseUnit"` - DigitalStates bool `json:"DigitalStates"` + Label string `json:"Label"` + WebID string `json:"WebID"` + UID string `json:"-"` + IntervalNanoSeconds int64 `json:"IntervalNanoSeconds"` + IsPIPoint bool `json:"IsPiPoint"` + Streamable bool `json:"isStreamable"` + FullTargetPath string `json:"FullTargetPath"` + ResponseUnits string `json:"ResponseUnits"` + BatchRequest BatchSubRequestMap `json:"BatchRequest"` + Response PiBatchData `json:"ResponseData"` + UseUnit bool `json:"UseUnit"` + DigitalStates bool `json:"DigitalStates"` + Display *string `json:"Display"` Error error + Index int + Resource string Regex *Regex `json:"Regex"` Summary *QuerySummary `json:"Summary"` } diff --git a/pkg/plugin/timeseries_response_error_model.go b/pkg/plugin/timeseries_response_error_model.go index 3090de1..18228ea 100644 --- a/pkg/plugin/timeseries_response_error_model.go +++ b/pkg/plugin/timeseries_response_error_model.go @@ -1,5 +1,7 @@ package plugin +import "encoding/json" + type ErrorResponse struct { Errors []string `json:"Errors"` } @@ -23,6 +25,19 @@ func (p PiBatchDataError) getSummaryTypes() *[]string { return &typeValues } +func convertError(data interface{}) (*[]string, error) { + raw, err := json.Marshal(data) + if err != nil { + return nil, err + } + var errors ErrorResponse + err = json.Unmarshal(raw, &errors) + if err != nil { + return nil, err + } + return &errors.Errors, nil +} + func createPiBatchDataError(errorMessage *[]string) *PiBatchDataError { errorResponse := &ErrorResponse{Errors: *errorMessage} resContent := &PiBatchDataError{Error: errorResponse} diff --git a/pkg/plugin/timeseries_response_models.go b/pkg/plugin/timeseries_response_models.go index e464430..8c23687 100644 --- a/pkg/plugin/timeseries_response_models.go +++ b/pkg/plugin/timeseries_response_models.go @@ -12,7 +12,7 @@ import ( type PIBatchResponse struct { Status int `json:"Status"` Headers map[string]string `json:"Headers"` - Content PiBatchData `json:"Content"` + Content interface{} `json:"Content"` } type PIBatchResponseBase struct { @@ -58,11 +58,26 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { var rawData map[string]interface{} err := json.Unmarshal(data, &rawData) if err != nil { - backend.Logger.Error("Error unmarshalling batch response", err) + backend.Logger.Error("Error unmarshalling raw data", "error", err.Error()) return err } Content, ok := rawData["Content"].(map[string]interface{}) + + if p.Status != http.StatusOK { + var errors *[]string + if ok { + errors, err = convertError(Content) + if err != nil { + return err + } + } else { + errors = &[]string{rawData["Content"].(string)} + } + p.Content = createPiBatchDataError(errors) + return nil + } + if !ok { backend.Logger.Error("key 'Content' not found in raw JSON", "rawData", rawData) return fmt.Errorf("key 'Content' not found in raw JSON") @@ -70,14 +85,9 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { rawContent, _ := json.Marshal(Content) - if p.Status != http.StatusOK { - temp_error := &ErrorResponse{} - err = json.Unmarshal(rawContent, temp_error) - if err != nil { - backend.Logger.Error("Error Batch Error Response", "Error", err) - return err - } - p.Content = createPiBatchDataError(&temp_error.Errors) + _, ok = Content["WebId"] + if ok { + p.Content = Content return nil } @@ -100,18 +110,32 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { } // Check if the response contained a value or a subitems array of values - _, ok = parentItem["Value"] - if ok { - ResContent := PiBatchDataWithSingleItem{} - err = json.Unmarshal(rawContent, &ResContent) - if err != nil { - backend.Logger.Error("Error unmarshalling batch response", err) - //Return an error Batch Data Response to the user is notified - errMessages := &[]string{"Could not process response from PI Web API"} - p.Content = createPiBatchDataError(errMessages) - return nil + value, exists := parentItem["Value"] + if exists { + _, isFloat := value.(float64) + if isFloat { + ResContent := PiBatchDataWithFloatItem{} + err = json.Unmarshal(rawContent, &ResContent) + if err != nil { + backend.Logger.Error("Error unmarshalling batch response 3", "error", err.Error(), "data", parentItem["Value"]) + //Return an error Batch Data Response to the user is notified + errMessages := &[]string{"Could not process response from PI Web API"} + p.Content = createPiBatchDataError(errMessages) + return nil + } + p.Content = ResContent + } else { + ResContent := PiBatchDataWithSingleItem{} + err = json.Unmarshal(rawContent, &ResContent) + if err != nil { + backend.Logger.Error("Error unmarshalling batch response 3", "error", err.Error(), "data", parentItem["Value"]) + //Return an error Batch Data Response to the user is notified + errMessages := &[]string{"Could not process response from PI Web API"} + p.Content = createPiBatchDataError(errMessages) + return nil + } + p.Content = ResContent } - p.Content = ResContent return nil } @@ -119,7 +143,7 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { itemsSlice, ok := parentItem["Items"].([]interface{}) if !ok { backend.Logger.Error("key 'Items' not found in 'Items'", "Items", parentItem) - backend.Logger.Error("Error unmarshalling batch response", err) + backend.Logger.Error("Error unmarshalling batch response 4") //Return an error Batch Data Response to the user is notified errMessages := &[]string{"Could not process response from PI Web API"} p.Content = createPiBatchDataError(errMessages) @@ -143,7 +167,7 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { ResContent := PiBatchDataSummaryItems{} err = json.Unmarshal(rawContent, &ResContent) if err != nil { - backend.Logger.Error("Error unmarshalling batch response", err) + backend.Logger.Error("Error unmarshalling batch response 5", "error", err.Error()) //Return an error Batch Data Response to the user is notified errMessages := &[]string{"Could not process response from PI Web API"} p.Content = createPiBatchDataError(errMessages) @@ -162,7 +186,7 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { ResContent := PiBatchDataWithoutSubItems{} err = json.Unmarshal(rawContent, &ResContent) if err != nil { - backend.Logger.Error("Error unmarshalling batch response", err) + backend.Logger.Error("Error unmarshalling batch response 6", "error", err.Error()) //Return an error Batch Data Response so the user is notified errMessages := &[]string{"Could not process response from PI Web API"} p.Content = createPiBatchDataError(errMessages) @@ -176,7 +200,7 @@ func (p *PIBatchResponse) UnmarshalJSON(data []byte) error { ResContent := PiBatchDataWithSubItems{} err = json.Unmarshal(rawContent, &ResContent) if err != nil { - backend.Logger.Error("Error unmarshalling batch response", err) + backend.Logger.Error("Error unmarshalling batch response 7", "error", err.Error()) //Return an error Batch Data Response to the user is notified errMessages := &[]string{"Could not process response from PI Web API"} p.Content = createPiBatchDataError(errMessages) diff --git a/pkg/plugin/timeseries_response_single_model.go b/pkg/plugin/timeseries_response_single_model.go index 5c833e1..c74006c 100644 --- a/pkg/plugin/timeseries_response_single_model.go +++ b/pkg/plugin/timeseries_response_single_model.go @@ -12,6 +12,12 @@ type PiBatchDataWithSingleItem struct { Error *string } +type PiBatchDataWithFloatItem struct { + Links map[string]interface{} `json:"Links"` + Items []PiBatchContentItem `json:"Items"` + Error *string +} + func (p PiBatchDataWithSingleItem) getUnits(typeFilter string) string { return p.Items[0].Value.UnitsAbbreviation } @@ -27,3 +33,17 @@ func (p PiBatchDataWithSingleItem) getSummaryTypes() *[]string { typeValues[0] = "" return &typeValues } + +func (p PiBatchDataWithFloatItem) getUnits(typeFilter string) string { + return p.Items[0].UnitsAbbreviation +} + +func (p PiBatchDataWithFloatItem) getItems(typeFilter string) *[]PiBatchContentItem { + return &p.Items +} + +func (p *PiBatchDataWithFloatItem) getSummaryTypes() *[]string { + typeValues := make([]string, 1) + typeValues[0] = "" + return &typeValues +} diff --git a/pkg/plugin/timeseries_response_summary_model.go b/pkg/plugin/timeseries_response_summary_model.go index dc75bcb..ad893ea 100644 --- a/pkg/plugin/timeseries_response_summary_model.go +++ b/pkg/plugin/timeseries_response_summary_model.go @@ -1,7 +1,5 @@ package plugin -import "github.com/grafana/grafana-plugin-sdk-go/backend" - type PiBatchDataSummaryItems struct { Links map[string]interface{} `json:"Links"` Items []struct { @@ -36,7 +34,6 @@ func (p PiBatchDataSummaryItems) getUnits(typeFilter string) string { func (p PiBatchDataSummaryItems) getItems(typeFilter string) *[]PiBatchContentItem { var items []PiBatchContentItem - backend.Logger.Info("summary, getItems", "typeFilter", typeFilter) for _, item := range p.Items[0].Items { if item.Type == typeFilter { items = append(items, item.Value) diff --git a/pkg/plugin/webidcache.go b/pkg/plugin/webidcache.go index 20ccff6..0118b8a 100644 --- a/pkg/plugin/webidcache.go +++ b/pkg/plugin/webidcache.go @@ -1,7 +1,6 @@ package plugin import ( - "context" "encoding/json" "reflect" "strings" @@ -17,6 +16,7 @@ import ( type WebIDCache struct { webIDPaths map[string]string webIDCache map[string]WebIDCacheEntry + duration time.Duration } // newWebIDCache creates a new WebIDCache with initialized maps. @@ -24,6 +24,7 @@ func newWebIDCache() WebIDCache { return WebIDCache{ webIDPaths: make(map[string]string), webIDCache: make(map[string]WebIDCacheEntry), + duration: 1 * time.Hour, } } @@ -100,41 +101,47 @@ type WebIDResponse interface { getUnits() string } -func (d *Datasource) getWebID(ctx context.Context, path string, isPiPoint bool) (WebIDCacheEntry, error) { +func (d *Datasource) getCachedWebID(path string) *WebIDCacheEntry { + d.datasourceMutex.Lock() + value := d._getCachedWebID(path) + d.datasourceMutex.Unlock() + return value +} + +func (d *Datasource) _getCachedWebID(path string) *WebIDCacheEntry { entry, ok := d.webIDCache.webIDCache[path] if ok && time.Now().Before(entry.ExpTime) { log.DefaultLogger.Debug("WebID cache hit", "path", path) d.webIDCache.webIDPaths[entry.WebID] = path - return entry, nil - } - entry = WebIDCacheEntry{} - c, err := d.requestWebID(ctx, path, isPiPoint) - if err != nil { - log.DefaultLogger.Error("WebID cache error", "path", path, "error", err) - return entry, err + return &entry } - log.DefaultLogger.Debug("WebID cache added", "path", path) - d.webIDCache.webIDCache[path] = c - d.webIDCache.webIDPaths[c.WebID] = path - backend.Logger.Info("WebID cache", "entry", c) - return c, nil + return nil } -func (d *Datasource) requestWebID(ctx context.Context, path string, isPiPoint bool) (WebIDCacheEntry, error) { +func (d *Datasource) getRequestWebId(path string, isPiPoint bool) string { uri := "" if isPiPoint { - uri = `/points?selectedFields=WebId;Name;Path;PointType;DigitalSetName;Descriptor;EngineeringUnits&path=\\` + uri = `points?selectedFields=WebId;Name;Path;PointType;DigitalSetName;Descriptor;EngineeringUnits&path=\\` uri += strings.Replace(strings.Replace(path, "|", `\`, -1), ";", `\`, -1) } else { - uri = `/attributes?selectedFields=WebId;Name;Path;Type;DigitalSetName;Description;DefaultUnitsName&path=\\` + uri = `attributes?selectedFields=WebId;Name;Path;Type;DigitalSetName;Description;DefaultUnitsName&path=\\` uri += path } - log.DefaultLogger.Info("WebID request", "uri", uri) + return uri +} +func (d *Datasource) saveWebID(data interface{}, path string, isPiPoint bool) string { + d.datasourceMutex.Lock() + savedWebID := d._saveWebID(data, path, isPiPoint) + d.datasourceMutex.Unlock() + return savedWebID +} - r, e := d.apiGet(ctx, uri) - if e != nil { - return WebIDCacheEntry{}, e +func (d *Datasource) _saveWebID(data interface{}, path string, isPiPoint bool) string { + r, err := json.Marshal(data) + if err != nil { + backend.Logger.Warn("Error saving web id", "data", data) + return "" } var response WebIDResponse @@ -143,19 +150,85 @@ func (d *Datasource) requestWebID(ctx context.Context, path string, isPiPoint bo } else { response = &WebIDResponseAttribute{} } - json.Unmarshal(r, &response) + err = json.Unmarshal(r, &response) + if err != nil { + // Handle unmarshaling error + backend.Logger.Error("Error unmarshaling JSON response", "error", err) + return "" + } - return WebIDCacheEntry{ + entry := WebIDCacheEntry{ Path: path, WebID: response.getWebID(), Type: getValueType(response.getType()), DigitalState: response.getDigitalSetName() != "", - ExpTime: time.Now().Add(5 * time.Minute), + ExpTime: time.Now().Add(d.webIDCache.duration), PointType: response.getType(), Units: response.getUnits(), - }, nil + } + + d.webIDCache.webIDCache[path] = entry + d.webIDCache.webIDPaths[entry.WebID] = path + + return entry.WebID } +// func (d *Datasource) getWebID(ctx context.Context, path string, isPiPoint bool) (WebIDCacheEntry, error) { +// entry, ok := d.webIDCache.webIDCache[path] + +// if ok && time.Now().Before(entry.ExpTime) { +// log.DefaultLogger.Debug("WebID cache hit", "path", path) +// d.webIDCache.webIDPaths[entry.WebID] = path +// return entry, nil +// } +// entry = WebIDCacheEntry{} +// c, err := d.requestWebID(ctx, path, isPiPoint) +// if err != nil { +// log.DefaultLogger.Error("WebID cache error", "path", path, "error", err) +// return entry, err +// } +// if c.WebID == "" { +// err = errors.New("WebID not found") +// log.DefaultLogger.Error("WebID cache error", "path", path, "error", err) +// return entry, err +// } +// d.webIDCache.webIDCache[path] = c +// d.webIDCache.webIDPaths[c.WebID] = path +// return c, nil +// } + +// func (d *Datasource) requestWebID(ctx context.Context, path string, isPiPoint bool) (WebIDCacheEntry, error) { +// uri := "" +// if isPiPoint { +// uri = `points?selectedFields=WebId;Name;Path;PointType;DigitalSetName;Descriptor;EngineeringUnits&path=\\` +// uri += strings.Replace(strings.Replace(path, "|", `\`, -1), ";", `\`, -1) +// } else { +// uri = `attributes?selectedFields=WebId;Name;Path;Type;DigitalSetName;Description;DefaultUnitsName&path=\\` +// uri += path +// } +// r, e := d.apiGet(ctx, uri) +// if e != nil { +// return WebIDCacheEntry{}, e +// } +// var response WebIDResponse +// if isPiPoint { +// response = &WebIDResponsePiPoint{} +// } else { +// response = &WebIDResponseAttribute{} +// } +// json.Unmarshal(r, &response) + +// return WebIDCacheEntry{ +// Path: path, +// WebID: response.getWebID(), +// Type: getValueType(response.getType()), +// DigitalState: response.getDigitalSetName() != "", +// ExpTime: time.Now().Add(d.webIDCache.duration), +// PointType: response.getType(), +// Units: response.getUnits(), +// }, nil +// } + func getValueType(Type string) reflect.Type { var dataType reflect.Type switch Type { @@ -164,12 +237,12 @@ func getValueType(Type string) reflect.Type { case "Byte": dataType = reflect.TypeOf([]byte{}) case "DateTime": - dataType = reflect.TypeOf(time.Time{}) + dataType = reflect.TypeOf([]time.Time{}) case "Single", "Double", "Float16", "Float32", "Float64": dataType = reflect.TypeOf([]float64{}) case "GUID": dataType = reflect.TypeOf([]string{}) - case "Int16", "Int32": + case "Int16", "Int32", "EnumerationValue": dataType = reflect.TypeOf([]int32{}) case "Int64": dataType = reflect.TypeOf([]int64{}) @@ -199,11 +272,18 @@ func cleanWebIDCache(cache WebIDCache) { } func (d *Datasource) getTypeForWebID(webID string) reflect.Type { + d.datasourceMutex.Lock() + webIdType := d._getTypeForWebID(webID) + d.datasourceMutex.Unlock() + return webIdType +} + +func (d *Datasource) _getTypeForWebID(webID string) reflect.Type { path, exists := d.webIDCache.webIDPaths[webID] if exists { entry, exists := d.webIDCache.webIDCache[path] if exists { - entry.ExpTime = time.Now().Add(5 * time.Minute) + entry.ExpTime = time.Now().Add(d.webIDCache.duration) d.webIDCache.webIDCache[path] = entry return entry.Type } @@ -213,11 +293,18 @@ func (d *Datasource) getTypeForWebID(webID string) reflect.Type { } func (d *Datasource) getDigitalStateForWebID(webID string) bool { + d.datasourceMutex.Lock() + webIdState := d._getDigitalStateForWebID(webID) + d.datasourceMutex.Unlock() + return webIdState +} + +func (d *Datasource) _getDigitalStateForWebID(webID string) bool { path, exists := d.webIDCache.webIDPaths[webID] if exists { entry, exists := d.webIDCache.webIDCache[path] if exists { - entry.ExpTime = time.Now().Add(5 * time.Minute) + entry.ExpTime = time.Now().Add(d.webIDCache.duration) d.webIDCache.webIDCache[path] = entry return entry.DigitalState } @@ -227,11 +314,18 @@ func (d *Datasource) getDigitalStateForWebID(webID string) bool { } func (d *Datasource) getPointTypeForWebID(webID string) string { + d.datasourceMutex.Lock() + webIdPointType := d._getPointTypeForWebID(webID) + d.datasourceMutex.Unlock() + return webIdPointType +} + +func (d *Datasource) _getPointTypeForWebID(webID string) string { path, exists := d.webIDCache.webIDPaths[webID] if exists { entry, exists := d.webIDCache.webIDCache[path] if exists { - entry.ExpTime = time.Now().Add(5 * time.Minute) + entry.ExpTime = time.Now().Add(d.webIDCache.duration) d.webIDCache.webIDCache[path] = entry return entry.PointType } @@ -241,11 +335,18 @@ func (d *Datasource) getPointTypeForWebID(webID string) string { } func (d *Datasource) getUnitsForWebID(webID string) string { + d.datasourceMutex.Lock() + webIdUnits := d._getUnitsForWebID(webID) + d.datasourceMutex.Unlock() + return webIdUnits +} + +func (d *Datasource) _getUnitsForWebID(webID string) string { path, exists := d.webIDCache.webIDPaths[webID] if exists { entry, exists := d.webIDCache.webIDCache[path] if exists { - entry.ExpTime = time.Now().Add(5 * time.Minute) + entry.ExpTime = time.Now().Add(d.webIDCache.duration) d.webIDCache.webIDCache[path] = entry return entry.Units } @@ -255,11 +356,18 @@ func (d *Datasource) getUnitsForWebID(webID string) string { } func (d *Datasource) getDescriptionForWebID(webID string) string { + d.datasourceMutex.Lock() + webIdDescription := d._getDescriptionForWebID(webID) + d.datasourceMutex.Unlock() + return webIdDescription +} + +func (d *Datasource) _getDescriptionForWebID(webID string) string { path, exists := d.webIDCache.webIDPaths[webID] if exists { entry, exists := d.webIDCache.webIDCache[path] if exists { - entry.ExpTime = time.Now().Add(5 * time.Minute) + entry.ExpTime = time.Now().Add(d.webIDCache.duration) d.webIDCache.webIDCache[path] = entry return entry.Description } diff --git a/src/datasource.ts b/src/datasource.ts index 466d8db..836b70d 100644 --- a/src/datasource.ts +++ b/src/datasource.ts @@ -9,11 +9,13 @@ import { ScopedVars, AnnotationEvent, DataFrame, + DataQueryRequest, + DataQueryResponse, } from '@grafana/data'; import { BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, DataSourceWithBackend } from '@grafana/runtime'; import { PIWebAPIQuery, PIWebAPIDataSourceJsonData, PiDataServer, PiwebapiInternalRsp, PiwebapiRsp } from './types'; -import { metricQueryTransform } from 'helper'; +import { metricQueryTransform, parseRawQuery } from 'helper'; import { PiWebAPIAnnotationsQueryEditor } from 'query/AnnotationsQueryEditor'; @@ -88,6 +90,19 @@ export class PiWebAPIDatasource extends DataSourceWithBackend): Observable { + if (options.targets.length === 1 && !!options.targets[0].isAnnotation) { + return super.query(options); + } + + const query = this.buildQueryParameters(options); + if (query.targets.length <= 0) { + return of({ data: [] }); + } + + return super.query(query); + } + /** * This method does the discovery of the AF Hierarchy and populates the query user interface segments. * @@ -175,6 +190,72 @@ export class PiWebAPIDatasource extends DataSourceWithBackend) { + options.targets = filter(options.targets, (target) => { + if (!target || !target.target || target.attributes?.length === 0 || target.target === ';' || !!target.hide) { + return false; + } + return !target.target.startsWith('Select AF'); + }); + + options.targets = map(options.targets, (target) => { + if (!!target.rawQuery && !!target.target) { + const { attributes, elementPath } = parseRawQuery(this.templateSrv.replace(target.target, options.scopedVars)); + target.attributes = attributes; + target.elementPath = elementPath; + } + const tar = { + enableStreaming: target.enableStreaming, + target: this.templateSrv.replace(target.elementPath, options.scopedVars), + elementPath: this.templateSrv.replace(target.elementPath, options.scopedVars), + attributes: map(target.attributes, (att) => + this.templateSrv.replace(att.value?.value || att, options.scopedVars) + ), + isAnnotation: !!target.isAnnotation, + segments: map(target.segments, (att) => this.templateSrv.replace(att.value?.value, options.scopedVars)), + display: !!target.display ? this.templateSrv.replace(target.display, options.scopedVars) : undefined, + refId: target.refId, + hide: target.hide, + interpolate: target.interpolate || { enable: false }, + useLastValue: target.useLastValue || { enable: false }, + useUnit: target.useUnit || { enable: false }, + recordedValues: target.recordedValues || { enable: false }, + digitalStates: target.digitalStates || { enable: false }, + webid: target.webid ?? '', + webids: target.webids || [], + regex: target.regex || { enable: false }, + expression: target.expression || '', + summary: target.summary || { types: [] }, + startTime: options.range.from, + endTime: options.range.to, + isPiPoint: !!target.isPiPoint, + scopedVars: options.scopedVars, + }; + + if (tar.expression) { + tar.expression = this.templateSrv.replace(tar.expression, options.scopedVars); + } + + if (tar.summary.types !== undefined) { + tar.summary.types = filter(tar.summary.types, (item) => { + return item !== undefined && item !== null && item !== ''; + }); + } + + return tar; + }); + + return options; + } + /** * Localize the eventFrame dataFrame records to Grafana Annotations. * @param {any} annon - The annotation object. @@ -253,7 +334,7 @@ export class PiWebAPIDatasource extends DataSourceWithBackend { const observable = this.backendSrv.fetch({ - url: `/api/datasources/${this.id}/resources/${path}`, + url: `/api/datasources/${this.id}/resources${path}`, method: 'GET', headers: { 'Content-Type': 'application/json' }, }); diff --git a/src/helper.ts b/src/helper.ts index 5d64498..7e4b4a9 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -3,13 +3,6 @@ import { each, map } from 'lodash'; import { AnnotationQuery, DataFrame, - FieldConfig, - TimeSeries, - FieldType, - TimeSeriesValue, - TIME_SERIES_VALUE_FIELD_NAME, - TIME_SERIES_TIME_FIELD_NAME, - ArrayVector, TableData, MetricFindValue, Field, @@ -50,48 +43,6 @@ export function lowerCaseFirstLetter(string: string): string { return string.charAt(0).toLocaleLowerCase() + string.slice(1); } -export function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame { - const times: number[] = []; - const values: TimeSeriesValue[] = []; - - // Sometimes the points are sent as datapoints - const points = timeSeries.datapoints; - for (const point of points) { - values.push(point[0]); - times.push(point[1] as number); - } - - const fields = [ - { - name: TIME_SERIES_TIME_FIELD_NAME, - type: FieldType.time, - config: {}, - values: new ArrayVector(times), - }, - { - name: timeSeries.target ?? TIME_SERIES_VALUE_FIELD_NAME, - type: FieldType.number, - config: { - unit: timeSeries.unit, - }, - values: new ArrayVector(values), - labels: timeSeries.tags, - }, - ]; - - if (timeSeries.title) { - (fields[1].config as FieldConfig).displayNameFromDS = timeSeries.title; - } - - return { - name: '', - refId: timeSeries.refId, - meta: timeSeries.meta, - fields, - length: values.length, - }; -} - /** * Builds the Grafana metric segment for use on the query user interface. * @@ -150,7 +101,6 @@ export function processAnnotationQuery(annon: AnnotationQuery,dat // Check whether f.values is an array or not to allow for each. // Check whether f.values is an array or not to allow for each. if (Array.isArray(f.values)) { - console.log(f.values) f.values.forEach((value: any) => { if (attribute) { @@ -177,9 +127,7 @@ export function processAnnotationQuery(annon: AnnotationQuery,dat } export function convertToTableData(items: any[], valueData?: any[]): TableData[] { - console.log("items",items) const response: TableData[] = items.map((item: any, index: number) => { - console.log("item",item) const columns = [{ text: 'StartTime' }, { text: 'EndTime' }]; const rows = [item.StartTime, item.EndTime]; if (valueData) { @@ -199,7 +147,6 @@ export function convertToTableData(items: any[], valueData?: any[]): TableData[] rows: [rows], }; }); - console.log("response",response) return response; } diff --git a/src/types.ts b/src/types.ts index c15c1ea..d020ca6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { DataQuery } from '@grafana/schema'; -import { DataSourceJsonData, Labels, QueryResultMeta, TimeSeriesPoints } from '@grafana/data'; +import { DataSourceJsonData } from '@grafana/data'; export interface PiwebapiElementPath { path: string; @@ -12,16 +12,6 @@ export interface PiwebapiInternalRsp { url: string; } -export interface PiwebapTargetRsp { - refId: string; - target: string; - tags: Labels; - datapoints: TimeSeriesPoints; - path?: string; - meta?: QueryResultMeta; - unit?: string; -} - export interface PiwebapiRsp { Name?: string; InstanceType?: string; diff --git a/yarn.lock b/yarn.lock index 09e315c..a7b752d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1537,16 +1537,25 @@ tslib "2.6.0" typescript "5.2.2" -"@grafana/e2e@10.3.3": - version "10.3.3" - resolved "https://registry.yarnpkg.com/@grafana/e2e/-/e2e-10.3.3.tgz#9d71b937124f4c0ce461ce8a31731284159ad742" - integrity sha512-T6AvGcMM7sy9dOPVCBGwv5PTBDnqVUuO1wcZ6gMj12Kyg5LfjJQ8tidiXvDl5Pj0sz/0bwmzcIMgZSn3nwJXBQ== +"@grafana/e2e-selectors@10.4.1", "@grafana/e2e-selectors@latest": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@grafana/e2e-selectors/-/e2e-selectors-10.4.1.tgz#e81d88c12b3902967efce22a9de8231faa32210e" + integrity sha512-AYT/64EoMu3uTA/V+pEEXaQfBlzr2ir9ts6sk1yM/IN2tVIx5HALACLd4LjHG/B+lu2WKC0BKRUdfG2m1M5O+g== + dependencies: + "@grafana/tsconfig" "^1.2.0-rc1" + tslib "2.6.2" + typescript "5.3.3" + +"@grafana/e2e@latest": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@grafana/e2e/-/e2e-10.4.1.tgz#ef8607476c25c201d5f3f6512c8d64a95a16f00a" + integrity sha512-8XQ9EMVg303Ptt3Tq784EQZwAV8ODUkW083rneSobz71sSJmWCZOZeEwbEac/w4tmaAd7sL45lErBh2z0eB7ng== dependencies: "@babel/core" "7.23.2" "@babel/preset-env" "7.23.2" "@cypress/webpack-preprocessor" "5.17.1" - "@grafana/e2e-selectors" "10.3.3" - "@grafana/schema" "10.3.3" + "@grafana/e2e-selectors" "10.4.1" + "@grafana/schema" "10.4.1" "@grafana/tsconfig" "^1.2.0-rc1" "@mochajs/json-file-reporter" "^1.2.0" babel-loader "9.1.3" @@ -1622,6 +1631,13 @@ dependencies: tslib "2.6.0" +"@grafana/schema@10.4.1", "@grafana/schema@latest": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@grafana/schema/-/schema-10.4.1.tgz#acc3b6d0b84585ef4279f203f6f8b64ef6a8bad7" + integrity sha512-d6f5ZeWQ0z9Dl1PQ8vtuMdh/1RJaJjhwQFBrKZnGy0daKkPRw+DuFjre7XHcetWBg9dDX8O2FE7ViOMNImf6Iw== + dependencies: + tslib "2.6.2" + "@grafana/tsconfig@^1.2.0-rc1": version "1.2.0-rc1" resolved "https://registry.npmjs.org/@grafana/tsconfig/-/tsconfig-1.2.0-rc1.tgz" @@ -10004,6 +10020,11 @@ tslib@2.6.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== +tslib@2.6.2, tslib@^2.3.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -10014,11 +10035,6 @@ tslib@^2.1.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslib@^2.3.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -10119,6 +10135,11 @@ typescript@5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== +typescript@5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + ua-parser-js@^1.0.32: version "1.0.33" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4"