nope/modules/generic-plc/helpers/gen-config-cli.ts

571 lines
17 KiB
TypeScript
Raw Normal View History

2020-08-30 07:45:44 +00:00
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2018-12-19 15:26:04
* @modify date 2020-03-12 21:40:52
* @desc [Tool to convert the the provided Inputs to a config file]
*/
import { existsSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
import { SPLITCHAR } from "../../../lib/helpers/objectMethods";
import { createCollectionObject } from "../../mod-Arango-Connector/src/creating";
/** Imports for File Handeling */
import { getAddress, getBitNumber, getType } from "../src/logic-functions";
import {
IPLCsConfig,
IPLCsModuleConfig,
validTypes
} from "../type/interfaces";
/**
* Function to Convert the Belegungs-Txt to a JSON Object
* @param _pathToFile The Path to the File.
*/
export function convertPortConfig(_pathToFile = "SPS-Belegung.txt") {
if (existsSync(join(process.cwd(), _pathToFile))) {
const data = readFileSync(join(process.cwd(), _pathToFile)).toString();
/** Split the Config based on '\n' */
const lines = data.split("\n");
const ports: {
inputs: {
[index: string]: {
start: string;
type: validTypes;
db: "input" | "output" | number;
};
};
outputs: {
[index: string]: {
start: string;
type: validTypes;
db: "input" | "output" | number;
};
};
} = {
inputs: {},
outputs: {}
};
/** Iterate over the Lines */
for (const line of lines) {
/** The Values are splitted by a tab-stop */
const values = line.split("\t");
/** Extract the Values, only if the Line is Valid */
if (values.length >= 4) {
/** Based on the Location extract the Name, Type and Address */
const name = values[0];
let db: "input" | "output" | number = 0;
if (values[1].replace(/[0-9]/g, "").toUpperCase() === "DB") {
db = parseInt(values[1].toUpperCase().split("DB")[1]);
} else {
db = values[3].toUpperCase().startsWith("%I") ? "input" : "output";
}
const type = getType(values[2].toUpperCase());
const addr = values[3].toUpperCase();
const isInput = addr.startsWith("%I");
let offset: string = "";
/** Test if the offset could be extracted */
if (/(\d+)\.(\d+)/.test(addr)) {
/** Extract the Offset using a Regular expression */
offset = (/(\d+)\.(\d+)/.exec(addr) as Array<any>)[0];
} else if (/(\d+)/.test(addr)) {
/** Extract the Offset using a Regular expression */
offset = (/(\d+)/.exec(addr) as Array<any>)[0] + ".0";
}
/** Check if all Data are */
if (name && type && addr && offset) {
/** Store the Value in the Config */
ports[isInput ? "inputs" : "outputs"][name] = {
start: offset,
type,
db
};
} else {
console.log("Can't read line: \"" + line + '"');
console.log(name, type, addr, offset);
}
}
}
return ports;
}
return null;
}
export const DEFAULT_PLC_MODULE_CONFIG_FILE =
"./config/plc-module-configuration.json";
export const DEFAULT_PORT_CONFIG_FILE =
2020-09-08 14:59:06 +00:00
"./modules/mod-Generic-PLC-Interface/helpers/SPS-Belegung.txt";
2020-08-30 07:45:44 +00:00
export const DEFAULT_PLC_CONFIG_FILE = "./config/plc-configuration.json";
export async function adaptConfig(
_moduleConfig = "",
_portConfig = "",
_plcConfig = ""
) {
process.stdout.write("\x1B[2J\x1B[0f");
// Import Inquirer and the Fuzzy-Path Module
const inquirer = require("inquirer");
inquirer.registerPrompt("path", require("inquirer-fuzzy-path"));
let result: any = {};
/** Create the File Name */
const _dirName = join(process.cwd(), "config");
if (!_moduleConfig) {
_moduleConfig = (
await inquirer.prompt([
{
type: "input",
name: "moduleConfig",
message: "Enter the Configuration File of the Utilized PLC-Modules",
default: DEFAULT_PLC_MODULE_CONFIG_FILE,
validate: function (value: string) {
if (value.endsWith(".json")) {
return true;
}
return "Please add the Enter a Valid Filename (including .json)";
}
}
])
).moduleConfig;
}
if (!_portConfig) {
_portConfig = (
await inquirer.prompt([
{
type: "input",
name: "portConfig",
message:
"Enter the Configuration File of the Ports, provided by a Siemens PLC",
default: DEFAULT_PORT_CONFIG_FILE,
validate: function (value: string) {
if (value.endsWith(".txt")) {
return true;
}
return "Please add the Enter a Valid Filename (including .txt)";
}
}
])
).portConfig;
}
if (!_plcConfig) {
_plcConfig = (
await inquirer.prompt([
{
type: "input",
name: "plcConfig",
message:
"Enter the Configuration File, in whicht the configuration should be stored",
default: DEFAULT_PLC_CONFIG_FILE,
validate: function (value: string) {
if (value.endsWith(".json")) {
return true;
}
return "Please add the Enter a Valid Filename (including .json)";
}
}
])
).plcConfig;
}
let _config: IPLCsModuleConfig = {
plcs: {}
};
let _plcConfigObject: { plcs: IPLCsConfig } = {
plcs: {}
};
const _ports = convertPortConfig(_portConfig);
console.log(_portConfig);
if (_ports) {
/** Check if a Config exists => If So Extend the given config */
while (!existsSync(_moduleConfig)) {
_config = {
plcs: {
S7MRK: {
name: "S7MRK",
options: {
connection: {
mqtt: {
hostname: "127.0.0.1",
port: 1883
},
plc: {
ip: "192.168.2.2",
rack: 0,
slot: 0
}
}
},
type: "raw",
modules: {
inputs: {
"Digital-Term-01": {
start: 0,
len: 4,
pathToData:
"S7MRK.modules.Digital-Term-01.rawData.input.value",
db: "input"
},
"Analog-Term-01": {
start: 4,
len: 16,
pathToData:
"S7MRK.modules.Analog-Term-01.rawData.input.value",
db: "input"
},
"Nachladestation.CPX": {
start: 64,
len: 2,
pathToData: "S7MRK.Nachladestation.CPX.rawData.input.value",
db: "input"
},
"Nachladestation.Motor": {
start: 32,
len: 30,
pathToData: "S7MRK.Nachladestation.Motor.rawData.input.value",
db: 19
},
"Schrauber.Motor": {
start: 32,
len: 30,
pathToData: "S7MRK.Schrauber.Motor.rawData.input.value",
db: 20
},
"ET200.Digital": {
start: 20,
len: 4,
pathToData: "S7MRK.modules.ET200.Digital.rawData.input.value",
db: "input"
},
"ET200.Analog": {
start: 24,
len: 8,
pathToData: "S7MRK.modules.ET200.Analog.rawData.input.value",
db: "input"
},
C30SXMv3: {
start: 256,
len: 29,
pathToData: "S7MRK.modules.C30SXMv3.rawData.input.value",
db: "input"
},
Kamera: {
start: 32,
len: 30,
pathToData: "S7MRK.modules.Kamera.rawData.input.value",
db: 100
}
},
outputs: {
"Digital-Term-01": {
start: 0,
len: 4,
pathToData:
"S7MRK.modules.Digital-Term-01.rawData.output.value",
db: "output"
},
"Analog-Term-01": {
start: 4,
len: 8,
pathToData:
"S7MRK.modules.Analog-Term-01.rawData.output.value",
db: "output"
},
"Nachladestation.CPX": {
start: 64,
len: 4,
pathToData: "S7MRK.Nachladestation.CPX.rawData.output.value",
db: "output"
},
"Nachladestation.Motor": {
start: 0,
len: 32,
pathToData:
"S7MRK.Nachladestation.Motor.rawData.output.value",
db: 19
},
"Schrauber.Motor": {
start: 0,
len: 32,
pathToData: "S7MRK.Schrauber.Motor.rawData.output.value",
db: 20
},
"ET200.Digital": {
start: 12,
len: 4,
pathToData:
"S7MRK.modules.ET200.Digital.rawData.output.value",
db: "output"
},
C30SXMv3: {
start: 256,
len: 2,
pathToData: "S7MRK.modules.C30SXMv3.rawData.output.value",
db: "output"
},
Kamera: {
start: 0,
len: 32,
pathToData: "S7MRK.modules.Kamera.rawData.output.value",
db: 100
}
},
pathToSubscribe: "S7MRK.modules.rawData.output.value",
pathToPublish: "S7MRK.modules.rawData.input.value"
}
}
},
// load: "S7MRK"
};
/** Write the new Configuration */
writeFileSync(_moduleConfig, JSON.stringify(_config, undefined, 4));
console.log(
new Date().toISOString(),
"Generated sample Config File. Will use this one."
);
}
_config = JSON.parse(readFileSync(_moduleConfig, { encoding: "utf-8" }));
result = await inquirer.prompt([
{
type: "list",
name: "selectedPLC",
message: "What size do you need?",
choices: Object.getOwnPropertyNames(_config.plcs)
},
{
type: "checkbox",
name: "inputs",
message: "Select/Deselect the available Inputs",
choices: Object.getOwnPropertyNames(_ports.inputs).map((name) => {
return {
name,
checked: true
};
})
},
{
type: "checkbox",
name: "outputs",
message: "Select/Deselect the available Outputs",
choices: Object.getOwnPropertyNames(_ports.outputs).map((name) => {
return {
name,
checked: true
};
})
}
]);
const unfound = [];
const plcConfig = _config.plcs[result.selectedPLC];
// Update the Configuration Object.
_plcConfigObject.plcs[result.selectedPLC] = {
modules: {
inputs: {},
outputs: {},
pathToPublish: plcConfig.modules.pathToPublish,
pathToSubscribe: plcConfig.modules.pathToSubscribe
},
name: result.selectedPLC,
options: plcConfig.options,
type: plcConfig.type
};
for (const _type of ["inputs", "outputs"]) {
const type: "inputs" | "outputs" = _type as "inputs" | "outputs";
/** Overwrite old Config */
for (const name in _config.plcs[result.selectedPLC].modules[type]) {
// Define initial Config Entry.
_plcConfigObject.plcs[result.selectedPLC].modules[type][name] = {
// In the Beginning No Element is Provided
elements: {},
// Use the Length of the Module
len: _config.plcs[result.selectedPLC].modules[type][name].len,
// Module Name
name,
// Path for the Buffer
pathToPublishBufferContent:
_config.plcs[result.selectedPLC].modules[type][name].pathToData,
// Path for the Buffer
pathToSubscribeBufferContent:
_config.plcs[result.selectedPLC].modules[type][name].pathToData,
// PLC Settings of the Module
plc: {
// PLC Name
name: result.selectedPLC,
// Path for Publishing updated Buffers
pathToPublishModules:
_config.plcs[result.selectedPLC].modules.pathToPublish,
// Path for Subscribing updated Buffers
pathToSubscribeModules:
_config.plcs[result.selectedPLC].modules.pathToSubscribe
},
// Start Address.
start: _config.plcs[result.selectedPLC].modules[type][name].start,
// Type of the Module (Input / Output)
type
};
}
for (const portName of result[type]) {
/** Get the Object */
const port = _ports[type as "inputs" | "outputs"][portName];
let found = false;
if (typeof port.db === "string") {
/** Iterate over the Modules to finde the corresponding Definition. */
for (const modName in _config.plcs[result.selectedPLC].modules[
type
]) {
const mod = _config.plcs[result.selectedPLC].modules[type][modName];
const min = mod.start * 8;
const max = (mod.start + mod.len) * 8;
if (
typeof mod.db === "string" &&
getBitNumber(port.start) >= min &&
getBitNumber(port.start) < max
) {
console.log("[x]", portName, "->", modName);
/** Adapt the Bit-Address to Match the Modules Buffer */
port.start = getAddress(getBitNumber(port.start) - min);
/** Extract the Module Name */
_plcConfigObject.plcs[result.selectedPLC].modules[type][
modName
].elements[portName] = Object.assign(
{
pathToData:
result.selectedPLC +
SPLITCHAR +
modName +
SPLITCHAR +
type +
SPLITCHAR +
portName +
SPLITCHAR +
"value"
},
port
);
found = true;
break;
}
}
} else {
/** Element is mapped to a DB */
for (const modName in _config.plcs[result.selectedPLC].modules[
type
]) {
const mod = _config.plcs[result.selectedPLC].modules[type][modName];
const min = mod.start * 8;
const max = (mod.start + mod.len) * 8;
if (
typeof mod.db === "number" &&
mod.db == port.db &&
getBitNumber(port.start) >= min &&
getBitNumber(port.start) < max
) {
console.log("[x]", portName, "->", modName);
/** Adapt the Bit-Address to Match the Modules Buffer */
port.start = getAddress(getBitNumber(port.start) - min);
/** Extract the Module Name */
_plcConfigObject.plcs[result.selectedPLC].modules[type][
modName
].elements[portName] = Object.assign(
{
pathToData:
result.selectedPLC +
SPLITCHAR +
modName +
SPLITCHAR +
type +
SPLITCHAR +
portName +
SPLITCHAR +
"value"
},
port
);
found = true;
break;
}
}
}
if (!found) {
console.log("[ ]", portName);
unfound.push(portName);
}
}
}
console.log("\n\n\n");
process.stdout.write("\x1B[2J\x1B[0f");
writeFileSync(_plcConfig, JSON.stringify(_plcConfigObject, undefined, 4));
console.log("\n\n\n\n" + new Date().toISOString(), "Generated Config File");
console.log(
"\n\nUnfound Elements:\n",
JSON.stringify(unfound, undefined, 4)
);
await createCollectionObject("config", "plc", _plcConfigObject);
}
}
if (require.main == module) {
let moduleConfig = "";
let portConfig = "";
let plcConfig = "";
/** Extract the Process pathes. */
process.argv.forEach((value, idx) => {
switch (idx) {
case 2:
moduleConfig = value;
break;
case 3:
portConfig = value;
break;
case 4:
plcConfig = value;
break;
}
});
adaptConfig(moduleConfig, portConfig, plcConfig);
}