jahmin
Version:
A JavaScript framework to build browser friendly Human Machine Interfaces for automation
144 lines (128 loc) • 4.48 kB
text/typescript
import {LitElement} from 'lit-element'
import {litStatesMixin} from 'impera-js'
import {Manager, ServiceManager} from '../ServiceManager.js'
import { systemObject, systemVariable, VarStatusCodes } from '../DataModels/Types.js';
export interface props {
[key:string] : { type : String | Number | Object }
}
export class hmiElement extends litStatesMixin([Manager.dataTree, Manager.errorTray],LitElement) implements systemObject{
name : string
system : string
engine : string
datatree : any
_init : boolean
service_manager : ServiceManager
_local_status : string
_local_value : any
constructor(){
super();
this.name = "";
this.system = "default";
this.engine = "default";
this._init = false;
this.service_manager = Manager;
this._local_status = "UNDEFINED";
this._local_value = undefined;
}
static get properties() : props{
return {
name : { type: String },
system : { type: String },
engine : { type: String },
/* this would be nice to have so that one could set status of
a child component declaratively from the code of the host,
but PROBLEM: lit-Element overrides getter and setter of inherited props
in case the "get properties" is redefined in the child class, in short
if you want to add props in a child class the status prop will not work.
*/
// status : { type: String }
};
}
get value():any
{
if(!this._init) return undefined;
if(this.service_manager.dataTree.ExistVar(this)) return this.datatree[this.system][this.name].value;
else return null;
}
get status():string
{
if(!this._init) return VarStatusCodes.Pending;
if(this.service_manager.dataTree.ExistVar(this)) return this.datatree[this.system][this.name].status;
else return VarStatusCodes.Error;
}
set status(Status:string)
{
if(typeof Status !== "string") return;
if(!this.service_manager.dataTree.ExistVar(this)) return;
const old_val = this.getAttribute("status");
if( old_val !== Status || old_val !== this.status ) {
this.DataUpdate(null,Status);
}
}
on_datatree_update()
{
this.setAttribute("status",this.status);
if( this.status !== this._local_status ) {
let Ev = new CustomEvent("status-changed",{
bubbles:true, composed:true, cancelable:true,
detail:{newStatus : this.status , oldStatus : this._local_status} });
this._local_status = this.status;
this.dispatchEvent(Ev);
}
if(this.value !== this._local_value){
let Ev = new CustomEvent("value-changed",{
bubbles:true, composed:true, cancelable:true,
detail: { newValue : this.value , oldValue : this._local_value } });
this._local_value = this.value;
this.dispatchEvent(Ev);
}
}
connectedCallback()
{
super.connectedCallback();
this.subscribe()
}
subscribe()
{
// maybe here dispatch READY event??
// maybe here resolve a READY promise so one can await it??
Manager.Subscribe(this.engine,this).then(()=>{
this._init = true;
this.on_datatree_update();
this.requestUpdate();
})
}
disconnectedCallback()
{
if(super.disconnectedCallback) super.disconnectedCallback();
Manager.Unsubscribe(this.engine, this);
}
async Write(value:any)
{
return await this.WriteMultiple([this],[value]);
}
async WriteMultiple(targets:systemObject[],values:any[])
{
return await Manager.Write(this.engine, targets, values);
}
async Read()
{
return this.ReadMultiple([this]);
}
async ReadMultiple(targets:systemObject[])
{
return await Manager.Read(this.engine,targets);
}
DataUpdate(Value:any, Status:string):void
{
let sysVar = new systemVariable(this);
sysVar.status = Status;
sysVar.value = Value;
this.DataUpdateMultiple(sysVar);
}
DataUpdateMultiple(sysvar:systemVariable[]|systemVariable):void
{
Manager.Update(sysvar)
}
}
customElements.define("hmi-element",hmiElement);