Prodress Connection fertig implementiert

This commit is contained in:
Nils 2025-05-31 21:55:23 +02:00
parent 7e892e8c42
commit b1f264424c
4 changed files with 218 additions and 49 deletions

View File

@ -1,8 +1,6 @@
import {app, BrowserWindow} from 'electron'; import {app, BrowserWindow} from 'electron';
import {Socket} from 'net';
import path from 'path'; import path from 'path';
import {R2RequestErweiterungsdaten, R2RequestKopfdaten, Request, RequestLeser} from '@prodress/pdr2com'; import {ProdressConnection} from './pdr2com/connection.js';
import {ProdressRequest} from './pdr2com/wrapper.js';
const APPLICATION_PATH = path.join(app.getAppPath(), '/dist-svelte/index.html'); const APPLICATION_PATH = path.join(app.getAppPath(), '/dist-svelte/index.html');
@ -14,23 +12,39 @@ app.on('ready', async () => {
mainWindow.loadFile(APPLICATION_PATH); mainWindow.loadFile(APPLICATION_PATH);
} }
try { const req = new ProdressConnection(
const socket = new Socket(); {
socket.connect(4788, 'localhost'); port: 4788,
host: 'localhost',
const test = new ProdressRequest('db', 'handler', 'aktion', {}); database: 'test',
test.schreibeText('HalloWelt 2355435'); handler: 'Shopify',
socket.on('connect', () => { action: 'artikelSync',
console.log('verbunden'); },
test.sende(socket); [['string', 'HalloWelt']],
}); [
const r2reader = new RequestLeser(); ['line1', 'string'],
socket.on('data', (data: Buffer) => { ['line2', 'string'],
r2reader.verarbeiteDaten(data); ],
}); );
const r2request = await r2reader.leseRequest(); const res = await req.handleRequest();
console.log(r2request.nächsterText()); console.log(res);
} catch (error) { // try {
console.error(error); // const socket = new Socket();
} // socket.connect(4788, 'localhost');
//
// const test = new ProdressRequest('db', 'handler', 'aktion');
// test.schreibeText('HalloWelt 2355435');
// socket.on('connect', () => {
// console.log('verbunden');
// test.sende(socket);
// });
// const r2reader = new RequestLeser();
// socket.on('data', (data: Buffer) => {
// r2reader.verarbeiteDaten(data);
// });
// const r2request = await r2reader.leseRequest();
// console.log(r2request.nächsterText());
// } catch (error) {
// console.error(error);
// }
}); });

View File

@ -0,0 +1,139 @@
import {RequestLeser as ProdressListener, type R2AnfrageBlaupause} from '@prodress/pdr2com';
import {type ResponseTupel, type ResponseObject, ProdressRequest, type RequestTupel} from './wrapper.js';
import {Socket} from 'node:net';
export interface ProdressHeader {
port?: number;
host?: string;
database: string;
handler: string;
action: string;
}
// @todo move to nyi utils file
function stringifyError(error: Error): string {
const {message, stack} = error;
return JSON.stringify(
{
message,
stack,
},
null,
2,
);
}
export class ProdressConnection {
public static readonly TIMEOUT = 30_000;
private port: number;
private host: string;
private socket: Socket;
private request: ProdressRequest;
private listener: ProdressListener;
private requestTupelList: RequestTupel[];
private responseTupelList: ResponseTupel[];
public constructor(header: ProdressHeader, requestTupelList: RequestTupel[], responseTupelList: ResponseTupel[]) {
this.port = header.port ?? 4788;
this.host = header.host ?? 'localhost';
this.socket = new Socket();
this.request = new ProdressRequest(header.database, header.handler, header.action);
this.listener = new ProdressListener();
this.requestTupelList = requestTupelList;
this.responseTupelList = responseTupelList;
}
public async handleRequest(): Promise<string> {
let response: string = '';
const responseAsPromise = new Promise<string>((resolve) => {
this.socket.once('finished', () => {
if (!this.socket.destroyed) {
this.socket.destroy();
}
resolve(response);
});
});
this.socket.setTimeout(ProdressConnection.TIMEOUT);
this.socket.connect(this.port, this.host);
this.socket.once('connect', () => {
try {
this.request.writeEntries(this.requestTupelList);
this.request.sende(this.socket);
} catch (error) {
response = stringifyError(error as Error);
this.finished();
}
});
// @todo Rework Error Message
this.socket.on('timeout', () => {
const timeoutError = new Error('timeout');
response = stringifyError(timeoutError);
this.finished();
});
this.socket.on('error', (error: Error) => {
response = stringifyError(error);
this.finished();
});
this.socket.on('data', (data: Buffer) => {
this.listener.verarbeiteDaten(data);
this.socket.emit('dataRecieved');
});
this.socket.once('dataRecieved', async () => {
try {
const incomingResponse = await this.listener.leseRequest();
const responseObject = this.readResponse(incomingResponse);
response = JSON.stringify(responseObject, null, 2);
this.finished();
} catch (error) {
response = stringifyError(error as Error);
this.finished();
}
});
return responseAsPromise;
}
private finished(): void {
this.socket.emit('finished');
}
/**
* reads the Reponse based on the provided responseTupelList and creates a ResponseObject
* @param responseInstance The Listener Instance of the active TCP Socket
* @returns ResponseObject
*/
private readResponse(responseInstance: R2AnfrageBlaupause): ResponseObject {
const response: ResponseObject = {};
for (const tupel of this.responseTupelList) {
const [key, type] = tupel;
switch (type) {
case 'string':
response[key] = responseInstance.nächsterText();
break;
case 'integer':
response[key] = responseInstance.nächsteGanzzahl();
break;
case 'float':
response[key] = responseInstance.nächsteDezimalzahl();
break;
case 'date':
response[key] = responseInstance.nächstesDatum();
break;
case 'date_time':
//@todo missing method in lib
response[key] = responseInstance.nächstesDatum();
break;
}
}
return response;
}
}

View File

@ -8,32 +8,18 @@ export interface DataTypeConversion {
date_time: () => Date | null; date_time: () => Date | null;
} }
export interface ReponseSchema { export interface ResponseObject {
readonly [key: string]: keyof DataTypeConversion; [key: string]: ReturnType<DataTypeConversion[keyof DataTypeConversion]>;
} }
export type ResponseTupel = [string, keyof DataTypeConversion];
export type RequestTupel = { export type RequestTupel = {
[K in keyof DataTypeConversion]: [K, NonNullable<ReturnType<DataTypeConversion[K]>>]; [K in keyof DataTypeConversion]: [K, NonNullable<ReturnType<DataTypeConversion[K]>>];
}[keyof DataTypeConversion]; }[keyof DataTypeConversion];
export class ProdressRequest extends Request { export class ProdressRequest extends Request {
/** public constructor(db: string, handler: string, action: string) {
* Mapping of a datatype to the correspondig read method
*/
public readonly responseSchemaConversion: DataTypeConversion = {
string: this.nächsterText,
integer: this.nächsteGanzzahl,
float: this.nächsteDezimalzahl,
date: this.nächstesDatum,
date_time: this.nächstesDatumMitZeit,
} as const;
/**
* Definition of the Reponse
*/
public readonly responseSchema: ReponseSchema;
public constructor(db: string, handler: string, action: string, responseSchema: ReponseSchema) {
super( super(
{ {
interneProtokollVersion: 1, interneProtokollVersion: 1,
@ -55,6 +41,40 @@ export class ProdressRequest extends Request {
maximalePaketgröße: 0, maximalePaketgröße: 0,
}, },
); );
this.responseSchema = responseSchema; }
/**
* writes entries based on the provided requestTupelList into the request
* @param requestTupelList
*/
public writeEntries(requestTupelList: RequestTupel[]): void {
for (const tupel of requestTupelList) {
this.writeEntry(tupel);
}
}
/**
* writes an entry based on the provided requestTupel into the request
* @param requestTupel
*/
public writeEntry(requestTupel: RequestTupel): void {
const [type, value] = requestTupel;
switch (type) {
case 'string':
this.schreibeText(value);
break;
case 'integer':
this.schreibeGanzzahl(value);
break;
case 'float':
this.schreibeDezimalzahl(value);
break;
case 'date':
this.schreibeDatum(value);
break;
case 'date_time':
this.schreibeDatumMitZeit(value);
break;
}
} }
} }

View File

@ -1,16 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
// require strict types (null-save)
"strict": true, "strict": true,
// tell TypeScript to generate ESM Syntax
"target": "ESNext", "target": "ESNext",
// tell TypeScript to require ESM Syntax as input (including .js file imports)
"module": "NodeNext", "module": "NodeNext",
// define where to put generated JS "moduleResolution": "NodeNext",
"esModuleInterop": true,
"outDir": "../../dist-electron", "outDir": "../../dist-electron",
// ignore errors from dependencies "skipLibCheck": true
"skipLibCheck": true,
// add global types
//"types": ["../../types"] //"types": ["../../types"]
} }
} }