homebridge-logo-platform
Version:
This is a Siemens LOGO! Platform Plugin.
527 lines (460 loc) • 17.6 kB
text/typescript
let snap7 = require('napi-snap7');
import { ErrorNumber } from "./error";
export enum Area {
S7AreaPE = 0x81,
S7AreaPA = 0x82,
S7AreaMK = 0x83,
S7AreaDB = 0x84,
S7AreaCT = 0x1C,
S7AreaTM = 0x1D
}
export enum WordLen {
S7WLBit = 0x01,
S7WLByte = 0x02,
S7WLChar = 0x03,
S7WLWord = 0x04,
S7WLInt = 0x05,
S7WLDWord = 0x06,
S7WLDInt = 0x07,
S7WLReal = 0x08,
S7WLCounter = 0x1C,
S7WLTimer = 0x1D
}
export class LogoAddress {
constructor(
public addr: number,
public bit: number,
public wLen: WordLen
) {}
}
export class Snap7Logo {
public type: string = "0BA7";
public ipAddr: string = "";
public local_TSAP: number = 0x2000;
public remote_TSAP: number = 0x2000;
public db: number = 1;
public debugMsgLog: number = 0;
public log: Function;
public retryCnt: number;
public s7client: any;
constructor(
type: string,
ipAddr: string,
local_TSAP: number,
remote_TSAP: number,
debug: number,
logFunction: any,
retrys: number
) {
this.type = type;
this.ipAddr = ipAddr;
this.local_TSAP = local_TSAP;
this.remote_TSAP = remote_TSAP;
this.debugMsgLog = debug;
this.log = logFunction;
this.retryCnt = retrys;
this.s7client = new snap7.S7Client();
this.s7client.SetConnectionParams(this.ipAddr, this.local_TSAP, this.remote_TSAP);
}
ReadLogo(item: string, callBack: (value: number) => any) {
this.ConnectS7(this.s7client, this.debugMsgLog, this.retryCnt, (success: Boolean) => {
if(success == false) {
if (this.debugMsgLog == 1) {
this.log('ReadLogo() - Connection failed.');
}
callBack(ErrorNumber.noData);
return ErrorNumber.noData;
}
var target = this.getAddressAndBit(item, this.type);
this.DBReadS7(this.s7client, this.db, target, this.debugMsgLog, this.retryCnt, (success: number) => {
if (success == ErrorNumber.noData) {
callBack(ErrorNumber.noData);
} else {
if (success > ErrorNumber.maxPositivNumber) {
success = success - ErrorNumber.max16BitNumber;
}
callBack(success);
}
});
});
}
WriteLogo(item: string, value: number) {
this.ConnectS7(this.s7client, this.debugMsgLog, this.retryCnt, (success: Boolean) => {
if(success == false) {
if (this.debugMsgLog == 1) {
this.log('WriteLogo() - Connection failed.');
}
return ErrorNumber.noData;
}
var target = this.getAddressAndBit(item, this.type);
var buffer_on;
if (target.wLen == WordLen.S7WLBit) {
buffer_on = Buffer.from([value << target.bit]);
}
if (target.wLen == WordLen.S7WLByte) {
buffer_on = Buffer.from([value]);
}
if (target.wLen == WordLen.S7WLWord) {
buffer_on = Buffer.from([((value & 0b1111111100000000) >> 8), (value & 0b0000000011111111)]);
}
if (target.wLen == WordLen.S7WLDWord) {
buffer_on = Buffer.from([((value & 0b11111111000000000000000000000000) >> 24), ((value & 0b00000000111111110000000000000000) >> 16), ((value & 0b00000000000000001111111100000000) >> 8), (value & 0b00000000000000000000000011111111)]);
}
this.DBWriteS7(this.s7client, this.db, target, this.debugMsgLog, this.retryCnt, buffer_on, (success: Boolean) => {
if(!success) {
return ErrorNumber.noData;
}
});
});
}
ConnectS7(s7client: any, debugLog: number, retryCount: number, callBack?: (success: Boolean) => any) {
if (retryCount == 0) {
if (debugLog == 1) {
this.log('ConnectS7() - Retry counter reached max value');
}
if (callBack) {
callBack(false);
}
return false;
}
if (s7client.GetConnected() != true) {
s7client.Disconnect();
retryCount = retryCount - 1;
let err = s7client.Connect();
if(err == ErrorNumber.noConnection) {
if ((debugLog == 1) && (retryCount == 1)) {
this.log('ConnectS7() - Connection failed. Retrying. Code #' + err + ' - ' + s7client.ErrorText(err));
}
if ((debugLog == 1) && (retryCount > 1)) {
this.log('ConnectS7() - Connection failed. Retrying. (%i)', retryCount);
}
s7client.Disconnect();
sleep(100).then(() => {
this.ConnectS7(s7client, debugLog, retryCount, callBack);
});
} else {
if (callBack) {
callBack(true);
}
}
} else {
if (callBack) {
callBack(true);
}
}
return true;
}
DisconnectS7() {
if (this.s7client.GetConnected() == true) {
this.s7client.Disconnect();
if (this.debugMsgLog == 1) {
this.log('DisconnectS7() - Disconnect LOGO!');
}
}
}
ReadAreaS7(s7client: any, db: number, target: LogoAddress, debugLog: number, retryCount: number, callBack: (success: number) => any) {
if (retryCount == 0) {
if (debugLog == 1) {
this.log('ReadS7() - Retry counter reached max value');
}
callBack(ErrorNumber.noData);
return ErrorNumber.noData;
}
retryCount = retryCount - 1;
// Area, DBNumber, Start, Amount, WordLen
s7client.ReadArea(Area.S7AreaDB, db, target.addr, 1, 0x02, (err, data) => {
if(err) {
if ((debugLog == 1) && (retryCount == 1)) {
this.log('ReadS7() - ReadArea failed. Code #' + err + ' - ' + this.s7client.ErrorText(err));
}
if ((debugLog == 1) && (retryCount > 1)) {
this.log('ReadS7() - ReadArea failed. Retrying. (%i)', retryCount);
}
sleep(100).then(() => {
this.ReadAreaS7(s7client, db, target, debugLog, retryCount, callBack);
});
} else {
var buffer = Buffer.from(data);
if (target.wLen == WordLen.S7WLBit) {
callBack((buffer[0] >> target.bit) & 1);
}
if (target.wLen == WordLen.S7WLByte) {
callBack(buffer[0]);
}
if (target.wLen == WordLen.S7WLWord) {
callBack( (buffer[0] << 8) | buffer[1] );
}
if (target.wLen == WordLen.S7WLDWord) {
callBack( (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3] );
}
callBack(ErrorNumber.noData);
}
});
}
DBReadS7(s7client: any, db: number, target: LogoAddress, debugLog: number, retryCount: number, callBack: (success: number) => any) {
if (retryCount == 0) {
if (debugLog == 1) {
this.log('ReadS7() - Retry counter reached max value');
}
callBack(ErrorNumber.noData);
return ErrorNumber.noData;
}
retryCount = retryCount - 1;
let leng = this.getWordSize(target.wLen);
s7client.DBRead(db, target.addr, leng, (err, data) => {
if(err) {
if ((debugLog == 1) && (retryCount == 1)) {
this.log('ReadS7() - DBRead failed. Code #' + err + ' - ' + this.s7client.ErrorText(err));
}
if ((debugLog == 1) && (retryCount > 1)) {
this.log('ReadS7() - DBRead failed. Retrying. (%i)', retryCount);
}
sleep(100).then(() => {
this.DBReadS7(s7client, db, target, debugLog, retryCount, callBack);
});
} else {
var buffer = Buffer.from(data);
if (target.wLen == WordLen.S7WLBit) {
// this.log('Bit: ', (buffer[0] >> target.bit) & 1);
callBack((buffer[0] >> target.bit) & 1);
}
if (target.wLen == WordLen.S7WLByte) {
// this.log('Byte: ', buffer[0]);
callBack(buffer[0]);
}
if (target.wLen == WordLen.S7WLWord) {
// this.log('Word: ', (buffer[0] << 8) | buffer[1]);
callBack( (buffer[0] << 8) | buffer[1] );
}
if (target.wLen == WordLen.S7WLDWord) {
// this.log('DWord: ', (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]);
callBack( (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3] );
}
// callBack(ErrorNumber.noData);
}
});
}
WriteAreaS7(s7client: any, db: number, target: LogoAddress, debugLog: number, retryCount: number, buffer: Buffer, callBack: (success: Boolean) => any) {
if (retryCount == 0) {
if (debugLog == 1) {
this.log('WriteS7() - Retry counter reached max value');
}
if (callBack) {
callBack(false);
}
return ErrorNumber.noData;
}
retryCount = retryCount - 1;
// Area, DBNumber, Start, Amount, WordLen, Buffer
s7client.WriteArea(Area.S7AreaDB, db, target.addr, 1, 0x02, buffer, (err) => {
if(err) {
if ((debugLog == 1) && (retryCount == 1)) {
this.log('WriteS7() - DBWrite failed. Code #' + err + ' - ' + s7client.ErrorText(err));
}
if ((debugLog == 1) && (retryCount > 1)) {
this.log('WriteS7() - DBWrite failed. Retrying. (%i)', retryCount);
}
sleep(100).then(() => {
this.WriteAreaS7(s7client, db, target, debugLog, retryCount, buffer, callBack);
});
}
if (callBack) {
callBack(true);
}
});
}
DBWriteS7(s7client: any, db: number, target: LogoAddress, debugLog: number, retryCount: number, buffer: Buffer, callBack: (success: Boolean) => any) {
if (retryCount == 0) {
if (debugLog == 1) {
this.log('WriteS7() - Retry counter reached max value');
}
if (callBack) {
callBack(false);
}
return ErrorNumber.noData;
}
retryCount = retryCount - 1;
let leng = this.getWordSize(target.wLen);
s7client.DBWrite(db, target.addr, leng, buffer, (err) => {
if(err) {
if ((debugLog == 1) && (retryCount == 1)) {
this.log('WriteS7() - DBWrite failed. Code #' + err + ' - ' + s7client.ErrorText(err));
}
if ((debugLog == 1) && (retryCount > 1)) {
this.log('WriteS7() - DBWrite failed. Retrying. (%i)', retryCount);
}
sleep(100).then(() => {
this.DBWriteS7(s7client, db, target, debugLog, retryCount, buffer, callBack);
});
}
if (callBack) {
callBack(true);
}
});
}
getAddressAndBit(name: string, target_type: string): LogoAddress {
if (name.match("AI[0-9]{1,2}")) {
var num = parseInt(name.replace("AI", ""), 10)
if (target_type == "0BA7") {
return Snap7Logo.calculateWord(926, num);
} else {
return Snap7Logo.calculateWord(1032, num);
}
}
if (name.match("AQ[0-9]{1,2}")) {
var num = parseInt(name.replace("AQ", ""), 10)
if (target_type == "0BA7") {
return Snap7Logo.calculateWord(944, num);
} else {
return Snap7Logo.calculateWord(1072, num);
}
}
if (name.match("AM[0-9]{1,2}")) {
var num = parseInt(name.replace("AM", ""), 10)
if (target_type == "0BA7") {
return Snap7Logo.calculateWord(952, num);
} else {
return Snap7Logo.calculateWord(1118, num);
}
}
if (name.match("I[0-9]{1,2}")) {
var num = parseInt(name.replace("I", ""), 10)
if (target_type == "0BA7") {
return Snap7Logo.calculateBit(923, num);
} else {
return Snap7Logo.calculateBit(1024, num);
}
}
if (name.match("Q[0-9]{1,2}")) {
var num = parseInt(name.replace("Q", ""), 10)
if (target_type == "0BA7") {
return Snap7Logo.calculateBit(942, num);
} else {
return Snap7Logo.calculateBit(1064, num);
}
}
if (name.match("M[0-9]{1,2}")) {
var num = parseInt(name.replace("M", ""), 10)
if (target_type == "0BA7") {
return Snap7Logo.calculateBit(948, num);
} else {
return Snap7Logo.calculateBit(1104, num);
}
}
if (name.match("V[0-9]{1,4}\.[0-7]{1}")) {
var str = name.replace("V", "");
var a = parseInt(str.split(".", 2)[0], 10);
var b = parseInt(str.split(".", 2)[1], 10);
return new LogoAddress(a, b, WordLen.S7WLBit);
}
if (name.match("VB[0-9]{1,4}")) {
var num = parseInt(name.replace("VB", ""), 10)
return new LogoAddress(num, 0, WordLen.S7WLByte);
}
if (name.match("VW[0-9]{1,4}")) {
var num = parseInt(name.replace("VW", ""), 10)
return new LogoAddress(num, 0, WordLen.S7WLWord);
}
if (name.match("VD[0-9]{1,4}")) {
var num = parseInt(name.replace("VD", ""), 10)
return new LogoAddress(num, 0, WordLen.S7WLDWord);
}
return new LogoAddress(0, 0, WordLen.S7WLBit);
}
isValidLogoAddress(name: string): boolean {
if (name.match("AI[0-9]{1,2}")) {
return true;
}
if (name.match("AQ[0-9]{1,2}")) {
return true;
}
if (name.match("AM[0-9]{1,2}")) {
return true;
}
if (name.match("I[0-9]{1,2}")) {
return true;
}
if (name.match("Q[0-9]{1,2}")) {
return true;
}
if (name.match("M[0-9]{1,2}")) {
return true;
}
if (name.match("V[0-9]{1,4}\.[0-7]{1}")) {
return true;
}
if (name.match("VB[0-9]{1,4}")) {
return true;
}
if (name.match("VW[0-9]{1,4}")) {
return true;
}
if (name.match("VD[0-9]{1,4}")) {
return true;
}
return false;
}
isAnalogLogoAddress(name: string): boolean {
if (name.match("AI[0-9]{1,2}")) {
return true;
}
if (name.match("AQ[0-9]{1,2}")) {
return true;
}
if (name.match("AM[0-9]{1,2}")) {
return true;
}
if (name.match("I[0-9]{1,2}")) {
return false;
}
if (name.match("Q[0-9]{1,2}")) {
return false;
}
if (name.match("M[0-9]{1,2}")) {
return false;
}
if (name.match("V[0-9]{1,4}\.[0-7]{1}")) {
return false;
}
if (name.match("VB[0-9]{1,4}")) {
return true;
}
if (name.match("VW[0-9]{1,4}")) {
return true;
}
if (name.match("VD[0-9]{1,4}")) {
return true;
}
return false;
}
getWordSize(wordLen: Number) {
switch (wordLen) {
case WordLen.S7WLBit:
case WordLen.S7WLByte:
return 1;
case WordLen.S7WLWord:
case WordLen.S7WLCounter:
case WordLen.S7WLTimer:
return 2;
case WordLen.S7WLDWord:
case WordLen.S7WLReal:
return 4;
default:
return 0;
}
}
static calculateBit(base: number, num: number) {
var x = Math.floor((num - 1) / 8);
var y = 8 * (x + 1);
var addr = base + x;
var bit = 7 - (y - num);
return new LogoAddress(addr, bit, WordLen.S7WLBit);
}
static calculateWord(base: number, num: number) {
var addr = base + ((num - 1) * 2);
return new LogoAddress(addr, 0, WordLen.S7WLWord);
}
}
const sleep = (milliseconds: number) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}