import WebSocket = require('ws'); import MetaGrammarManifest from '../../src/org/subalternproductions/seepResource/dsl/serialize/MetaGrammarManifest'; import GrammarTestSuite from '../../src/org/subalternproductions/seepResource/dsl/testsuite/GrammarTestSuite'; import GrammarTest from '../../src/org/subalternproductions/seepResource/dsl/testsuite/GrammarTest'; import GrammarTestSrc from '../../src/org/subalternproductions/seepResource/dsl/testsuite/GrammarTestSrc'; import GrammarTestSuiteMgr from '../../src/org/subalternproductions/seepResource/dsl/testrunner/GrammarTestsuiteMgr'; import TESTS from '../../test/minimal-testsuites' import TEST_2 from '../../test/TEST_2' import { ITypeManifest, ArchiveDeserializer, Assert } from '@cafetextual/util'; import SimpleTypeManifest from '@cafetextual/util/dist/src/manifest/SimpleTypeManifest'; import {saveFile , loadFile, serialize } from './parse-ws-util' import ParserService, { createFromJSON } from '../parser-service/ParserService'; import ParseResult from '../../src/org/subalternproductions/seepResource/dsl/testsuite/ParseResult'; import ParseResponse from '../../src/org/subalternproductions/seepResource/dsl/service/ParseResponse'; import { deserializeParseRequest, serializeParseResponse } from '../../src/parse-service/parse-service-serializer'; import ParseRequest from '../../src/org/subalternproductions/seepResource/dsl/service/ParseRequest'; var WebSocketServer = require('ws').Server export default class ParserWsServer { constructor(port:number = 8007) { this.port = port; var archivePath:string = "/Users/ismcdonald/dev/seep2/seep/ex/SeasTestRunner/src/test/editable" var appPath:string = "/Users/ismcdonald/dev/seep2/seep/ex/SeasTestRunner/src/test" // hardcoding archive location for the moment this.savePath = archivePath + "/_saved/" } port:number private savePath:string private wss:any; private index:number = 0 // --------------- // Basics of a running service // createParseService(json:any):void { this.parseService = createFromJSON(json); } // createTestMgr private parseService:ParserService // --------------- start() { var wss = new WebSocketServer({port:this.port}); console.log(' web socket parse server started at localhost:8007') this.wss = wss wss.on('connection', (ws:any) => { var connectionName = "Cn#" + this.index.toString() this.index++ var protocol:string = "unknown" if (typeof ws.protocol === "string") { protocol = ws.protocol as string } this.setConnected(connectionName, ws, protocol) console.log(" new connection: " + connectionName + " protocol: " + protocol); ws.send(serialize("handshake", "Connected as connection: " + connectionName , {connectionID:connectionName})); ws.on('error', () => { console.log('-- some kind of tcp errored, probably client lost abruptly') }); ws.on('message', (message:string) => { // var type:string = message.type console.log("--- got message ") // message); // console.log(message) try { var o:any = JSON.parse(message) console.log(' parsed') } catch (e) { console.log(" failed to parse json") return } if (o && o.type) { console.log(" message of type: " + o.type) var msgType:string = o.type var target:string = o.target ? o.target : "*" if (msgType == "pub") { var channel:string = o.channel this.log(" got pub " + channel + " on protocol = " + protocol) // for the moment, be publish to all channels this.broadcast(connectionName, message, "*") return } else if (msgType == 'get-hardcoded-testcase') { // TODO - POST /parser/set-current-testsuite {etc} if (!this.parseService) { this.createParseService(TEST_2) } var res:any = this.parseService.rawResourceByDocURI("app/anyonic/test/test-contexts/gtest/tests/primatives.gtest.txt"); //app/anyonic/test/test-contexts/gtest/tests/primatives.gtest.txt //http://localhost:8001/app/anyonic/test/test-contexts/gtest/tests/rules.gtest.txt //http://localhost:8001/app/anyonic/test/test-contexts/gtest/tests/tokens.gtest.txt") var tss = this.parseService.allRawTss() var serializedTS:any = {resources:tss} ws.send(serialize('get-hardcoded-testcase', "testcase found", serializedTS ) ) } else if (msgType == "parse-request" ) { // ---- if (!this.parseService ) { Assert.fail(" TODO - implement error protocol") } // --- parse /w simple json object //var rawReq:{src:[string], srcId:string, grammarURI?:string, ruleName?:string } = o.data var req:ParseRequest = deserializeParseRequest(o.data) var res2:ParseResponse = this.parseService.doParse( req ) if (res2) { var plainObjectParseResponse = serializeParseResponse(res2) this.broadcast(null, serialize("parse-result", "-", plainObjectParseResponse), null ) } } else if (msgType == "anyonic-actor") { this.handleActorMsg(o) return } else if (msgType == "doc-request") { this.traceAppData() var data = this.getAppDataByTarget(protocol) if (data) { ws.send(data) } else { ws.send(serialize("error", "no data available for appID" + protocol, null)) this.log(" not sending app data to protocol: " + protocol) // if we had data for #protocol , could send it } } else if (msgType == "doc-script") { this.log(" serialize script from " + protocol) this.broadcast(connectionName, message, "*") // <--- scripts go everywhere, clients work out what to run } else if (msgType == "script-status") { console.log(" got script status") this.broadcast(connectionName, message, "#debug") } else if (msgType == "doc-serialization") { var uri:string = o.data.topURI var appID:string = o.data.appID if (appID) { this.persistDataByTarget(o.data, appID, uri) this.traceAppData() this.broadcastToTestSubs(uri) this.broadcast(connectionName, message, appID) // <-- TODO - target serializations if (uri) { console.log(" about to save: " + uri) saveFile(uri, this.savePath, message) } } } else if (msgType == "doc-increment") { var appID:string = o.data.appID if (appID) { console.log(" got incremental update for appID: " + appID) this.persistDataByTarget(null, protocol, uri) // <--- delete any persisted data for this target as it's no longer value this.broadcast(connectionName, message, appID) } else { this.log(" no appID from + " + protocol) } } else if (msgType == "dom") { console.log(" got dom serialization") this.broadcast(connectionName, message, "#debug") // <-- only send dom into to the debug client } else if (msgType == "dom-request") { console.log(" got dom request") this.broadcast(connectionName, message, "*") // <--- request dom from everything } else if (msgType == "figure-channel") { console.log(" got figure") } else if (msgType == "error") { console.log(" got error ") this.broadcast(connectionName, message, "#debug") // <--- request dom from everything } else if (msgType == "test-sub") { var testURI:string = o.data.testURI; this.addSubscription(connectionName, testURI) console.log(" recieved test subscription " + testURI ) this.broadcastToTestSubs( testURI) } else { ws.send(serialize("error", " unknown msgType : " + msgType, null)) } } // ws.send(serialize("echo", "connected", "")) this.traceConnections(connectionName); }) ws.on('close', () => { console.log('disconnecting ' + connectionName); this.removeConnection(connectionName) }); ws.on('open',() => { console.log('opening'); ws.send(Date.now().toString(), {mask: true}); }); }) } // start private allConnections:{[name:string]:{ ws:any, protocol:string, subs?:Array }} = {} private appDataByTarget:{[targetID:string]:any} = {} private appDataByURI:{[uri:string]:any} = {} handleActorMsg(data:Object) { console.log('x') } addSubscription(connectionName:string, testURI:string) { var connection:any = this.allConnections[connectionName] var subs:{[testURI:string]:boolean} = connection[connectionName] if (!subs) { subs = {} connection.subs = subs } subs[testURI] = true } setConnected(name:string, ws:any, protocol:string) { this.allConnections[name] = {ws:ws, protocol:protocol} } removeConnection(name:string) { console.log("removeing connection " + name) delete this.allConnections[name] this.traceConnections(name) } traceConnections(current:string) { this.log(" ------- all current connections -----") for (var key in this.allConnections) { var isCurrent = (key == current) ? " <--- current " : "" var {ws, protocol} = this.allConnections[key] this.log("c: " + key + " (connected) " + isCurrent + "protocol: " + protocol) } } log = (v:string) => { console.log(v) } traceAppData() { this.log(" ---- all currently registered app data ----") for (var key in this.appDataByTarget) { var data = this.appDataByTarget[key] this.log(" " + key + " size: " + data.length) } this.log(" -------------------------------------------") } getAppDataByTarget(appID:string) { return this.appDataByTarget[appID] } persistDataByTarget(data:any, appID:string, uri:string):void { this.appDataByTarget[appID] = data if (uri) { this.appDataByURI[uri] = data; } } /** * @param source name (uid) of source,to ensure message isn't broadcast back to self * @param msg data to broadcase * @param target name of target protocol */ broadcast(source:string, msg:string, target:string) { var noTarget:Boolean = !target || target == '*' for (var name in this.allConnections) {23 if (name != source) { var {ws, protocol} = this.allConnections[name] if (noTarget || target == protocol) { ws.send(msg) } } } } broadcastToTestSubs( testURI:string) { var data = this.appDataByURI[testURI] if (!data) { // could attempt to load from disk here data = loadFile(testURI, this.savePath) if (!data) { console.log('ATTN: no data for testURI ' + testURI) // {data:[ ]. } // when loaded, doesn't have return; } data = data.data } else { //data = data.data; console.log('x') } var connectionName:string for (connectionName in this.allConnections) { let { ws, subs } = this.allConnections[connectionName]; if (subs) { if (testURI in subs) { ws.send(serialize("test-sub", "test data for uri=" + testURI, data)) } } } } }