Triggering events from async/await not respecting order - node.js

I'm having some issues understanding/applying a correct order of events. I'm working on a "frankenstein" code, where from the back-end of nodejs I call several other files/executables that work together to produce a final result. This system has a queue and if the DB has more documents to process the cycle should restart after the final step, until every document is read.
Basically: I call excel vba, that excel calls an exe file that is commanded by excel during it's processing, after that, those are closed and another exe is invoked and should finish processing before.
The problem is that when I have documents on queue the all system starts running ahead of time, which leads to the programs not being closed properly and jamming everything down the line.
Below is the code:
const { getReports, getToDo, sendOutput, parseJSON, getEmail} = require('./dbAPI');
const { execSync } = require('child_process');
const excel = require('exceljs');
const fs = require('fs');
const iconv = require('iconv-lite');
const nodemailer = require('nodemailer');
const exec = require('child_process').execFile;
let reportId;
require('log-timestamp');
//const { watch } = require('fs/promises');
const { report } = require('process');
//const ac = new AbortController();
//const { signal } = ac;
//setTimeout(() => ac.abort(), 10000);
let working = false;
process.on('message', (message) => {
if (message === 'new input') {
console.log('New Input Detected');
working || processInput();
// Used the OR operator from JS.
// If working === true, nothing happens; if working === false, the function is called.
// This prevents the function to be called on top of an already working process.
// This way, the program will start processing forms only when it's not already doing so.
}
});
async function processInput() {
working = true;
console.log('Started working');
while (true) {
let report = await getToDo();
reportId = parseJSON(report);
if (typeof report === 'undefined') break;
console.log('----------------------------------------');
transformData(report.inputs);
await writeToExcel(report);
execSync('start "Form Processing" /WAIT "C:/ibasim/reportProcessing/IBAS_v1.0.2.xlsm"');
//await generateOutput(report.outputs); Ana commented
console.log('Start counting')
const delay = ms => new Promise(resolve => setTimeout(resolve,ms))
await delay(240000)
console.log('Timeout reached')
//checkExistsWithTimeout('./output.xlsx', 400000)
let output = await readOutputXlsx(); //await
await sendOutput(report, output);
try {
// console.log("Drawings will start...");
await startDrawing();
fs.unlinkSync('C:/ibasim/output.xlsx')
console.log('File removed')
//form.kill();
//console.log('Form killed');
// await notificationEmail();
//file removed
} catch(err) {
//sendEmail();
//console.error(err)
return;
}
// (async () => {
// try {
// console.log("watching...");
// let output = awayt readOutputXlsx(); //await
// await sendOutput(report, output); //await
// } catch (err) {
// if (err.name === 'AbortError')
// return;
// throw err;
// }})();
// let output = await readOutputXlsx(); //await
// await sendOutput(report, output); //await
//
//let filename = './output.xlsx';
// (async () => {
// try {
// console.log("watching...");
// fs.watch('./output.xlsx', (eventType, filename) => { //watch('./output.xlsx', { signal }); //const watcher =
// console.log("watching...");
// //for await (const event of watcher)
// if (filename) {
// console.log("XLSX file created");
// let output = readOutputXlsx(); //await
// sendOutput(report, output); //await
// }
// });
// } catch (err) {
// if (err.name === 'AbortError')
// return;
// throw err;
// }
// })();
//
}
working = false;
process.send('Finished working, exiting...');
process.exit();
}
async function writeToExcel(report) {
let workbook = new excel.Workbook();
let worksheet = workbook.addWorksheet('study');
let filename = 'C:/ibasim/study.xlsx';
// Auxiliary variable used to create an array and then equal the worksheet columns to this array.
// Did this because you can't push elements to worksheet.columns, you can only change that once,
// so I was getting an error.
columns = [];
for (let key in report.inputs) {
columns.push({
header: key,
key: key,
});
}
worksheet.columns = columns;
const row2 = worksheet.getRow(2);
row2.values = report.inputs;
worksheet.getRow(3).getCell(1).value = reportId;
//await workbook.csv.writeFile(filename);
await workbook.xlsx.writeFile(filename, { encoding: 'utf-8' });
//originalFile = fs.readFileSync(filename, {encoding: 'binary'});
//decodedFile = iconv.decode(filename, 'iso88591');
console.log('Written Excel');
return;
}
function transformData(inputs) {
try {
// For every single field that needs to be a number and not a string, we try to convert it to a float.
// If the input is a number, the parseFloat will succeed, otherwise it will return NaN.
// When it returns NaN, we keep the input as it is, as later it will trigger an error in external software.
// Before using parseFloat, we replace any commas (,) with periods (.) as the user may have inputted
// a number as 12,7 instead of 12.7 and that doesn't classify as a float.
inputs.vao = inputs.vao.replace(',', '.');
inputs.vao = parseFloat(inputs.vao) !== NaN ? parseFloat(inputs.vao) : inputs.vao;
inputs.c = inputs.c.replace(',', '.');
inputs.c = parseFloat(inputs.c) !== NaN ? parseFloat(inputs.c) : inputs.c;
inputs.af = inputs.af.replace(',', '.');
inputs.af = parseFloat(inputs.af) !== NaN ? parseFloat(inputs.af) : inputs.af;
inputs.h = inputs.h.replace(',', '.');
inputs.h = parseFloat(inputs.h) !== NaN ? parseFloat(inputs.h) : inputs.h;
inputs.hc = inputs.hc.replace(',', '.');
inputs.hc = parseFloat(inputs.hc) !== NaN ? parseFloat(inputs.hc) : inputs.hc;
inputs.afmc = inputs.afmc.replace(',', '.');
inputs.afmc = parseFloat(inputs.afmc) !== NaN ? parseFloat(inputs.afmc) : inputs.afmc;
inputs.afmf = inputs.afmf.replace(',', '.');
inputs.afmf = parseFloat(inputs.afmf) !== NaN ? parseFloat(inputs.afmf) : inputs.afmf;
inputs.platibandas_comprimento = inputs.platibandas_comprimento.replace(',', '.');
inputs.platibandas_comprimento = parseFloat(inputs.platibandas_comprimento) !== NaN ? parseFloat(inputs.platibandas_comprimento) : inputs.platibandas_comprimento;
inputs.altitude = inputs.altitude.replace(',', '.');
inputs.altitude = parseFloat(inputs.altitude) !== NaN ? parseFloat(inputs.altitude) : inputs.altitude;
inputs.piso_area = inputs.piso_area.replace(',', '.');
inputs.piso_area = parseFloat(inputs.piso_area) !== NaN ? parseFloat(inputs.piso_area) : inputs.piso_area;
inputs.piso_altura = inputs.piso_altura.replace(',', '.');
inputs.piso_altura = parseFloat(inputs.piso_altura) !== NaN ? parseFloat(inputs.piso_altura) : inputs.piso_altura;
} catch (error) {}
return;
}
// async function generateOutput(outputs) {
// let workbook = new excel.Workbook();
// let worksheet = workbook.addWorksheet('outputs');
// let filename = './output.xlsx';
// for (let key in outputs) {
// worksheet.addRow('');
// if (typeof outputs[key] === 'object' && key !== 'piso' && key !== 'platibandas') {
// worksheet.addRow([key]);
// for (let property in outputs[key]) {
// worksheet.addRow([property, outputs[key][property]]);
// }
// } else if (key === 'piso') {
// worksheet.addRow([key]);
// for (let subObject in outputs[key]) {
// worksheet.addRow([subObject]);
// for (let property in outputs[key][subObject]) {
// worksheet.addRow([property, outputs[key][subObject][property]]);
// }
// }
// } else if (key === 'platibandas') {
// worksheet.addRow([key]);
// for (let property in outputs[key]) {
// if (typeof outputs[key][property] === 'object') {
// worksheet.addRow([property]);
// for (let subProperty in outputs[key][property]) {
// worksheet.addRow([subProperty, outputs[key][property][subProperty]]);
// }
// } else {
// worksheet.addRow([property, outputs[key][property]]);
// }
// }
// } else {
// worksheet.addRow([key, outputs[key]]);
// }
// }
// await workbook.xlsx.writeFile(filename);
// return;
// }
async function readOutputXlsx() {
console.log("watching...");
// (async () => {fs.watch('./output.xlsx', (eventType, filename) => { //watch('./output.xlsx', { signal }); //const watcher =
// console.log("watching...");
// //for await (const event of watcher)
// if (filename) {
// console.log("XLSX file created");
// }
// });})
let filename = 'C:/ibasim/output.xlsx';
console.log("Output starting to read")
let output = {};
const workbook = new excel.Workbook();
//await workbook.xlsx.readFile('./output.xlsx');
try {
await workbook.xlsx.readFile('C:/ibasim/output.xlsx');
} catch(err) {
// create reusable transporter object using the default SMTP transport
//var transporter = nodemailer.createTransport('smtps://user%40gmail.com:pass#smtp.gmail.com');
// // create reusable transporter object using the default SMTP transport
// let transporter = nodemailer.createTransport({
// host: 'smtp-pt.securemail.pro',
// port: 465,
// secure: true, // true for 465, false for other ports
// auth: {
// user: process.env.sender, // generated ethereal user
// pass: process.env.emailPassword // generated ethereal password
// },
// tls: {
// rejectUnauthorized: false,
// },
// });
// // send mail with defined transport object
// let info = await transporter.sendMail({
// from: `"IBAS Error Warning" <${process.env.sender}>`, // sender address
// to: process.env.receiverOfError, // list of receivers
// subject: "Error Warning", // Subject line
// text: "Error", // plain text body
// html:"<p>Error on study with id " + reportId + "</p>"
// });
// // send mail with defined transport object
// transporter.sendMail(mailOptions, function(error, info){
// if(error){
// return console.log(error);
// }
// console.log('Message sent: ' + info.response);
// });
// console.log("this file does not exist!");
return;
}
let rows = workbook.getWorksheet().getSheetValues();
rows = rows.filter((a) => a);
for (let index = 0; index < rows.length; index++) {
if (rows[index][1] !== 'piso' && rows[index][1] !== 'platibandas') {
let subobject = rows[index][1];
output[subobject] = {};
index++;
while (index < rows.length && rows[index].length === 3) {
if (rows[index][1] === 'componentesSoldados') {
output['componentesSoldados'] = rows[index][2];
index++;
continue;
} else if (rows[index][1] === 'debug') {
output['debug'] = rows[index][2];
index++;
continue;
}
output[subobject][rows[index][1]] = rows[index][2];
index++;
}
index--;
} else if (rows[index][1] === 'piso') {
output['piso'] = {};
index++;
for (let i = 0; i < 3; i++) {
let subobject = rows[index][1];
output['piso'][subobject] = {};
index++;
while (index < rows.length && rows[index].length === 3) {
output['piso'][subobject][rows[index][1]] = rows[index][2];
index++;
}
}
index--;
} else if (rows[index][1] === 'platibandas') {
output['platibandas'] = {};
index++;
while (index < rows.length && rows[index].length === 3) {
output['platibandas'][rows[index][1]] = rows[index][2];
index++;
}
let subobject = rows[index][1];
output['platibandas'][subobject] = {};
index++;
while (index < rows.length && rows[index].length === 3) {
output['platibandas'][subobject][rows[index][1]] = rows[index][2];
index++;
}
index--;
}
}
console.log("Output read")
return output;
}
async function sendEmail () {
// create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
host: 'smtp-pt.securemail.pro',
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: process.env.sender, // generated ethereal user
pass: process.env.emailPassword // generated ethereal password
},
tls: {
rejectUnauthorized: false,
},
});
// send mail with defined transport object
let info = await transporter.sendMail({
from: `"IBAS Error Warning" <${process.env.sender}>`, // sender address
to: process.env.receiverOfError, // list of receivers
subject: "Error Warning", // Subject line
text: "Error", // plain text body
html:"<p>Error on study with id " + reportId + "</p>"
});
// send mail with defined transport object
// transporter.sendMail(mailOptions, function(error, info){
// if(error){
// return console.log(error);
// }
// console.log('Message sent: ' + info.response);
// });
// console.log("this file does not exist!");
}
async function notificationEmail () {
// create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
host: 'smtp-pt.securemail.pro',
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: process.env.sender, // generated ethereal user
pass: process.env.emailPassword // generated ethereal password
},
tls: {
rejectUnauthorized: false,
},
});
// send mail with defined transport object
let info = await transporter.sendMail({
from: `"IBAS" <${process.env.sender}>`, // sender address
to: 'xxxx#xxx.com', // list of receivers
subject: "O seu estudo está concluído", // Subject line
// text: "Error", // plain text body
html:"<p>O seu estudo está concluído e encontra-se na sua área de cliente. Aceda a <a href='url'>www.ibasim.com/perfil</a> para visualizar.</p>"
});
// send mail with defined transport object
// transporter.sendMail(mailOptions, function(error, info){
// if(error){
// return console.log(error);
// }
// console.log('Message sent: ' + info.response);
// });
// console.log("this file does not exist!");
}
async function startDrawing() {
console.log("start drawing");
//add the condition, if user has that extra
let revit = exec('C:/Program Files/Autodesk/Revit 2020/Revit.exe', (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
})
setTimeout(function(){
console.log("counting down for revit!")
revit.kill();
}, 120 * 1000);
}
The excel restarts while startDrawing() is still active (the revit file is not closed yet) and after it never does close, only if there's no queue.
Can someone please help on how to adjust this process to make sure the queue only gets a new element after the revit.kill process happens (and the exe effectively is closed)?
Thanks in advance for your help.

Related

Get and return value outside function - Nodejs

I'm working on some method that have a callback with value, that I need to return and response, but value is null outside callback funciton
How can I solve?
async function createChannel(req) {
let user_id = req.query.user_id;
let chat_name = req.query.chat_name;
var chimesdkmessaging = new AWS.ChimeSDKMessaging({region: region});
var channel = null;
try {
let dateNow = new Date();
const params = {
Name: chat_name,
AppInstanceArn: config.aws_chime_app_istance,
ClientRequestToken: dateNow.getHours().toString() + dateNow.getMinutes().toString(),
ChimeBearer: await AppInstanceUserArn(user_id),
AppInstanceUserArn of the user making the API call.
Mode: 'RESTRICTED',
Privacy: 'PRIVATE'
};
chimesdkmessaging.createChannel(params, function (err, data) {
if (err) {
console.log(err, err.stack);
} // an error occurred
else {
console.log(data); // successful response
console.log('Assegno il canale: ' + data.ChannelArn);
channel = data.ChannelArn; // <----- VALUE I WANT TO RETURN OUTSIDE
}
});
} catch (e) {
return response({error: e}, 500);
}
return response({data: channel},200); // <---- but this channel is null
}
wrap with Promise
let user_id = req.query.user_id;
let chat_name = req.query.chat_name;
var chimesdkmessaging = new AWS.ChimeSDKMessaging({region: region});
let channel = null;
try {
let dateNow = new Date();
const params = {
Name: chat_name,
AppInstanceArn: config.aws_chime_app_istance,
ClientRequestToken: dateNow.getHours().toString() + dateNow.getMinutes().toString(),
ChimeBearer: await AppInstanceUserArn(user_id),
AppInstanceUserArn of the user making the API call.
Mode: 'RESTRICTED',
Privacy: 'PRIVATE'
};
channel = await new Promise((resolve,reject)=>{
chimesdkmessaging.createChannel(params, function (err, data) {
if (err) {
console.log(err, err.stack);
reject(err)
} // an error occurred
else {
console.log(data); // successful response
console.log('Assegno il canale: ' + data.ChannelArn);
resolve(data.ChannelArn)// <----- VALUE I WANT TO RETURN OUTSIDE
}
});
})
} catch (e) {
return response({error: e}, 500);
}
return response({data: channel},200); // <---- but this channel is null
}

WebSocket and multiple Dyno's on Heroku Node.js app Using Redis

I'm building an App deployed to Heroku which uses WebSocket and Redis.
The WebSocket connection is working properly when I use only 1 dyno, but when I scale to 2, then I send event my application do it twice.
const ws = require('ws')
const jwt = require('jsonwebtoken')
const redis = require('redis')
const User = require('../models/user')
function verifyClient (info, callback) {
let token = info.req.headers['sec-websocket-protocol']
if (!token) { callback(false, 401, 'Unauthorized') } else {
jwt.verify(token, Config.APP_SECRET, (err, decoded) => {
if (err) { callback(false, 401, 'Unauthorized') } else {
if (info.req.headers.gameId) { info.req.gameId = info.req.headers.gameId }
info.req.userId = decoded.aud
callback(true)
}
})
}
};
let websocketServer, pub, sub
let clients = {}
let namespaces = {}
exports.initialize = function (httpServer) {
websocketServer = new ws.Server({
server: httpServer,
verifyClient: verifyClient
})
pub = redis.createClient(Config.REDIS_URL, { no_ready_check: true, detect_buffers: true })
pub.auth(Config.REDIS_PASSWORD, function (err) {
if (err) throw err
})
sub = redis.createClient(Config.REDIS_URL, { no_ready_check: true, detect_buffers: true })
sub.auth(Config.REDIS_PASSWORD, function (err) {
if (err) throw err
})
function handleConnection (socket) {
// socket.send(socket.upgradeReq.userId);
socket.userId = socket.upgradeReq.userId // get the user id parsed from the decoded JWT in the middleware
socket.isAlive = true
socket.scope = socket.upgradeReq.url.split('/')[1] // url = "/scope/whatever" => ["", "scope", "whatever"]
console.log('New connection: ' + socket.userId + ', scope: ' + socket.scope)
socket.on('message', (data, flags) => { handleIncomingMessage(socket, data, flags) })
socket.once('close', (code, reason) => { handleClosedConnection(socket, code, reason) })
socket.on('pong', heartbeat)
if (socket.scope === 'gameplay') {
try {
User.findByIdAndUpdate(socket.userId, { $set: { isOnLine: 2, lastSeen: Date.now() } }).select('id').lean()
let key = [socket.userId, socket.scope].join(':')
clients[key] = socket
sub.psubscribe(['dispatch', '*', socket.userId, socket.scope].join(':'))
} catch (e) { console.log(e) }
} else {
console.log('Scope : ' + socket.scope)
}
console.log('Connected Users : ' + Object.keys(clients))
}
function handleIncomingMessage (socket, message, flags) {
let scope = socket.scope
let userId = socket.userId
let channel = ['dispatch', 'in', userId, scope].join(':')
pub.publish(channel, message)
}
function handleClosedConnection (socket, code, reason) {
console.log('Connection with ' + socket.userId + ' closed. Code: ' + code)
if (socket.scope === 'gameplay') {
try {
User.findByIdAndUpdate(socket.userId, { $set: { isOnLine: 1 } }).select('id').lean()
let key = [socket.userId, socket.scope].join(':')
delete clients[key]
} catch (e) {
console.log(e)
}
} else {
console.log('Scope : ' + socket.scope)
}
}
function heartbeat (socket) {
socket.isAlive = true
}
sub.on('pmessage', (pattern, channel, message) => {
let channelComponents = channel.split(':')
let dir = channelComponents[1]
let userId = channelComponents[2]
let scope = channelComponents[3]
if (dir === 'in') {
try {
let handlers = namespaces[scope] || []
if (handlers.length) {
handlers.forEach(h => {
h(userId, message)
})
}
} catch (e) {
console.log(e)
}
} else if (dir === 'out') {
try {
let key = [userId, scope].join(':')
if (clients[key]) { clients[key].send(message) }
} catch (e) {
console.log(e)
}
}
// otherwise ignore
})
websocketServer.on('connection', handleConnection)
}
exports.on = function (scope, callback) {
if (!namespaces[scope]) { namespaces[scope] = [callback] } else { namespaces[scope].push(callback) }
}
exports.send = function (userId, scope, data) {
let channel = ['dispatch', 'out', userId, scope].join(':')
if (typeof (data) === 'object') { data = JSON.stringify(data) } else if (typeof (data) !== 'string') { throw new Error('DispatcherError: Cannot send this type of message ' + typeof (data)) }
pub.publish(channel, data)
}
exports.clients = clients
This is working on localhost.
Please let me know if I need to provide more info or code.Any help on this would be greatly appreciated, thanks in advance!
You have alot of extraneous info in the code you posted, so it's difficult to understand exactly what you mean.
However, if I understand correctly, you currently have multiple worker dyno instances subscribing to the same channels in some kind of pub/sub network. If you don't want all dynos to subscribe to the same channels, you need to put some logic in to make sure that your channels get distributed across dynos.
One simple way to do that might be to use something like the logic described in this answer.
In your case you might be able to use socket.userId as the key to distribute your channels across dynos.

HTTPS Request Inside app.post Doesn't work

I got problem where
https.get(urlDosen,res => {......});
not working as you can see in here:
function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
// ignore non-text-message event
return Promise.resolve(null);
}
const b = String(event.message.text);
if(b.substring(0,5)=='dosen'){
const namaDosen = b.substring(6);
const urlDosen = url+namaDosen;
https.get(urlDosen,res => {
console.log(res.headers['content-type']);
The code goes like this:
app.post('/callback', line.middleware(config), (req, res) => {
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result))
.catch((err) => {
console.error(err);
res.status(500).end();
});
});
// event handler
function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
// ignore non-text-message event
return Promise.resolve(null);
}
const b = String(event.message.text);
if(b.substring(0,5)=='dosen'){
const namaDosen = b.substring(6);
const urlDosen = url+namaDosen;
https.get(urlDosen,res => {
console.log(res.headers['content-type']);
if(res.headers['content-type']=='application/json; charset=UTF-8'){
res.setEncoding('utf8');
let body = '';
res.on('data', data=>{
body += data;
});
res.on('end', ()=>{
body = JSON.parse(body);
if(body['hasil']=='sukses'){
const echo = {type:'text',text: 'Nama Dosen: ' + body['nama'] + ' Status: ' + body['status']};
return client.replyMessage(event.replyToken, echo);}
else{
const echo ={type:'text',text:body['status']};
return client.replyMessage(event.replyToken, echo);
}
}
);
} else{
const hasil={type:'text',text:'Mohon mengulang kembali'};
return client.replyMessage(event.replyToken, hasil);
// message.channel.send("Mohon mengulang kembali");
}
});
} else{
// create a echoing text message
const echo = { type: 'text', text: 'salahnya dimana?' };
// use reply API
return client.replyMessage(event.replyToken, echo);
}
}
I'm using node.js. Thanks

WebRTC video only works when computers share same connection

So I have set up a web RTC connection, nearly fully understand the steps and mechanisms involved and have a test out on the web with a full SSL certificate (so I can use it on chrome) but It doesn't ever seem to work with computers that are not under the same wireless connection/router.
In my test I have logged that there are ICE candidates generated and that the two browsers are communicating but I get no video feed except for when the two trying to communicate are under the same connection.
Browser Side to set up webRTC:
var socket = io.connect();
var isChannelReady;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pc_config = {'iceServers': [{'url': 'stun:stun.l.google.com:19302'}]};
var pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': true}]};
// Set up audio and video regardless of what devices are present.
// var sdpConstraints = {'mandatory': {
// 'OfferToReceiveAudio':true,
// 'OfferToReceiveVideo':true }};
var sdpConstraints = {
optional: [],
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true
}
};
function sendMessage(message){
console.log('Client sending message: ', message);
// if (typeof message === 'object') {
// message = JSON.stringify(message);
// }
socket.emit('message', message);
}
socket.on('message', function (message){
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message), function(){doAnswer()});
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');
function handleUserMedia(stream) {
console.log('Adding local stream.');
console.log(localVideo);
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
function handleUserMediaError(error){
console.log('getUserMedia error: ', error);
}
// window.onload = function () {
// var localVideo = document.getElementById('localVideo');
// var remoteVideo = document.getElementById('mehh');
// }
var constraints = {video: true};
navigator.getUserMedia(constraints, handleUserMedia, handleUserMediaError);
console.log('Getting user media with constraints', constraints);
if (location.hostname != "localhost") {
requestTurn('https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913');
}
function maybeStart() {
if (!isStarted && typeof localStream != 'undefined' && isChannelReady) {
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function(e){
sendMessage('bye');
}
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(null);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('handleIceCandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate});
} else {
console.log('End of candidates.');
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event){
console.log('createOffer() error: ', e);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer(setLocalAndSendMessage, function(err){if(err) throw err;}, sdpConstraints);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message' , sessionDescription);
sendMessage(sessionDescription);
}
function requestTurn(turn_url) {
var turnExists = false;
for (var i in pc_config.iceServers) {
if (pc_config.iceServers[i].url.substr(0, 5) === 'turn:') {
turnExists = true;
turnReady = true;
break;
}
}
if (!turnExists) {
console.log('Getting TURN server from ', turn_url);
// No TURN server. Get one from computeengineondemand.appspot.com:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200) {
var turnServer = JSON.parse(xhr.responseText);
console.log('Got TURN server: ', turnServer);
pc_config.iceServers.push({
'url': 'turn:' + turnServer.username + '#' + turnServer.turn,
'credential': turnServer.password
});
turnReady = true;
}
};
xhr.open('GET', turn_url, true);
xhr.send();
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
// console.log('Session terminated.');
// stop();
// isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null || mLineIndex === undefined) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
console.log(sdpLines[mLineIndex])
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length-1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}
/////////////////////////////////////////////
room = prompt("Enter room name:");
var socket = io.connect();
if (room !== '') {
console.log('Create or join room', room);
socket.emit('create or join', room);
}
socket.on('created', function (room){
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function (room){
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function (room){
console.log('This peer has joined room ' + room);
isChannelReady = true;
});
socket.on('log', function (array){
console.log.apply(console, array);
});
////////////////////////////////////////////////
And here is everything on the server side pertaining to webRTC (using socket io):
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket){
console.log('connected');
// convenience function to log server messages on the client
function log(){
var array = [">>> Message from server: "];
for (var i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', function (message) {
log('Got message:', message);
// for a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', function (room) {
//io.sockets.clients(room)
//io.of('/').in(room).clients
//io.sockets.adapter.rooms[room]
var numClients;
var ish = io.sockets.adapter.rooms[room];
(ish === undefined) ? numClients = 0 : numClients = ish.length
log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);
if (numClients === 0){
socket.join(room);
socket.emit('created', room);
} else if (numClients === 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room);
} else { // max two clients
socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);
});
});
I simply can't find any errors that prevent the streams from being sent...
More than willing to do a private chat so I can get moving on my project.

wit.ai : sessionid undefined in my runActions function

I am writing my first apps with wit.ai using a node.js backend. I found some posts here similar to my question, but not really the answer :
I use a socket.io to communicate with my node script.
The two relevant parts of my node are :
io.sockets.on('connection', function (socket) {
socket.on('message',
function(data) {
var json = JSON.parse(data);
var sid = json['sessionid'];
console.log("Received on sid " + sid);
if (_sessions[sid] == undefined) {
_sessions[sid] = {};
_sessions[sid].context = {};
}
_sessions[sid].socket = socket;
client.runActions(sid, json['text'], _sessions[sid].context, 30)
.then((context) => {
_sessions[sid].context = context;
}
)
.catch((err) =>
{
console.error('Oops! Got an error from Wit: ', err.stack || err);
}
);
}
);
}
);
========
const actions = {
send(request, response) {
const {sessionId, context, entities} = request;
const {text, quickreplies} = response;
return new Promise(function(resolve, reject) {
var session = _sessions[sessionId];
console.log("-------------------------------");
console.dir(context);
console.log("-------------------------------");
session.socket.emit("message", JSON.stringify(response));
return resolve();
});
},
gettaxi ({sessionid, context, text, entities}) {
return new Promise(function(resolve, reject) {
console.log(`Session ${sessionid} received ${text}`);
var quand = firstEntityValue(entities, "quand");
if (!quand && context['quand'] != undefined) quand = context['quand'];
var depart = firstEntityValue(entities, "depart");
var arrivee = firstEntityValue(entities, "arrivee");
if (depart) {
console.log("Found depart");
context.depart = depart;
delete context.missing_depart;
}
else {
context.missing_depart = true;
}
if (arrivee) {
console.log("Found arrivee");
context.arrivee = arrivee;
delete context.missing_arrivee;
}
else {
context.missing_arrivee = true;
}
console.dir(context);
if (quand) {
console.log("Found quand");
context.quand = quand;
delete context.missing_quand;
}
else {
context.missing_quand = true;
}
return resolve(context);
}
);
},
};
All is working rather good, except than my gettaxi receives a undefined sessionid.
It's not the case for the send function that receives the correct sessionid.
What am I doing wrong ?

Resources