/*-------------------------------------------------------------------------------------------------------------- * Copyright (c) insite-gmbh. All rights reserved. * Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------------------------*/ import { Injectable } from '@angular/core'; import { Http, Headers } from '@angular/http'; import { InaxConfiguration, ProfileService, InaxSignalR, ConnectionState, IHubService, SignalrWindow, Map} from '../../../@inax/common'; import { PlcWriteTransportPack } from './transport/plcWriteTransportPack'; import { IPlcMappingPack, IPlcBlockInfo, DataChangeEvent, IPlcEventProxy} from './domain'; import { PlcEventProxyFactory } from './internal/plcEventProxyFactory'; import { Observable } from 'rxjs/Rx'; import { Subject } from "rxjs/Subject"; @Injectable() /** * Service class to communicate with inax to handle plc access * @class InaxPlcService * @classdesc Service class to communicate with inax to handle plc access * @author insite-gmbh */ export class InaxPlcService implements IHubService { private static _proxyFactory: PlcEventProxyFactory; private static _plcControllerName: string = "Plc"; private static _connectionStateDelegate: any = null; public get hubName():string{ return "PlcHub"; } public get isHubConnected():boolean{ if(InaxPlcService._connectionStateDelegate != null){ let state = InaxPlcService._connectionStateDelegate(); return state == ConnectionState.Connected; } return false; } /** * constructor. * @param {Http} _http http service to handle web api calls. * @param {Configuration} _configuration configuration specifies signalRHubs, serveraddress,... . * @param {ProfileService} _profileService this service adds authentication data to the webApiRequests. */ constructor(private _http: Http, private _configuration: InaxConfiguration, private _profileService: ProfileService) { } /*********************************************************************** * SIGNALR * ********************************************************************/ /** * Initialize SignalR to create the hubProxy and activate the callback from server to client. * This mehtod will be called from InaxSignalR * @method registerEvents * @param {InaxSignalR} signalR Reference to SignalR */ public registerEvents(signalR: InaxSignalR) { if (this._configuration.UseSignalR && this._configuration.EnabledSignalRHubs.indexOf(this.hubName) >= 0) { InaxPlcService._proxyFactory = new PlcEventProxyFactory(signalR.getHubProxy(this.hubName)); InaxPlcService._connectionStateDelegate = () => signalR.state; } } /** * Create a proxy to subscribe to plc data changes of the given variables (initial datachange will be fired after subscription) * @method createEventProxy * @param {string} group Group name for the callbacks, so you can register variables for this group from different places, * and every watcher of this group gets an update * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) * @param {string} mapping Mapping name of the Variables. Every symbolic variable has to have a Mapping specification * which specifies the area, offset,.. of the variable * @param {Array} variables Names of the symbolic/absolute variables * @param {boolean} abs Switch between Absolute and Symbolic variables * @return {IPlcEventProxy} returns a object with the registered properties to subscribe on changes. */ public createEventProxy(group: string, plcId: string, mapping: string, variables: Array, abs: boolean = false): IPlcEventProxy { if(InaxPlcService._proxyFactory == null) throw "PlcHub not available or not registered!"; return InaxPlcService._proxyFactory.create(group, plcId, mapping, variables, abs); } /*********************************************************************** * WEB API * ********************************************************************/ /** * Read a single variable from the plc * @method readSingle * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) . * @param {string} mapping Mapping name of the Variables. Every symbolic variable has to have a Mapping specification * which specifies the area, offset,.. of the variable. * @param {string} variable Names of the variable. * @return {Observable} returns a Observable which is called with the red data after the read operation is finished. */ public readSingle(plcId: string, mapping: string, variable: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "Read") + "?mappingName=" + plcId + "." + mapping + "&variable=" + variable; let headers = this._profileService.addAuthorization(new Headers()); return this._http.get(accessUrl, { headers }).map(res => res.json()); } /** * Read variables from the plc * @method read * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) * @param {string} mapping Mapping name of the Variables. Every symbolic variable has to have a Mapping specification * which specifies the area, offset,.. of the variable * @param {Array} variables Names of the symbolic/absolute variables * @return {Observable} returns a Observable which is called with the red data after the read operation is finished. */ public read(plcId: string, mapping: string, variable: Array): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "Read") + "?mappingName=" + plcId + "." + mapping + "&variable=" + variable.join(","); let headers = this._profileService.addAuthorization(new Headers()); return this._http.get(accessUrl, { headers }).map(res => res.json()); } /** * Reads the given variables and returns the result as a string * @method readToString * @param {string} command A read command like this: mapping:variable,variable[,variable=value]; * @return {Observable} returns a Observable which is called with the red data after the read operation is finished. */ public readToString(command: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "ReadToString") + "?command=" + this.normalizeUri(command); let headers = this._profileService.addAuthorization(new Headers()); return this._http.get(accessUrl, { headers }).map(res => res.json()); } /** * Reads the given absolut data e.g. "DB1", "W10" or "DB11", "X16_0" or "DB2", "S528_10" * @method readAbs * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) . * @param {string} selector This parameter sets the read area (e.g. * IB -> Input Byte * FB -> Flag Byte * QB -> Output Byte * TM -> Timer * CT -> Counter * DB[number] -> DB) * @param {string} variable This parameter specifies the offset and length. (W10 (offset= 10 length = 2byte (W=Word)), * X15_0 (offset = 15 bitoffset = 0 length = 1bit), B10 (offset = 10 length = 1Byte), S5_10 (offset = 5 length = 12 (becasue datatype is String, * the length of the string is 10 and 2 MetaBytes are in front of a plc string))) * @return {Observable} returns a Observable which is called with the red data after the read operation is finished. */ public readAbs(plcId: string, selector: string, variable: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "ReadAbs") + "?selector=" + plcId + "." + selector + "&variable=" + variable; let headers = this._profileService.addAuthorization(new Headers()); return this._http.get(accessUrl, { headers }).map(res => res.json()); } /** * Write the given data to the plc * @method write * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) . * @param {string} mapping Mapping name of the Variables. Every symbolic variable has to have a Mapping specification * which specifies the area, offset,.. of the variable. * @param {string} variable Names of the variable. * @param {string} value The value to write to the plc. * @return {Observable} returns a Observable which is called with the write state after the write operation is finished. */ public write(plcId: string, mapping: string, variable: string, value: any):Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "Write") + "?mappingName=" + plcId + "." + mapping + "&variable=" + variable; let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, JSON.stringify(value), { headers }).map(res => res.ok); } /** * Write the given data to the plc * @method writeAbs * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) . * @param {string} selector Selector of the Variables like DB100. Every symbolic variable has to have a Mapping specification * which specifies the area, offset,.. of the variable. * @param {string} variable Address of the variable. * @param {string} value The value to write to the plc. * @return {Observable} returns a Observable which is called with the write state after the write operation is finished. */ public writeAbs(plcId: string, selector: string, variable: string, value: any):Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "WriteAbs") + "?selector=" + plcId + "." + selector + "&variable=" + variable; let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, JSON.stringify(value), { headers }).map(res => res.ok); } /** * Executes a plc write query. For this write operation you can specify a where clause, if that clause matches to true the data will be written, otherwise not. * @method writeQuery * @param {string} plcId This id specifies a single plc, the id have to be the same as specified in the * INAX config (with different ids you could adress different plcs or connections) . * @param {string} mapping Mapping name of the Variables. Every symbolic variable has to have a Mapping specification * which specifies the area, offset,.. of the variable. * @param {Map} keyvaluepair the keys and values to write. * @param {string} where This is the were clause, the data will be written only if this parameter matches or it is null * @param {string} from mapping name for the where clause. * @param {boolean} abs true is the value to write is an address and not a symbolic variable * @return {Observable} returns a Observable which is called with the write state after the write operation is finished. */ public writeQuery(plcId: string, mapping: string, keyvaluepair: Map, where: string = null, from: string = null, abs: boolean = false): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "Write"); let headers = this._profileService.addAuthorization(new Headers()); let data: PlcWriteTransportPack = new PlcWriteTransportPack(); if (mapping != null && keyvaluepair != null) { data.To = plcId + "." + mapping; data.Set = keyvaluepair; if (where != null && from != null) { data.From = plcId + "." + from; data.Where = where; } if (abs != null && abs) data.Abs = true; } return this._http.post(accessUrl, JSON.stringify(data), { headers }).map(res => res.ok); } /** * Write data from a given string command * the sharp means absolute adressing * @method writeFromString * @param {string} command This parameter contains a write command to write the given data to the plc. * (e.g.[#]mapping:variable=value[,variable=value]?variable==value,variable=value]; //the sharp means absolute adressing) * @return {Observable} returns a Observable which is called with the write state after the write operation is finished. */ public writeFromString(command: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName,"WriteFromString") + "?command=" + this.normalizeUri(command); let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, null, { headers }).map(res => res.ok); } /** * Add a mapping to the plc * the sharp means absolute adressing * @method addMapping * @param {IPlcMappingPack} mapping This parameter represents the mappingname and additional parameters for the new mapping which shall be added. * @return {Observable} returns a Observable which is called with the result state after the operation is finished. */ public addMapping(mapping: IPlcMappingPack): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "AddMapping"); let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, JSON.stringify(mapping), { headers }).map(res => res.ok); } /** * Add a plc mapping to INAX * @method addMappingFromString * @param {string} mapping This parameter represents the mappingname and additional parameters for the new mapping which shall be added. * (e.g. mapping: type,selector,offset or mapping: Plc.MappingName:TypeName,ValueNumber,ValueOffset or mapping: default.MyMappingName:MyTypeName,DB10,W5) * @return {Observable} returns a Observable which is called with the result state after the operation is finished. */ public addMappingFromString(mapping: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "AddMappingFromString") + "?mapping=" + this.normalizeUri(mapping); let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, null, { headers }).map(res => res.ok); } /** * Remove a plc mapping from INAX * @method removeMappingFromString * @param {string} mapping This parameter represents the mappingname which shall be removed. * @return {Observable} returns a Observable which is called with the result state after the operation is finished. */ public removeMappingFromString(mapping: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName,"RemoveMappingFromString") + "?mapping=" + this.normalizeUri(mapping); let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, null, { headers }).map(res => res.ok); } /** * Open connection to a new plc * @method connect * @param {string} key This parameter represents the connection key for which the connection shall be opened. * @param {string} connectionString This parameter contains the connectioparameter for the new connection (IP address, rack, slot, ...) * @return {Observable} returns a Observable which is called with the result state after the operation is finished. */ public connect(key: string, connectionString: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName,"Connect") + "?key=" + this.normalizeUri(key); let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, connectionString, { headers }).map(res => res.ok); } /** * Close plc connection with the given key * @method disconnect * @param {string} key This parameter represents the connection key for which the connection shall be closed. * @return {Observable} returns a Observable which is called with the result state after the operation is finished. */ public disconnect(key: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName,"Disconnect") + "?key=" + this.normalizeUri(key); let headers = this._profileService.addAuthorization(new Headers()); return this._http.post(accessUrl, null, { headers }).map(res => res.ok); } /** * Close plc connection with the given key * @method readBlockInfo * @param {string} selector This parameter contains the name of the block for which the information shall be determined. (e.g. DB10, FB1, FC2, ...) * @return {Observable} returns a Observable which is called with the result information after the operation is finished. */ public readBlockInfo(selector: string): Observable { let accessUrl = this._configuration.buildRestUrl(InaxPlcService._plcControllerName, "ReadBlockInfo") + this.normalizeUri("?selector=" + selector); let headers = this._profileService.addAuthorization(new Headers()); return this._http.get(accessUrl, { headers }).map(res => res.json()); } private normalizeUri(uri: string): string { return encodeURIComponent(uri); } }