/*-------------------------------------------------------------------------------------------------------------- * 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. *--------------------------------------------------------------------------------------------------------------*/ 'use strict' export /** * plcAbs */ class PlcAbsStat { public Selector : string; public Type : string; public Offset : number; public Length: number; //currently serves for Bools and Strings public CountOfData :number; public Format : number; public Mnemonic : string; constructor(sel? :string, type? :string, offs? :number, len? :number, cod? :number, pointer :number = 0, mnemonic :string = 'DE') { } public static toINAX(absAddress :string, mnemonic :string = 'DE') :PlcAbsStat{ //converts given absolute PLC adress to INAX and returns element with set properties; set mnemonic to 'DE' for german operands absAddress = absAddress.replace(/ /g,''); if (absAddress.substr(0,2) === 'P#'){ if (absAddress.length < 8){ throw Error('address too short'); } //P#[Operand][Offset].[Length][Type][CountOfData] is at least 8 chars return this.splitPlcPointerAddress(absAddress, mnemonic); } else { if (absAddress.length < 3){ throw Error('address too short'); } //[Operand][Type][Offset] is at least 3 chars return this.splitPlcAddress(absAddress, mnemonic); } } public static splitPlcAddress(absAddress :string, mnemonic :string = 'DE') :PlcAbsStat{ //returns [ , , , ] let selector :string, selectorLength : number, offset :number, length :number = null; selector = this.getINAXSelector(absAddress, mnemonic); //removes the Selector part from the Address if (absAddress[0] == 'D') { selectorLength = absAddress.indexOf('.') + 3; } else { selectorLength = 1; } let address = absAddress.substring(selectorLength); //separates Type and Offset(_Length) let type = this.extractINAXType(address); let restAddress = address.replace(type,''); type = this.standardiseINAXType(type); //gets Offset and Length from remaining Address part for Strings and Bools, parseInt's remaining address to Offset for other types if (type === 'S' || type === 'X'){ let offsetAndLength = this.getINAXOffsetAndLength(restAddress); offset = offsetAndLength[0]; length = offsetAndLength[1]; } else { offset = parseInt(restAddress); } return new PlcAbsStat(selector, type, offset, length, null, 0, mnemonic); } public static splitPlcPointerAddress(absAddress :string, mnemonic :string = 'DE') :PlcAbsStat{ let selector :string, selectorLength : number, offset :number, length :number, countOfData :number = null; absAddress = absAddress.substring(2); //removes P# at the beginning selector = this.getINAXSelector(absAddress, mnemonic); //removes the Selector part from the Address if (absAddress[0] == 'D') { selectorLength = absAddress.indexOf('.') + 3; } else { selectorLength = 1; } let address = absAddress.substring(selectorLength); if (address[0] === 'X'){ address = address.substring(1); } //removes X from pointer format address //separates Type and Offset(_Length) let type = this.extractINAXType(address); address = address.replace(type, '?'); //avoid errors from digits in type let dataCountString = address.substring(address.indexOf('?')+1); if (dataCountString.length > 0) { //If CountOfData !== '' countOfData = parseInt(dataCountString); } type = this.standardiseINAXType(type); let offsetAndLengthString = address.substring(0, address.indexOf('?')); //gets Offset and Length from remaining Address part for Strings and Bools, parseInt's remaining address to Offset for other types let offsetAndLength = this.getINAXOffsetAndLength(offsetAndLengthString); offset = offsetAndLength[0]; length = offsetAndLength[1]; //this.checkBitOffset(type, length); return new PlcAbsStat(selector, type, offset, length, countOfData, 1, mnemonic); } public static extractINAXType(address :string) :string{ //accepts address without selector let TYPES = [ 'X' , 'DATETIME' , 'TIMEBCD' , 'BOOLEAN' , 'BYTE' , 'BIT' , 'BOOL' , 'COUNT' , 'CHAR' , 'DWORD' , 'DATE' , 'DINT' , 'DT' , 'DI' , 'INT' , 'REAL' , 'S5TIME' , 'STRING' , 'TIMEOFDAY' , 'TIME' , 'TOD' , 'WORD' , 'CT' , 'DW' , 'C' , 'B' , 'T' , 'S' , 'I' , 'W' ]; for (let i = 0; i < TYPES.length; i++){ if (address.replace(TYPES[i], '').search(/[^\d\.]/g) === -1){ //If address without TYPES[i] only contains numbers and dots [\d\.] return TYPES[i]; } } throw Error('Invalid Type'); //if no Type was specified, boolean will be set in standardiseINAXType } public static checkBitOffset(type :string, length :number) :void{ switch(type){ default: } } public static getINAXSelector(absAddress :string, mnemonic :string = 'DE') :string{ let selector : string; if (mnemonic === 'DE'){ switch (absAddress[0]) { case "D": selector = absAddress.split('.')[0]; break; case "E": selector = "IB"; break; case "M": selector = "FB"; break; case "A": selector = "QB"; break; case "T": selector = "TB"; break; case "Z": selector = "CT"; break; default: throw Error('Invalid Selector'); } } else { switch (absAddress[0]) { case "D": selector = absAddress.split('.')[0]; break; case "I": selector = "IB"; break; case "M": selector = "FB"; break; case "Q": selector = "QB"; break; case "T": selector = "TB"; break; case "C": selector = "CT"; break; default: throw Error('Invalid Selector'); } } return selector; } /* public static getINAXAddress(absAddress :string) :string{ let offset = 0; if (absAddress[0] == 'D') { offset = absAddress.indexOf('.') + 3; } else { offset = 1; } let Address = absAddress.substring(offset); Address = Address.replace(/\./g,'_'); return Address; } */ public static getINAXOffsetAndLength(addressPart :string) :number[]{ //returns array with [ , ()] ( is only set for Strings or Bools respectively) let endOfOffset = addressPart.search(/\D/); let offset = parseInt(addressPart.substring(0, endOfOffset)); let length; length = parseInt(addressPart.substring(endOfOffset+1)); return [offset, length]; } public static standardiseINAXType(type :string) :string{ //returns the shortest name for a Type if (type === ''){ return 'X';} //if Type was omitted, assume boolean if (type.length < 3) { return type; } //if Type consists of two characters at most, it already is in the shortest form possible switch(type){ case 'BIT': case 'BOOL': return 'X'; case 'BYTE': return 'B'; case 'CHAR': return 'C'; case 'DATETIME': return 'DT'; case 'DINT': return 'DI'; case 'DWORD': return 'DW'; case 'INT': return 'I'; case 'REAL': return 'R'; case 'STRING': return 'S'; case 'TIME': return 'T'; case 'WORD': return 'W'; case 'COUNT': return 'CT'; default: return type; } } public getAddress() :string{ if(this.Format) { //pointer format address return this.Type + this.Offset + '_' + this.Length; } else { //normal address if (this.Type === 'S' || this.Type === 'X'){ return this.Type + this.Offset + '_' + this.Length; } else { return this.Type + this.Offset; } } } public getSelector() :string{ return this.Selector; } public getType() :string{ return this.Type; } public getOffset() :number{ return this.Offset; } public getLength() :number{ return this.Length; } public getCountOfData() :number{ return this.CountOfData; } public getMnemonic() :string{ return this.Mnemonic; } public toPLC(mnemonic :string = this.Mnemonic) :string{ if (this.Format === 1){ return 'P# ' + this.getPLCSelector(mnemonic) + ' ' + this.Offset + '.' + this.Length + ' ' + this.Type + ' ' + this.CountOfData; } else { let address = ""; if (this.Type === 'S' || this.Type === 'X'){ address = this.Type + this.Offset + '.' + this.Length; } else { address = this.Type + this.Offset; } return this.getPLCSelector(mnemonic) + address; } } public getPLCSelector(mnemonic :string = this.Mnemonic) :string{ //pass 'DE' to output German operand types const DE = (mnemonic === 'DE'); switch(this.Selector){ case "IB": return(DE ? "E" : "I"); case "QB": return(DE ? "A" : "Q"); case "FB": return "M"; case "TM": return "T"; case "CT": return(DE ? "Z" : "C"); default: //case "DBxxxx" return this.Selector+".DB"; } } }