Nachdem die Open Source Technologie auf der IBM i eingeführt wurde, ergeben sich völlig neue Möglichkeiten die RPG Welt um neue Anwendungen zu erweitern. Node.js bietet eine ganze Palette von nützlichen Tools an: Versenden von SMS, Erstellen von PDFs und in unserem Beispiel, das Erstellen einer Excel-Datei aus einer Datenbanktabelle.
1. Systemvoraussetzungen
Auf Ihrer Maschine benötigen Sie Node.js, das Sie über das Open Source Package Management oder über YUM (habe ich in einem früheren Artikel beschrieben) installieren.
Damit Sie mit der QSHELL problemlos arbeiten können, müssen Sie die
ENVVAR QIBM_MULTI_THREADED auf Y einstellen

Um die Node.js Version festzustellen geben Sie folgendes ein:
5250>QSH
$ PATH=/QOpenSys/pkgs/bin:$PATH
export PATH
$ node -v

Die neueste Node.js Version ist v18.0.0
Das Tool für das Erstellen, Lesen und Ändern von Excel-Dateien nennt sich xlsx und finden Sie hier. Es ist sehr beliebt und hat derzeit 2 Millionen Downloads pro Woche. Es wird mit folgendem Command in der QSHELL installiert
$ npm i xlsx
Mit dem Befehl npm ls können Sie sich die instalierten Node.js Packages anzeigen lassen.

2. Eine einfache Excel-Datei mit Node.js und xlsx erstellen
Sie erstellen sich ein Verzeichnis z. B. /Home/Projects/Node/Excel für ihr Node.js Programm und erstellen dort eine Datei z. B. mit dem Namen CreateExcel.js
Bitte achten Sie darauf, dass diese Datei die CCSID 1208 hat. Mit WRKLNK können sie das leicht ändern.
Mit sechs Statements können wir diese Excel-Datei erstellen, die ich jetzt nachfolgend beschreibe.
const XLSX = require("xlsx");
Mit diesem Statement referenzieren Sie auf das xlsx Framework und die Konstante XLSX erbt damit alle Methoden des Frameworks. Das hat Ähnlichkeit mit Serviceprogrammen auf der IBM i.
const data = [
{Kunde:"Müller", Stadt:"Köln", Umsatz:12000},
{Kunde:"Heinrich", Stadt:"Hamburg", Umsatz:500},
{Kunde:"Scherzer", Stadt:"Hannover", Umsatz:5360}
];
Für die Definition der Daten habe ich mich für das JSON-Format entschieden, da wir das Programm später über einen Webservice per RPG-Programm mit embedded SQL aufrufen wollen.
Kunde, Stadt und Überschrift werden als Header für die Excel-Datei verwendet. In diesem Fall haben wir drei Zeilen mit den Daten.
const workSheet = XLSX.utils.json_to_sheet(data);
Damit erstellen wir das Arbeitsblatt mit den Daten.
const workBook = XLSX.utils.book_new();
Diese Anweisung erstellt eine neue Excel-Tabelle.
XLSX.utils.book_append_sheet(workBook, workSheet, "Kundenliste");
Jetzt geben wir dem Arbeitsblatt den Namen „Kundenliste“ und fügen Arbeitsblatt und Excel-Tabelle zusammen. Sie können auch mehrere Arbeitsblätter der Excel-Tabelle hinzufügen.
XLSX.writeFile(workBook, "/home/export/excel/Kundenliste.xlsx");
Jetzt schreiben die fertige Datei in das IFS.
Hier ist das komplette Listing:
const XLSX = require("xlsx");
const data = [
{Kunde:"Müller", Stadt:"Köln", Umsatz:12000},
{Kunde:"Heinrich", Stadt:"Hamburg", Umsatz:500},
{Kunde:"Scherzer", Stadt:"Hannover", Umsatz:5360}
];
const workSheet = XLSX.utils.json_to_sheet(data);
const workBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workBook, workSheet, "Kundenliste");
XLSX.writeFile(workBook, "/home/export/excel/Kundenliste.xlsx");
Das Programm starten Sie in der QSHELL mit dem Pfad ihrer Node.js Datei.
5250>QSH
$ PATH=/QOpenSys/pkgs/bin:$PATH
export PATH
$ node /home/projects/node/excel/createExcel.js
Damit erhalten Sie folgendes Ergebnis:

Die Dokumentation der APIs und der Möglichkeiten der xlsx Software finden sie auf folgender Website.
3. Die Kommunikation zwischen RPG und Node.js
Die Kommunikation zwischen Programmen auf der IBM i incl. der Parameterübergabe geht über den Befehl CALL. Mit Node.js ist es anders. Grundsätzlich gibt es zwei Möglichkeiten:
- Über DataQueues: ideal für asynchrone Aufrufe
- Über einen Webservice
In diesem Beispiel habe ich mich für den Aufruf über Webservice entschieden. Grundsätzlich funktioniert ein Webservice wie ein CALL, nur dass er auf ein anderes System erfolgt oder über das Internet. Der GET oder POST-Request entspricht dem CALL. Die Definition der Parameter erfolgt im JSON Format. SQL stellt dafür alle notwendigen Funktionen bereit.
Der Webservice funktioniert so:
- Mit Node.js wird ein Webservice definiert, gestartet und wartet auf POST Anfragen. Er läuft solange, bis der Job beendet wird.
- Dieser Webservice wird mit der SQL Function QSYS2.HTTP_POST aufgerufen, die drei Parameter hat: URL, Daten und Header.
URL: die URL enthält die IP-Adresse der Maschine, den Port (wird im Node.js Programm definiert) und die Funktion, z. B. http://172.16.0.120:8080/createExcel
Daten: Um die gleichen Dateien wie in Punkt 2 zu bekommen verwenden wir folgendes SQL- Statement. Die Datei QCUSTCDT verwende ich, weil sie jeder IBM i vorhanden ist. Sie können jede beliebige Datei verwenden. Den Namen der Excel-Datei sowie den Namen des Arbeitsblatts übergeben wir ebenfalls als Parameter, damit wir mehr Flexibilität haben.
select JSON_OBJECT(
'workFile' value '/home/export/excel/Kundenumsatz.xlsx',
'workBook' value 'Kundenumsatz',
'data' value
JSON_ARRAYAGG(
JSON_OBJECT(
'Kunde' value trim(LSTNAM),
'Stadt' value trim(CITY),
'Umsatz' value BALDUE
)
)
)
from QIWS.QCUSTCDT;
Header: der aufgerufene Webservice benötigt die Information in welchem Format die Daten kodiert sind. Das wird über den Header definiert. In unserem Fall: application/json. Wir erzeugen den Header ebenfalls mit JSON_OBJECT.
values json_object(
'header': 'Content-Type,application/json'
);
4. Das RPG Programm
Jetzt können wir das RPG Programm erstellen, dass das Node.js Programm über den Webservice aufruft.
ctl-opt dftactgrp(*no) main(main);
//------------------------------------------------------------------//
// //
// Excel-Datei mit Node.js und xlsx erstellen //
// //
//----------------- //
// R.Ross 07.2023 * //
//------------------------------------------------------------------//
// SQL-Options //
//------------------------------------------------------------------//
exec sql set option datfmt=*iso, timfmt=*iso, commit=*none,
decmpt=*period, closqlcsr=*endactgrp;
//------------------------------------------------------------------//
// Main //
//------------------------------------------------------------------//
dcl-proc main;
dcl-s LocJson sqltype(CLOB:1000000) ccsid(*utf8);
dcl-s LocResult sqltype(CLOB:1024) ccsid(*utf8);
exec sql
select JSON_OBJECT(
'workFile' value '/home/export/excel/Kundenumsatz.xlsx',
'workBook' value 'Kundenumsatz',
'data' value
JSON_ARRAYAGG(
JSON_OBJECT(
'Kunde' value trim(LSTNAM),
'Stadt' value trim(CITY),
'Umsatz' value BALDUE
)
)
)
into :LocJson
from QIWS.QCUSTCDT;
exec sql
values QSYS2.HTTP_POST(
'http://172.16.0.120:8080/createExcel',
:LocJson,
json_object('header':'Content-Type,application/json')
) into :LocResult;
end-proc;
//------------------------------------------------------------------//
5. Das Node.js Programm mit Webservice
Als nächstes erstellen wir den Code für den HTTP-Server, der die POST Requests entgegennimmt. Die benötigten Methoden dafür werden über require(‘http’) geladen. Der Port muss frei sein und darf von keiner anderen Applikation verwendet werden. Der Befehl um die verwendeten Adressen anzuzeigen ist: netstat *cnn.
Das Programm wird mit dem SQL POST Request aufgerufen. Die Daten stehen in der Variablen data. Dann wird die Funktion createExcel mit den Parameterdaten aufgerufen.
Das Programm nenne ich createExcelWebservice.js
//------------------------------------------------------------------------------//
// Node.js Module //
//------------------------------------------------------------------------------//
const XLSX = require('xlsx'); // Excel Modul
const http = require('http'); // HTTP Server
const port = 8080; // HTTP Server Port
//------------------------------------------------------------------------------//
// Antwort des HTTP-Servers //
//------------------------------------------------------------------------------//
let response = {
success: true,
error: ''
};
//------------------------------------------------------------------------------//
// HTTP-Server //
//------------------------------------------------------------------------------//
http.createServer(function(req, res) {
console.log('URL: ' + req.url);
console.log('Method: ' + req.method);
console.log('Content-Type: ' + req.headers['content-type']);
console.log('Content-Length: ' + req.headers['content-length']);
if (req.method === 'POST') {
let body = new Array();
req.on('data', function(data) {
body.push(data);
})
req.on('end', function() {
let data = JSON.parse(Buffer.concat(body).toString());
createExcel(data);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(response));
})
}
}).listen(port);
console.log('Server running at Port: ' + port);
//------------------------------------------------------------------------------//
// Create Excel //
//------------------------------------------------------------------------------//
function createExcel(data) {
const workSheet = XLSX.utils.json_to_sheet(data.data);
const workBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workBook, workSheet, data.workBook);
XLSX.writeFile(workBook, data.workFile);
}
6. Starten und Ablauf der Anwendung
Das Node.js Programm starten Sie in der QSHELL mit dem Pfad ihrer Node.js Datei.
5250>QSH
$ PATH=/QOpenSys/pkgs/bin:$PATH
export PATH
$ node /home/projects/node/excel/createExcelWebservice.js

Der Webservice ist nun bereit, um vom RPG aufgerufen zu werden. Nun rufen Sie das RPG Programm ganz normal mit CALL auf. Die QSHELL sieht dann so aus:

Das Ergebnis ist diese Excel-Datei:

Viel Spaß beim Ausprobieren.
Den Autor Rainer Ross erreichen Sie unter:
Rainer Ross IT-Beratung,
Hollweck-Str. 6,
85599 Parsdorf
Ausgezeichnet mit dem Innovationspreis der IBM und Spezialist für Webservices und Webanwendungen auf IBM i so schnell wie Greenscreen
Beispiel: www.myhofi.com/myapps/HTML/Myapp.html
Tel. (+49) 151/684 375 53 oder 089/413 252 94,
E-Mail: rainer_ross@web.de
Web: www.myhofi.com – Hotels finden – leicht gemacht
Für 88 Euro gibt’s hier sechs Monate lang tiefgreifendes IBM i und SQL Wissen. Hier kann man abonnieren.