Passing socket between multiple client side javascript files - javascript

I recently started coding a small web game in javascript. Even more recently, I started refactoring the code to use node.js with socket.io to make the game multiplayer.
My issue, which I cannot find a solution for, is that I would like to pass the client side socket variable to multiple client side javascript files.
In client/app.js:
var app = (function() {
...
var socket = io.connect('http://localhost:8080');
socket.on('connect', function() {
dice.setSocket(socket);
});
...
})();
In client/dice.js:
var dice = (function() {
...
var gameSocket;
if (gameSocket) {
gameSocket.on('dice rolling', function(data) {
playDiceAnim(data);
});
}
function setSocket(socket) {
gameSocket = socket;
}
})();
Unfortunately, when the 'dice rolling' event is emitted from the server side, playDiceAnim(data) is never executed. But, I know that the 'dice rolling' event is being received by app.js. For example, if in app.js I put:
var app = (function() {
...
var socket = io.connect('http://localhost:8080');
socket.on('dice rolling', function() {
console.log('event received');
});
...
})();
Then, the result in the developer tools console is:
'event received'
The examples that I can find online either put all client side javascript into a script tag in a .html file, or in a single .js file. But, I would like to be more modular than that.
How can I appropriately pass and use the client side socket to other client side files?
Here is the full code. Beware of bugs, it is in the process of being refactored. Also, any aside advice would be greatly appreciated as I am still fairly new to javascript and node.js.

Here's what I would suggest:
Load the app script first and have it make the socket available to other callers via a method:
var app = (function() {
...
var socket = io.connect('http://localhost:8080');
return {
getSocket: function() { return socket;}
}
...
})();
var dice = (function() {
...
var gameSocket = app.getSocket();
if (gameSocket) {
gameSocket.on('dice rolling', function(data) {
playDiceAnim(data);
});
}
})();
In this case, I think it makes more sense for the app to be a provider of services such as a connected webSocket and for other modules to request that information from the app. But, it really depends upon which module you want to be more generic and not depend upon the other. It could go either way.

Related

Send data from Server script to Client script (and not the other way around)

I am working on a widget, and I use data/input variables to communicate between the Client script to Server script.
for ex., in the Client Script I use:
c.data.action= "removeEvent";
c.server.update().then(function(){ c.data.action = undefined;});
and I am waiting that action in the Server Script with:
if(input.action == "removeEvent"){
//some code here...
}
My problem is that once I completed that action (in the Server Script) I need to go back to my Client Script to updated the data that is showed to the user and trigger other functions. So, how can I know from the Client Script that the code in the Server Script has ended?
I try to use the data variable again like:
// In Server Script
data.finished = true;
//In client Script
if(data.finished ) {
//do something
}
But, the Client Script doesn't update.
Is there a way to do it with a watcher or subscribe on a variable so when it changes I know that the Server Script has finished?
Thanks all for your help!
To an extent this will depend on your client - Whether the client is service portal, platform UI, seismic+UIB page etc.
In general you have 2 options in this case
Record watcher: https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_serviceportal_quebec_recordwatch
OR
Flow designer: Create a flow which will watch the server script or side-effect of server script like change to a record in database and based on that inject an action which will trigger refresh on the client UI.
In either case the client will need a callable hook that can be called by the server backend.
I ended up using GlideAjax and Scripts Includes that allows you to create functions that have return values for asynchronous or synchronous calls.
Exemple:
First, create the Ajax Call and their return function:
var ga = new GlideAjax("ucd_GetLocationData");
ga.addParam("sysparm_name", "getCampus");
ga.addParam("sysparm_buildingid", g_form.getValue("u_building"));
ga.getXML(updateCampus);
function updateCampus(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
var clearvalue; // Stays Undefined
if (answer) {
var returneddata = JSON.stringify(answer);
g_form.setValue("campus", returneddata.sys_id, returneddata.name);
} else {
g_form.setValue("campus", clearvalue);
}
}
Finally, create a Server Script (Script Include):
var ucd_GetLocationData = Class.create();
ucd_GetLocationData.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getCampus: function() {
var buildingid = this.getParameter("sysparm_buildingid");
var loc = new GlideRecord("cmn_location");
if (loc.get(buildingid)) {
var campus = new GlideRecord("cmn_location");
if (campus.get(loc.parent)) {
var json = new JSON();
var results = {
sys_id: campus.getValue("sys_id"),
name: campus.getValue("name")
};
return json.parse(results);
}
} else {
return null;
}
}
});
Check the tutorial from here:
https://sn.jace.pro/getting-started/GlideAjax/

How to efficiently stream a real-time chart from a local data file

complete noob picking up NodeJS over the last few days here, and I've gotten myself in big trouble, it looks like. I've currently got a working Node JS+Express server instance, running on a Raspberry Pi, acting as a web interface for a local data acquisition script ("the DAQ"). When executed, the script writes out data to a local file on the Pi, in .csv format, writing out in real-time every second.
My Node app is a simple web interface to start (on-click) the data acquisition script, as well as to plot previously acquired data logs, and visualize the actively being collected data in real time. Plotting of old logs was simple, and I wrote a JS function (using Plotly + d3) to read a local csv file via AJAX call, and plot it - using this script as a starting point, but using the logs served by express rather than an external file.
When I went to translate this into a real-time plot, I started out using the setInterval() method to update the graph periodically, based on other examples. After dealing with a few unwanted recursion issues, and adjusting the interval to a more reasonable setting, I eliminated the memory/traffic issues which were crashing the browser after a minute or two, and things are mostly stable.
However, I need help with one thing primarily:
Improving the efficiency of my first attempt approach: This acquisition script absolutely needs to be written to file every second, but considering that a typical run might last 1-2 weeks, the file size being requested on every Interval loop will quickly start to balloon. I'm completely new to Node/Express, so I'm sure there's a much better way of doing the real-time rendering aspect of this - that's the real issue here. Any pointers of a better way to go about doing this would be massively helpful!
Right now, the killDAQ() call issued by the "Stop" button kills the underlying python process writing out the data to disk. Is there a way to hook into using that same button click to also terminate the setInterval() loop updating the graph? There's no need for it to be updated any longer after the data acquisition has been stopped so having the single click do double duty would be ideal. I think that setting up a listener or res/req approach would be an option, but pointers in the right direction would be massively helpful.
(Edit: I solved #2, using global window. variables. It's a hack, but it seems to work:
window.refreshIntervalId = setInterval(foo);
...
clearInterval(window.refreshIntervalId);
)
Thanks for much for the help!
MWE:
html (using Pug as a template engine):
doctype html
html
body.default
.container-fluid
.row
.col-md-5
.row.text-center
.col-md-6
button#start_button(type="button", onclick="makeCallToDAQ()") Start Acquisition
.col-md-6
button#stop_button(type="button", onclick="killDAQ()") Stop Acquisition
.col-md-7
#myDAQDiv(style='width: 980px; height: 500px;')
javascript (start/stop acquisition):
function makeCallToDAQ() {
fetch('/start_daq', {
// call to app to start the acquisition script
})
.then(console.log(dateTime))
.then(function(response) {
console.log(response)
setInterval(function(){ callPlotly(dateTime.concat('.csv')); }, 5000);
});
}
function killDAQ() {
fetch('/stop_daq')
// kills the process
.then(function(response) {
// Use the response sent here
alert('DAQ has stopped!')
})
}
javascript (call to Plotly for plotting):
function callPlotly(filename) {
var csv_filename = filename;
console.log(csv_filename)
function makeplot(csv_filename) {
// Read data via AJAX call and grab header names
var headerNames = [];
d3.csv(csv_filename, function(error, data) {
headerNames = d3.keys(data[0]);
processData(data, headerNames)
});
};
function processData(allRows, headerNames) {
// Plot data from relevant columns
var plotDiv = document.getElementById("plot");
var traces = [{
x: x,
y: y
}];
Plotly.newPlot('myDAQDiv', traces, plotting_options);
};
makeplot(filename);
}
node.js (the actual Node app):
// Start the DAQ
app.use(express.json());
var isDaqRunning = true;
var pythonPID = 0;
const { spawn } = require('child_process')
var process;
app.post('/start_daq', function(req, res) {
isDaqRunning = true;
// Call the python script here.
const process = spawn('python', ['../private/BIC_script.py', arg1, arg2])
pythonPID = process.pid;
process.stdout.on('data', (myData) => {
res.send("Done!")
})
process.stderr.on('data', (myErr) => {
// If anything gets written to stderr, it'll be in the myErr variable
})
res.status(200).send(); //.json(result);
})
// Stop the DAQ
app.get('/stop_daq', function(req, res) {
isDaqRunning = false;
process.on('close', (code, signal) => {
console.log(
`child process terminated due to receipt of signal ${signal}`);
});
// Send SIGTERM to process
process.kill('SIGTERM');
res.status(200).send();
})

Capturing refresh on haskell websockets example server?

The websockets server example works as expected. On browser refresh (e.g. S-F5 with chrome), the websocket disconnects, still working as expected. After refresh, the user has to give name again to connect to the server.
How would you capture the refresh-event and keep the user connected? E.g.
Is this doable only on server side or does the client require modifications as well? Haskell examples or links to such would be nice as well as hints on how to do this!
How would you capture the refresh-event...
There isn't really such a thing as a refresh event to detect (I would love to be proved wrong in this!)
... and keep the user connected...
The refresh, or rather, the leaving of the page before loading it again, causes the websocket to disconnect, and (especially if this is the only page on the site that is open), there isn't really much you can do about it.
So the only thing that can be done, is have some sort of auto-reconnect the next time the page loads. A solution that allows this is one where..
when the name is initially entered, the name is saved somewhere in the browser;
when the page reloads, it checks for a previously saved name;
and if it's found, it connects again using that name.
Local storage is one such place to save this, as in the below example, modified from https://github.com/jaspervdj/websockets/tree/master/example to save/retrieve the name from local storage.
$(document).ready(function () {
var savedUser = sessionStorage.getItem("rejoin-user");
if (savedUser) {
joinChat(savedUser);
}
$('#join-form').submit(function () {
joinChat($('#user').val())
});
function joinChat(user) {
sessionStorage.setItem("rejoin-user", user);
$('#warnings').html('');
var ws = createChatSocket();
ws.onopen = function() {
ws.send('Hi! I am ' + user);
};
ws.onmessage = function(event) {
if(event.data.match('^Welcome! Users: ')) {
/* Calculate the list of initial users */
var str = event.data.replace(/^Welcome! Users: /, '');
if(str != "") {
users = str.split(", ");
refreshUsers();
}
$('#join-section').hide();
$('#chat-section').show();
$('#users-section').show();
ws.onmessage = onMessage;
$('#message-form').submit(function () {
var text = $('#text').val();
ws.send(text);
$('#text').val('');
return false;
});
} else {
$('#warnings').append(event.data);
ws.close();
}
};
$('#join').append('Connecting...');
return false;
};
});
... Is this doable only on server side or does the client require modifications as well?
It definitely needs something done in the client to auto-reconnect. The bare bones version above needs no changes to the server, but if you wanted something fancier, like having the cases of initial connect and auto reconnect handled/shown differently somehow, then the server might need to be modified.

nodejs for linux server programming / as scripting language

I am writing a script for provisioning new users for my application.
Script will be written in node, as one of its tasks will be connecting to mysql to create new users in application's database.
I tried to use spawn-sync library (that surprisingly seems to be also async) to execute bash commands but every single one of them I need to do the following:
var spawnSync = require('spawn-sync');
var user_name = process.argv[2];
new Promise((resolve)=>{
var result = spawnSync('useradd',[user_name]);
if (result.status !== 0) {
process.stderr.write(result.stderr);
process.exit(result.status);
} else {
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
}
resolve()
}).then(new Promise(function(resolve){
// execute another part of script
resolve()
})
Is there a better way of doing this? Whenever I try to look something up, all tutorials on the web seem to be talking only about express when it comes to the nodejs context.
Or perhaps you discourage using nodejs to be used as a scripting serverside laguage?
If you want to interact with processes synchronously, Node.js has that functionality built in via child_process.execSync(). Note that if the child process has a non-zero exit code it will throw (so you'll need to wrap it with a try/catch).
try {
var cmd = ['useradd', user_name].join(' ');
var stdout = require('child_process').execSync(cmd);
console.log(stdout);
}
catch (e) {
console.log(e);
}

Angularjs websocket service memory leak

In my project I have an angular factory which will take care of the websocket connection with a c++ app.
Structure of the websocket factory:
.factory('SocketFactory', function ($rootScope) {
var factory = {};
factory.connect = function () {
if(factory.ws) { return; }
var ws = new WebSocket('ws://...');
ws.onopen = function () {
console.log('Connection to the App established');
};
ws.onerror = function () {
console.log('Failed to established the connection to the App');
};
ws.onclose = function () {
console.log('Connection to the App closed');
};
ws.onmessage = function (message) {
//do stuff here
};
factory.ws = ws;
};
factory.send = function (msg) {
if(!factory.ws){ return; }
if(factory.ws.readyState === 1) {
factory.ws.send(JSON.stringify(msg));
}
};
return factory;
});
The c++ app will be sending images via websockets and they will be shown in a canvas that will be updated everytime a new image is received.
Everything works fine, however as soon as I start sending images to the browser, I noticed in ubuntu's system resource monitor that the memory used by chrome process keeps increasing +/-5mb each time ws.onMessage is fired (approximately).
I commented the code inside ws.onMessage leaving just the event detection and nothing else and the memory used still increases, if I comment the whole ws.onMessage event the memory used stays inside normal limits.
Do you have any suggestions to solve this problem? Is this happening because I should be using $destroy to prevent this kind of loop?
This turned out to be a bit more confusing than I thought.
First of all, instead of using the websocket service shown above, I used this one: https://github.com/gdi2290/angular-websocket
The memory leak still persisted, but I noticed that, during the canvas updating process, there was a reference to an imageObj.src that was not being garbage collected.
Chrome, opera and firefox seem to keep used memory between reasonable limits now.

Categories

Resources