nope/modules/generic-plc/helpers/gen-config-from-excel.ts
Martin Karkowski 838d910c2f adding gojs
2020-12-01 13:05:35 +01:00

345 lines
8.7 KiB
TypeScript

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2018-12-19 15:26:04
* @modify date 2018-12-19 15:26:04
* @desc [Tool to convert the the provided Inputs to a config file]
*/
import { writeFileSync } from "fs";
import { capitalize } from "lodash";
const readXlsxFile = require("read-excel-file/node");
const xl = require("excel4node");
export const DEFAULT_PLC_MODULE_CONFIG_FILE =
"./config/plc-module-configuration.json";
export const DEFAULT_PORT_CONFIG_FILE =
"./modules/mod-Generic-PLC-Interface/helpers/SPS-Belegung.txt";
export const DEFAULT_PLC_CONFIG_FILE = "./config/plc-configuration.json";
export async function loadExcelFile(
_pathToExcelFile = "",
_configFile = "",
_pathToVarFile = ""
) {
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"));
if (!_pathToExcelFile) {
_pathToExcelFile = (
await inquirer.prompt([
{
type: "path",
name: "excelSheet",
excludePath: (nodePath) =>
nodePath.startsWith(".git") ||
nodePath.startsWith("node_modules") ||
nodePath.startsWith("dist"),
// excludePath :: (String) -> Bool
// excludePath to exclude some paths from the file-system scan
itemType: "file",
// itemType :: 'any' | 'directory' | 'file'
// specify the type of nodes to display
// default value: 'any'
// example: itemType: 'file' - hides directories from the item list
rootPath: "./",
// rootPath :: String
// Root search directory
message: "Select an excel sheet which shoud be used:",
default:
"modules\\ZISS-Generic-PLC-Interface\\helpers\\sim-file.xlsx",
suggestOnly: true,
// suggestOnly :: Bool
// Restrict prompt answer to available choices or use them as suggestions
depthLimit: 0
// depthLimit :: integer >= 0
// Limit the depth of sub-folders to scan
// Defaults to infinite depth if undefined
}
])
).excelSheet;
}
if (!_configFile) {
_configFile = (
await inquirer.prompt([
{
type: "input",
name: "configFile",
message: "Enter the Filename, where the File 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)";
}
}
])
).configFile;
}
if (!_pathToVarFile) {
_pathToVarFile = (
await inquirer.prompt([
{
type: "input",
name: "output",
message: "Enter Variable Table",
default: "./config/vars.xlsx",
validate: function (value: string) {
if (value.endsWith(".xlsx")) {
return true;
}
return "Please add the Enter a Valid Filename (including .xlsx)";
}
}
])
).output;
}
const schema = {
"Signal Name": {
prop: "name",
type: String,
required: true
},
Memory: {
prop: "memory",
type: String
},
Type: {
prop: "type",
type: String
},
"Robot Signal Name": {
prop: "robotSignal",
type: String
},
Address: {
prop: "byteAddress",
type: String
},
"IEC Format": {
prop: "address",
type: String
}
};
const dataSheet = await readXlsxFile(_pathToExcelFile, { schema });
const inputs = {};
const outputs = {};
let maxByteNumber = -Infinity;
// https://www.spshaus.ch/files/inc/Downloads/Lernumgebung/Downloads/Allgemein/TIA_Portal_Uebersicht_Datentypen.pdf
const originalDataTypes = [
{ prefix: "", validTypes: ["BOOL"], size: 1 },
{
prefix: "B",
validTypes: ["BYTE", "CHAR", "SINT", "USINT", "WCHAR"],
size: 1
},
{ prefix: "W", validTypes: ["INT", "UINT", "DATE"], size: 2 },
{
prefix: "D",
validTypes: ["REAL", "UINT", , "UDINT", "TIME", "TOD"],
size: 4
}
];
const getPrefix = (type: string) => {
let ret = "";
/** Iterate over the Valid Types */
for (const element of originalDataTypes) {
if (element.validTypes.includes(type.toUpperCase())) {
return element.prefix;
}
}
return ret;
};
const getLastAddress = (type: string, address: string) => {
let high = parseFloat(address.split(".")[0]);
/** Iterate over the Valid Types */
for (const element of originalDataTypes) {
if (element.validTypes.includes(type.toUpperCase())) {
return element.size + high;
}
}
return high + 1;
};
dataSheet.rows.map((element) => {
// A Row is defined like
// {
// name: 'Tragwerk_Steel_structure_Position',
// memory: false,
// type: 'REAL',
// byteAddress: 'No Address',
// address: 'I'
// },
if (
element.byteAddress != "No Address" &&
element.address.toLowerCase().startsWith("i")
) {
// Input
inputs[element.name] = {
start: element.byteAddress,
type: element.type,
pathToData: "SIMULATION.term01.inputs." + element.name,
plcVarName: element.name
};
} else if (
element.byteAddress != "No Address" &&
element.address.toLowerCase().startsWith("q")
) {
// Output
outputs[element.name] = {
start: element.byteAddress,
type: element.type,
pathToData: "SIMULATION.term01.outputs." + element.name,
plcVarName: element.name
};
}
if (maxByteNumber < getLastAddress(element.type, element.byteAddress)) {
maxByteNumber = getLastAddress(element.type, element.byteAddress);
}
});
const _plcConfig = {
plcs: {
SIMULATION: {
name: "SIMULATION",
modules: {
inputs: {
term01: {
start: 0,
len: maxByteNumber,
pathToData: "SIMULATION.modules.term01.rawData.input",
db: "input",
elements: inputs
}
},
outputs: {
term01: {
start: 0,
len: maxByteNumber,
pathToData: "SIMULATION.modules.term01.rawData.output",
db: "output",
elements: outputs
}
},
pathToSubscribe: "SIMULATION.modules.rawData.output",
pathToPublish: "SIMULATION.modules.rawData.input"
},
connection: {
mqtt: {
hostname: "localhost",
port: 1883
},
plc: {
ip: "192.168.2.2",
rack: 0,
slot: 0
}
}
}
},
load: "SIMULATION"
};
/** Write the new Configuration */
writeFileSync(_configFile, JSON.stringify(_plcConfig, undefined, 4));
/** Create a new Excel file
*
*/
// Create a new instance of a Workbook class
var wb = new xl.Workbook();
// Add Worksheets to the workbook
var ws = wb.addWorksheet("PLC Tags");
const rows = [
"Name",
"Path",
"Data Type",
"Logical Address",
"Comment",
"Hmi Visible",
"Hmi Accessible",
"Hmi Writeable",
"Typeobject ID",
"Version ID"
];
for (const [index, tag] of rows.entries()) {
/** Define the header */
ws.cell(1, index + 1).string(tag);
}
let rowNumber = 2;
for (const content of dataSheet.rows) {
if (content.byteAddress != "No Address") {
ws.cell(rowNumber, 1).string(content.name);
ws.cell(rowNumber, 2).string("Standard-Variablentabelle");
ws.cell(rowNumber, 3).string(capitalize(content.type));
// Determine the Prefix
const inOutPrefix =
content.address.toLowerCase().startsWith("i") ||
content.address.toLowerCase().startsWith("e")
? "I"
: "Q";
ws.cell(rowNumber, 4).string(
"%" + inOutPrefix + getPrefix(content.type) + content.byteAddress
);
ws.cell(rowNumber, 6).string("True");
ws.cell(rowNumber, 7).string("True");
ws.cell(rowNumber, 8).string("True");
rowNumber += 1;
}
}
wb.write(_pathToVarFile);
}
if (require.main == module) {
let _pathToExcelFile = "",
_configFile = "",
_pathToVarFile = "";
/** Extract the Process pathes. */
process.argv.forEach((value, idx) => {
switch (idx) {
case 2:
_pathToExcelFile = value;
break;
case 3:
_configFile = value;
break;
case 4:
_pathToVarFile = value;
break;
}
});
loadExcelFile(_pathToExcelFile, _configFile, _pathToVarFile);
}