From 5b8024ca9cc188ab68b1c722e42b5ec7c8f92391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Fri, 17 Dec 2021 13:23:49 +0100 Subject: [PATCH] [ivette/server] restore ZMQ client Failing due to non-context aware native modules --- ivette/.eslintignore | 2 - ivette/package.json | 4 +- ivette/src/frama-c/client_zmq.ts | 277 ++++++++++++------------------- ivette/src/frama-c/server.ts | 1 + ivette/tsconfig.json | 3 +- ivette/yarn.lock | 25 +++ 6 files changed, 140 insertions(+), 172 deletions(-) diff --git a/ivette/.eslintignore b/ivette/.eslintignore index 8744f886e55..80c4dad31d6 100644 --- a/ivette/.eslintignore +++ b/ivette/.eslintignore @@ -9,5 +9,3 @@ lib # don't lint the generated API api -# ZMQ Server is not working (yet) -src/frama-c/client_zmq.ts diff --git a/ivette/package.json b/ivette/package.json index 1077ae6437f..52655b7d59c 100644 --- a/ivette/package.json +++ b/ivette/package.json @@ -29,6 +29,7 @@ "@types/react": "^16", "@types/react-dom": "^16", "@types/react-virtualized": "^9.21.0", + "@types/zeromq": "^5.2.1", "@typescript-eslint/eslint-plugin": "", "@typescript-eslint/parser": "", "babel-loader": "^8.2.3", @@ -77,6 +78,7 @@ "react-virtualized": "^9.22.3", "react-window": "", "source-map-support": "^0.5.21", - "tippy.js": "" + "tippy.js": "", + "zeromq": "^5.2.8" } } diff --git a/ivette/src/frama-c/client_zmq.ts b/ivette/src/frama-c/client_zmq.ts index 88128454f15..afe15ed62de 100644 --- a/ivette/src/frama-c/client_zmq.ts +++ b/ivette/src/frama-c/client_zmq.ts @@ -20,23 +20,20 @@ /* */ /* ************************************************************************ */ -import Emitter from 'events'; -import { Request as ZmqRequest } from 'zeromq'; -import { json } from 'dome/data/json'; +import * as ZMQ from 'zeromq'; +import { Debug } from 'dome'; import { Client } from './client'; -const pollingTimeout = 50; +const D = new Debug('ZmqServer'); // -------------------------------------------------------------------------- // --- Frama-C Server API // -------------------------------------------------------------------------- -class ZmqClient implements Client { +class ZmqClient extends Client { - events = new Emitter(); - queueCmd: string[] = []; - queueId: string[] = []; - zmqSocket: ZmqRequest | undefined; + queue: string[] = []; + zmqSocket: ZMQ.Socket | undefined; zmqIsBusy = false; /** Server CLI */ @@ -46,18 +43,18 @@ class ZmqClient implements Client { /** Connection */ connect(sockaddr: string): void { - if (!this.zmqSocket) { + if (this.zmqSocket) { this.zmqSocket.close(); } - this.zmqSocket = new ZmqRequest(); + this.zmqSocket = new ZMQ.Socket('req'); this.zmqIsBusy = false; - this.zmqSocket.connect(sockaddr); + this.zmqSocket.connect(`ipc://${sockaddr}`); + this.zmqSocket.on('message', (msg: string[]) => this._receive(msg)); } disconnect(): void { this.zmqIsBusy = false; - this.queueCmd = []; - this.queueId = []; + this.queue = []; if (this.zmqSocket) { this.zmqSocket.close(); this.zmqSocket = undefined; @@ -66,191 +63,137 @@ class ZmqClient implements Client { /** Send Request */ send(kind: string, id: string, request: string, data: any): void { - this.queueCmd.push(kind, id, request, data); - this.queueId.push(id); - this._flush(); + if (this.zmqSocket) { + this.queue.push(kind, id, request, data); + this._flush(); + } } /** Signal ON */ - sigOn(id: string): void { this.queueCmd.push('SIGON', id); this._flush(); } + sigOn(id: string): void { + if (this.zmqSocket) { + this.queue.push('SIGON', id); + this._flush(); + } + } /** Signal ON */ - sigOff(id: string): void { this.queueCmd.push('SIGOFF', id); this._flush(); } + sigOff(id: string): void { + if (this.zmqSocket) { + this.queue.push('SIGOFF', id); + this._flush(); + } + } /** Kill Request */ kill(id: string): void { if (this.zmqSocket) { - this.queueCmd.push('KILL', id); + this.queue.push('KILL', id); this._flush(); } } /** Polling */ - poll(): void { } - - /** Shutdown the server */ - shutdown(): void { - this._reset(); + poll(): void { + if (this.zmqSocket && this.queue.length == 0) { + this.queue.push('POLL'); + } this._flush(); - this.queueCmd.push('SHUTDOWN'); - } - - /** Request data callback */ - onData(callback: (id: string, data: json) => void): void { - this.events.on('DATA', callback); - } - - /** Rejected request callback */ - onRejected(callback: (id: string, err: string) => void): void { - this.events.on('REJECT', callback); - } - - /** Request error callback */ - onError(callback: (msg: string) => void): void { - this.events.on('ERROR', callback); - } - - /** Killed request callback */ - onKilled(callback: (id: string) => void): void { - this.events.on('KILL', callback); } - /** Signal callback */ - onSignal(callback: (id: string) => void): void { - this.events.on('SIGNAL', callback); - } - - /** Idle callback */ - onIdle(callback: () => void): void { - this.events.on('CALLBACK', callback); + /** Shutdown the server */ + shutdown(): void { + this.queue = []; + if (this.zmqSocket) { + this.queue.push('SHUTDOWN'); + this._flush(); + } } // -------------------------------------------------------------------------- // --- Low-Level Management // -------------------------------------------------------------------------- - pollingTimer: NodeJS.Timeout | undefined; - flushingTimer: NodeJS.Immediate | undefined; - - _reset() { - if (this.flushingTimer) { - clearImmediate(this.flushingTimer); - this.flushingTimer = undefined; - } - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); - this.pollingTimer = undefined; - } - } - _flush() { - if (!this.flushingTimer) { - this.flushingTimer = setImmediate(() => { - this.flushingTimer = undefined; - this._send(); - }); - } - } - - _poll() { - if (!this.pollingTimer) { - this.pollingTimer = setTimeout(() => { - this.pollingTimer = undefined; - this._send(); - }, pollingTimeout); + const socket = this.zmqSocket; + if (socket) { + const cmds = this.queue; + if (cmds && !this.zmqIsBusy) { + try { + this.queue = []; + socket.send(cmds); + this.zmqIsBusy = true; + } catch (err) { + D.error('ZmqSocket', err); + this.zmqIsBusy = false; + } + } + } else { + this.queue = []; } } - async _send() { - // when busy, will be eventually re-triggered - if (!this.zmqIsBusy) { - const cmds = this.queueCmd; - if (!cmds.length) { - this.queueCmd.push('POLL'); - this.events.emit('IDLE'); - } - this.zmqIsBusy = true; - const ids = this.queueId; - this.queueCmd = []; - this.queueId = []; - try { - await this.zmqSocket?.send(cmds); - const resp = await this.zmqSocket?.receive(); - this._receive(resp); - } catch (error) { - this._error(`Error in send/receive on ZMQ socket. ${error.toString()}`); - const err = 'Canceled request'; - ids.forEach((rid) => this._reject(rid, err)); - } + _receive(resp: string[]) { + try { + this._decode(resp); + } catch (err) { + D.error('ZmqSocket', err); + } finally { this.zmqIsBusy = false; - this.events.emit('IDLE'); + setImmediate(() => this._flush()); } } - _data(id: string, data: any) { - this.events.emit('DATA', id, data); - } - - _reject(id: string, error: string) { - this.events.emit('REJECT', id, error); - } - - _signal(id: string) { - this.events.emit('SIGNAL', id); - } - - _error(err: any) { - this.events.emit('ERROR', err); - } - - _receive(resp: any) { - try { - let rid; - let data; - let err; - let cmd; - const shift = () => resp.shift().toString(); - let unknownResponse = false; - while (resp.length && !unknownResponse) { - cmd = shift(); - switch (cmd) { - case 'NONE': - break; - case 'DATA': - rid = shift(); - data = shift(); - this._data(rid, data); - break; - case 'KILLED': - rid = shift(); - this._reject(rid, 'Killed'); - break; - case 'ERROR': - rid = shift(); - err = shift(); - this._reject(rid, err); - break; - case 'REJECTED': - rid = shift(); - this._reject(rid, 'Rejected'); - break; - case 'SIGNAL': - rid = shift(); - this._signal(rid); - break; - case 'WRONG': - err = shift(); - this._error(`ZMQ Protocol Error: ${err}`); - break; - default: - this._error(`Unknown Response: ${cmd}`); - unknownResponse = true; - break; - } + /* eslint-disable @typescript-eslint/indent */ + _decode(resp: string[]) { + const shift = () => resp.shift() ?? ''; + while (resp.length) { + const cmd = shift(); + switch (cmd) { + case 'NONE': + break; + case 'DATA': + { + const rid = shift(); + const data = JSON.parse(shift()); + this.emitData(rid, data); + } + break; + case 'KILLED': + { + const rid = shift(); + this.emitKilled(rid); + } + break; + case 'ERROR': + { + const rid = shift(); + const msg = shift(); + this.emitError(rid, msg); + } + break; + case 'REJECTED': + { + const rid = shift(); + this.emitRejected(rid); + } + break; + case 'SIGNAL': + { + const rid = shift(); + this.emitSignal(rid); + } + break; + case 'WRONG': + { + const err = shift(); + D.error(`ZMQ Protocol Error: ${err}`); + } + break; + default: + D.error(`Unknown Response: ${cmd}`); + return; } - } finally { - if (this.queueCmd.length) this._flush(); - else this._poll(); } } diff --git a/ivette/src/frama-c/server.ts b/ivette/src/frama-c/server.ts index 198300d0fdc..8c84ddcfb27 100644 --- a/ivette/src/frama-c/server.ts +++ b/ivette/src/frama-c/server.ts @@ -38,6 +38,7 @@ import * as Json from 'dome/data/json'; import { RichTextBuffer } from 'dome/text/buffers'; import { ChildProcess } from 'child_process'; import { client } from './client_socket'; +//import { client } from './client_zmq'; // -------------------------------------------------------------------------- // --- Events diff --git a/ivette/tsconfig.json b/ivette/tsconfig.json index 1d43b92a486..432ed41d037 100644 --- a/ivette/tsconfig.json +++ b/ivette/tsconfig.json @@ -86,8 +86,7 @@ "node_modules", "dist", "src/dome/doc", - "src/dome/template", - "src/frama-c/client_zmq.ts" + "src/dome/template" ], "typedocOptions": { "name": "Ivette Documentation", diff --git a/ivette/yarn.lock b/ivette/yarn.lock index 979d6e22b45..cf9d9194ab9 100644 --- a/ivette/yarn.lock +++ b/ivette/yarn.lock @@ -1364,6 +1364,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/zeromq@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@types/zeromq/-/zeromq-5.2.1.tgz#f4316166e90fbe01e25ec6a6efa84e9bc8f91314" + integrity sha512-B6fUT5ZanWEyKSmLLEA0K3zNPvVedrjL6LNZWAt1eOpNg0J2lw14ismPJeOrgPf3SzRuNhPReQWibHbnMrtRow== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@": version "5.6.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.6.0.tgz#efd8668b3d6627c46ce722c2afe813928fe120a0" @@ -6221,6 +6228,11 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" +nan@2.14.2: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + nan@^2.12.1: version "2.15.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" @@ -6286,6 +6298,11 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== +node-gyp-build@^4.2.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" @@ -9145,3 +9162,11 @@ yauzl@^2.10.0: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +zeromq@^5.2.8: + version "5.2.8" + resolved "https://registry.yarnpkg.com/zeromq/-/zeromq-5.2.8.tgz#94b0b85e4152e98b8bb163f1db4a34280d44d9d0" + integrity sha512-bXzsk7KOmgLSv1tC0Ms1VXBy90+Rz27ZYf27cLuldRYbpqYpuWJfxxHFhO710t22zgWBnmdUP0m3SKFpLI0u5g== + dependencies: + nan "2.14.2" + node-gyp-build "^4.2.3" -- GitLab