s7-server
Version:
TypeScript S7 PLC Server Simulator
285 lines • 10.6 kB
JavaScript
"use strict";
/**
* S7 Address Parser Utility
* Handles parsing of S7 PLC address strings into structured format
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.S7AddressParser = void 0;
// Internal modules
const s7_types_1 = require("../types/s7-types");
/**
* S7 Address Parser Class
* Provides comprehensive parsing of S7 address strings
*/
class S7AddressParser {
/**
* Parse S7 address string into structured format
* Supports full S7 addressing formats including data types
*/
static parseAddress(address) {
if (!address || typeof address !== 'string') {
return { success: false, error: 'Invalid address format' };
}
const trimmed = address.trim().toUpperCase();
// Parse DB addresses with data types (e.g., DB5,X0.1, DB23,B1, DB42,I3, etc.)
const dbWithTypeMatch = trimmed.match(/^DB(\d+),([XIBWDR]|DI|DW)(\d+)(?:\.(\d+))?$/);
if (dbWithTypeMatch) {
const [, dbNumber, dataType, offset, bitOffset] = dbWithTypeMatch;
const db = parseInt(dbNumber, 10);
const byteOffset = parseInt(offset, 10);
if (db < 0 || db > 65535) {
return { success: false, error: 'DB number out of range' };
}
if (byteOffset < 0 || byteOffset > 65535) {
return { success: false, error: 'Byte offset out of range' };
}
// Handle bit addressing for X type
if (dataType === 'X' && bitOffset !== undefined) {
const bit = parseInt(bitOffset, 10);
if (bit < 0 || bit > 7) {
return { success: false, error: 'Bit offset must be 0-7' };
}
return {
success: true,
address: {
area: s7_types_1.S7Area.DB,
index: db,
start: byteOffset,
wordLen: s7_types_1.S7WordLength.Bit,
size: 1
}
};
}
// Handle other data types
let wordLen;
let size;
switch (dataType) {
case 'B': // Byte
wordLen = s7_types_1.S7WordLength.Byte;
size = 1;
break;
case 'I': // Signed 16-bit
wordLen = s7_types_1.S7WordLength.Word;
size = 1;
break;
case 'W': // Unsigned 16-bit
wordLen = s7_types_1.S7WordLength.Word;
size = 1;
break;
case 'DI': // Signed 32-bit
wordLen = s7_types_1.S7WordLength.DWord;
size = 1;
break;
case 'DW': // Unsigned 32-bit
wordLen = s7_types_1.S7WordLength.DWord;
size = 1;
break;
case 'R': // Real (32-bit float)
wordLen = s7_types_1.S7WordLength.Real;
size = 1;
break;
default:
return { success: false, error: 'Invalid data type for DB address' };
}
return {
success: true,
address: {
area: s7_types_1.S7Area.DB,
index: db,
start: byteOffset,
wordLen,
size
}
};
}
// Parse simple DB addresses (e.g., DB1, DB2, etc.)
const dbMatch = trimmed.match(/^DB(\d+)$/);
if (dbMatch) {
const dbNumber = parseInt(dbMatch[1], 10);
if (dbNumber < 0 || dbNumber > 65535) {
return { success: false, error: 'DB number out of range' };
}
return {
success: true,
address: {
area: s7_types_1.S7Area.DB,
index: dbNumber,
start: 0,
wordLen: s7_types_1.S7WordLength.Byte,
size: 1
}
};
}
// Parse bit addresses (e.g., I1.0, Q2.1, M3.2, X0.0, etc.)
const bitMatch = trimmed.match(/^([IMQX])(\d+)\.(\d+)$/);
if (bitMatch) {
const [, areaType, byteOffset, bitOffset] = bitMatch;
const byte = parseInt(byteOffset, 10);
const bit = parseInt(bitOffset, 10);
if (byte < 0 || byte > 65535) {
return { success: false, error: 'Byte offset out of range' };
}
if (bit < 0 || bit > 7) {
return { success: false, error: 'Bit offset must be 0-7' };
}
let area;
switch (areaType) {
case 'I':
area = s7_types_1.S7Area.PE; // Process inputs
break;
case 'Q':
area = s7_types_1.S7Area.PA; // Process outputs
break;
case 'M':
case 'X':
area = s7_types_1.S7Area.MK; // Merkers (memory)
break;
default:
return { success: false, error: 'Invalid area type' };
}
return {
success: true,
address: {
area,
index: 0,
start: byte,
wordLen: s7_types_1.S7WordLength.Bit,
size: 1
}
};
}
// Parse byte addresses (e.g., IB0, QB1, MB2, XB2, etc.)
const byteMatch = trimmed.match(/^([IMQX])B(\d+)$/);
if (byteMatch) {
const [, areaType, byteOffset] = byteMatch;
const byte = parseInt(byteOffset, 10);
if (byte < 0 || byte > 65535) {
return { success: false, error: 'Byte offset out of range' };
}
let area;
switch (areaType) {
case 'I':
area = s7_types_1.S7Area.PE; // Process inputs
break;
case 'Q':
area = s7_types_1.S7Area.PA; // Process outputs
break;
case 'M':
case 'X':
area = s7_types_1.S7Area.MK; // Merkers (memory)
break;
default:
return { success: false, error: 'Invalid area type' };
}
return {
success: true,
address: {
area,
index: 0,
start: byte,
wordLen: s7_types_1.S7WordLength.Byte,
size: 1
}
};
}
// Parse word addresses with data types (e.g., II10, QI12, MI14, IW16, QW18, MW20, MR38, etc.)
// Note: B is excluded from this pattern since it's handled by byte addresses above
const wordMatch = trimmed.match(/^([IMQX])([IWDR])(\d+)$/);
if (wordMatch) {
const [, areaType, dataType, offset] = wordMatch;
const byteOffset = parseInt(offset, 10);
if (byteOffset < 0 || byteOffset > 65535) {
return { success: false, error: 'Byte offset out of range' };
}
let area;
switch (areaType) {
case 'I':
area = s7_types_1.S7Area.PE; // Process inputs
break;
case 'Q':
area = s7_types_1.S7Area.PA; // Process outputs
break;
case 'M':
case 'X':
area = s7_types_1.S7Area.MK; // Merkers (memory)
break;
default:
return { success: false, error: 'Invalid area type' };
}
let wordLen;
let size;
switch (dataType) {
case 'I': // Signed 16-bit
wordLen = s7_types_1.S7WordLength.Word;
size = 1;
break;
case 'W': // Unsigned 16-bit
wordLen = s7_types_1.S7WordLength.Word;
size = 1;
break;
case 'R': // Real (32-bit float)
wordLen = s7_types_1.S7WordLength.Real;
size = 1;
break;
default:
return { success: false, error: `Invalid data type for word address: ${address}` };
}
return {
success: true,
address: {
area,
index: 0,
start: byteOffset,
wordLen,
size
}
};
}
return { success: false, error: `Unsupported address format: ${address}` };
}
/**
* Validate if an address string is a valid S7 address
*/
static isValidAddress(address) {
const result = this.parseAddress(address);
return result.success;
}
/**
* Get the byte count required for a word length
*/
static getByteCountFromWordLen(wordLen) {
switch (wordLen) {
case s7_types_1.S7WordLength.Bit:
case s7_types_1.S7WordLength.Byte:
return 1;
case s7_types_1.S7WordLength.Word:
case s7_types_1.S7WordLength.Counter:
case s7_types_1.S7WordLength.Timer:
return 2;
case s7_types_1.S7WordLength.Real:
case s7_types_1.S7WordLength.DWord:
return 4;
default:
return 0;
}
}
/**
* Extract bit offset from address string for bit addressing
*/
static extractBitOffset(address) {
const parts = address.split('.');
return parts.length > 1 ? parseInt(parts[1], 10) : 0;
}
/**
* Check if an address represents a signed data type
*/
static isSignedDataType(address) {
const trimmed = address.trim().toUpperCase();
return (trimmed.includes(',I') ||
trimmed.includes(',DI') ||
/^[IMQX]I\d+$/.test(trimmed) ||
/^[IMQX]DI\d+$/.test(trimmed));
}
}
exports.S7AddressParser = S7AddressParser;
//# sourceMappingURL=address-parser.js.map