From b1f264424c6e2088c71bdf6a089dd358f67fbc3e Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 31 May 2025 21:55:23 +0200 Subject: [PATCH] Prodress Connection fertig implementiert --- src/electron/main.ts | 58 +++++++----- src/electron/pdr2com/connection.ts | 139 +++++++++++++++++++++++++++++ src/electron/pdr2com/wrapper.ts | 60 ++++++++----- src/electron/tsconfig.json | 10 +-- 4 files changed, 218 insertions(+), 49 deletions(-) create mode 100644 src/electron/pdr2com/connection.ts diff --git a/src/electron/main.ts b/src/electron/main.ts index 987ca99..599f810 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -1,8 +1,6 @@ import {app, BrowserWindow} from 'electron'; -import {Socket} from 'net'; import path from 'path'; -import {R2RequestErweiterungsdaten, R2RequestKopfdaten, Request, RequestLeser} from '@prodress/pdr2com'; -import {ProdressRequest} from './pdr2com/wrapper.js'; +import {ProdressConnection} from './pdr2com/connection.js'; const APPLICATION_PATH = path.join(app.getAppPath(), '/dist-svelte/index.html'); @@ -14,23 +12,39 @@ app.on('ready', async () => { mainWindow.loadFile(APPLICATION_PATH); } - try { - 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); - } + const req = new ProdressConnection( + { + port: 4788, + host: 'localhost', + database: 'test', + handler: 'Shopify', + action: 'artikelSync', + }, + [['string', 'HalloWelt']], + [ + ['line1', 'string'], + ['line2', 'string'], + ], + ); + const res = await req.handleRequest(); + console.log(res); + // try { + // 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); + // } }); diff --git a/src/electron/pdr2com/connection.ts b/src/electron/pdr2com/connection.ts new file mode 100644 index 0000000..757a0b4 --- /dev/null +++ b/src/electron/pdr2com/connection.ts @@ -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 { + let response: string = ''; + const responseAsPromise = new Promise((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; + } +} diff --git a/src/electron/pdr2com/wrapper.ts b/src/electron/pdr2com/wrapper.ts index 9b72527..6b12b35 100644 --- a/src/electron/pdr2com/wrapper.ts +++ b/src/electron/pdr2com/wrapper.ts @@ -8,32 +8,18 @@ export interface DataTypeConversion { date_time: () => Date | null; } -export interface ReponseSchema { - readonly [key: string]: keyof DataTypeConversion; +export interface ResponseObject { + [key: string]: ReturnType; } +export type ResponseTupel = [string, keyof DataTypeConversion]; + export type RequestTupel = { [K in keyof DataTypeConversion]: [K, NonNullable>]; }[keyof DataTypeConversion]; export class ProdressRequest extends Request { - /** - * 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) { + public constructor(db: string, handler: string, action: string) { super( { interneProtokollVersion: 1, @@ -55,6 +41,40 @@ export class ProdressRequest extends Request { 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; + } } } diff --git a/src/electron/tsconfig.json b/src/electron/tsconfig.json index c1768cc..0c2d639 100644 --- a/src/electron/tsconfig.json +++ b/src/electron/tsconfig.json @@ -1,16 +1,12 @@ { "compilerOptions": { - // require strict types (null-save) "strict": true, - // tell TypeScript to generate ESM Syntax "target": "ESNext", - // tell TypeScript to require ESM Syntax as input (including .js file imports) "module": "NodeNext", - // define where to put generated JS + "moduleResolution": "NodeNext", + "esModuleInterop": true, "outDir": "../../dist-electron", - // ignore errors from dependencies - "skipLibCheck": true, - // add global types + "skipLibCheck": true //"types": ["../../types"] } }