I'm using node.js for the first time with a simple website to control a motor.
On the website are 2 buttons: "Left" and "Right". You have to press and hold the "Left" or "Right" button to turn the motor. When you let the button go the motor stops.
The motor is connected to an Arduino on the serialport on the server.
It works, but
Problem
When I use it on my phone and spam the buttons the server sometimes stops responding for everyone until I restart the server. I thought the functions were called too fast because of the WiFi latency. So I tried calling the functions in the Chrome console.
LeftPressed();LeftReleased();LeftPressed();LeftReleased();
And indeed, after running them a few times the server stops responding.
No error, no crash..
When I remove motorPort.write(message); keeps the server responding.
How can I prevent the server from stopping to respond?
Client (Javascript in browser)
function LeftPressed() {
socket.emit('steer', { left: true });
}
function LeftReleased() {
socket.emit('steer', { left: false });
}
function RightPressed() {
socket.emit('steer', { right: true });
}
function RightReleased() {
socket.emit('steer', { right: false });
}
Server (node.js)
const SerialPort = require("serialport");
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server)
const motorPort = new SerialPort('/dev/arduinoUnoMicroUSB', {
baudrate: 9600,
});
app.use(express.static('web'));
server.listen(8081, function () {
console.log("Server running...");
});
io.on('connection', function (socket) {
socket.on('steer', function (data) {
var message = "2"; //stop
if (data.left != undefined) {
console.log("Left: " + data.left); //log to Console (below)
if (data.left) {
message = "1" //turn left
}
}
if (data.right != undefined) {
console.log("Right: " + data.right);
if (data.right) {
message = "3"; //turn right
}
}
motorPort.write(message); //send message over serial port
});
});
Console output example: "Left" button clicked
Server running...
Left: true //pressed (motor turning left)
Left: false //released (motor stops)
I got it working with this. It's not 100% perfect, but at least the server keeps responding. If you think there is a better way, I am always open for improvements.
const SerialPort = require("serialport");
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const motorPort = new SerialPort('/dev/arduinoUnoMicroUSB', {
baudrate: 9600,
});
app.use(express.static('web'));
server.listen(8081, function () {
console.log("Server running...");
});
var ready = true;
var NotSendMessages = [];
io.on('connection', function (socket) {
socket.on('steer', function (data) {
var message = "2"; //stop
if (data.left != undefined) {
console.log("Left: " + data.left);
if (data.left) {
message = "1" //turn left
}
}
if (data.right != undefined) {
console.log("Right: " + data.right);
if (data.right) {
message = "3"; //turn right
}
}
if (!ready) {
console.log("cats!!!");
NotSendMessages.push(message);
} else {
send(message);
}
});
});
function send(message) {
motorPort.write(message, function () {
ready = false;
motorPort.drain(function () {
motorPort.flush(function () {
ready = true;
});
});
});
}
setInterval(function () {
if (NotSendMessages.length > 0 && ready) {
send(NotSendMessages.shift());
}
}, 50);
Related
How to call different module for different socket path?
Here is server code:
require('dotenv-flow').config();
const express = require('express');
const app = express();
const http =require('http');
const httpServer= http.createServer(app);
const io = require('socket.io')(httpServer);
let C=require('./C');
let c=new C(io.of("/c"));
class C {
constructor(socket) {
socket.on("connection",()=>{
console.log("Connection to /c");
});
socket.on("hi", (peerName, calllBack) => {
console.log("Received say hi from " + peerName + ".");
});
}
}
module.exports = C;
The client side code:
import { useEffect} from "react";
import io from "socket.io-client";
export default function TestSocket() {
useEffect(() => {
let peerName;
let signalServerURL=process.env.REACT_APP_SOCKET_URL + "c";
let sUsrAg = navigator.userAgent;
if (sUsrAg.indexOf("Edg") > -1) {
peerName = "Edge";
} else {
if (sUsrAg.indexOf("Chrome") > -1) {
peerName = "Chrome";
} else {
if (sUsrAg.indexOf("Firefox") > -1) {
peerName = "Firefox";
} else {
if (sUsrAg.indexOf("Safari") > -1) {
peerName = "Safari";
}
}
}
}
let socket = io(signalServerURL, {
transports: ["websocket"],
});
socket.emit("hi", peerName, (response) => {
console.log(response);
});
},[]);
return (
<></>
)
}
The module C can receive "connection" event, however, it cannot receive the "hi" event, why?
Besides that, I want to use different module/class to handle different socket path.
e.g.
for the path /b socket access, I want to use the module B to handle it.
for the path /d socket access, I want to use the module D to handle it.
How can I implement it?
According to the Philippe answer, I modified the server code as below:
require('dotenv-flow').config();
const express = require('express');
const app = express();
const http =require('http');
const httpServer= http.createServer(app);
const io = require('socket.io')(httpServer);
let C=require('./C');
let c=new C(io,"/c");
class C {
constructor(io,path) {
io.of(path).on("connection",socket=>{
console.log("Connection to "+path);
socket.on("hi", (peerName, calllBack) => {
console.log("Received say hi from " + peerName + ".");
});
});
}
}
module.exports = C;
It works!
When a connection is successful, socket-io returns the socket you need to use. You can try something like:
constructor(io) {
io.on("connection", (socket) => {
console.log("Connection to /c " + socket.id);
socket.on("hi", (peerName, calllBack) => {
console.log("Received say hi from " + peerName + ".");
});
});
}
I have got a node server running locally and setting up a Socket.IO instance.
const http = require('http');
const socket = require('socket.io');
const path = require('path');
class Controller
{
constructor() {
this.localURL = path.resolve(process.cwd() + '/themes/');
this.theme = null;
const server = http.createServer();
this.io = new socket.Server(server, {
transports: ['websocket'],
});
this.io.on("connection", socket => {
// Wait for the client to send the website theme
socket.on('init', theme => {
// Inform current running client that the server is changing projects.
if (this.theme && this.theme !== theme) {
socket.emit(`message-${this.theme}`, {
type: 'message',
message: `Project changed to ${theme}, stopping ${this.theme}.`
});
return;
}
// Set the theme
this.theme = theme;
});
});
server.listen(8080);
}
}
new Controller();
Then on my website I have got a Vue component, but sometimes I could have 2 of the components, so I wanted to emit messages to BOTH of these component's from my server, I will handle accepting the messages in either Vue Instance myself.
This was working, all of a sudden it's not now, not too sure what I changed.
import { io } from 'socket.io-client';
export default {
props: [ 'code' ],
mounted: function () {
this.socket = io('ws://localhost:8080', {
forceNew: true,
timeout: 10000,
transports: ['websocket']
});
this.socket.on('connect', () => {
this.connected = true;
});
this.socket.on('disconnect', () => {
this.connected = false;
this.initiated = false;
});
this.socket.on(`stop-${this.code}`, () => {
this.started = '';
});
this.socket.on(`message-${this.code}`, message => {
console.log(message);
message.time = 'N/A';
this.messages.unshift(message);
})
this.socket.onAny((event, data) => {
if (event.indexOf(this.code) > -1) {
return;
}
event = event.replace(`-${this.code}`, '');
this[event] = data;
});
},
methods: {
initiate() {
this.messages = [];
this.socket.emit('init', this.code);
this.socket.on('started', code => {
if (code !== this.code) {
console.log('Themes don\'t match...');
this.initiated = false;
return;
}
So initially I would run initiate on one of the components, this sends some the theme name to the server and the server stores the theme in a property. Then I would run initiate on the second component, which would send a different theme, so this should hit the this.theme && this.theme !== theme if, and send a message back to the initial theme.
This message is being sent and the event names are as expected, but nothing comes through on the component.
I'm unable to set the themeSource of my electron app. Platform is Windows 8.1.
const electron = require('electron');
const app = electron.app;
if (app) app.on('ready', function() {
nativeTheme = electron.nativeTheme;
nativeTheme.themeSource = 'dark';
});
This produces an error in a modal pop-up alert saying nativeTheme is undefined.
I'm definitely doing something wrong, but for the life of me I can't see it.
Everything else I'm doing in Electron works like a charm.
Here's my entire app.js:
// server-side jquery
const jsdom = require('jsdom');
const jquery = require('jquery');
const { JSDOM } = jsdom;
const dom = new JSDOM('<!DOCTYPE html>');
const $ = jquery(dom.window);
global.jq = $;
// for priming the webapp
const request = require('request');
// electron config stuff
var backgroundColor = '#1A1A1A';
var width = 800, height = 600;
// get electron
const electron = require('electron');
// prime electron app
const app = electron.app;
// flags: don't enter GUI launch until both sails & electron report ready
var electronIsReady = false;
var sailsIsReady = false;
var gruntIsReady = false;
// block repeat launches (edge contingency)
var windowIsLaunching = false;
var splashIsUp = false;
var splashResponse = 'pong';
// electron window(s)
var mainWindow = null;
var splashWindow = null;
// delay after all preflight checks pass
var windowCreationDelay = 0;
// sails app address
const appAddress = 'http://127.0.0.1';
const appPort = 1337;
const BrowserWindow = electron.BrowserWindow;
if (app) app.on('ready', tryLaunchingForElectron);
else electronIsReady = true;
function tryLaunchingForSails() {
sailsIsReady = true;
try {
// "prime" the webapp by requesting content early
request(`${appAddress}:${appPort}`, (error,response,body) => {/*nada*/});
if (app && electronIsReady && gruntIsReady) createWindow();
}
catch (e) { console.error(e); }
}
function tryLaunchingForElectron() {
// try to prevent multiple instances of the app running
app.requestSingleInstanceLock();
electronIsReady = true;
if (!splashIsUp) {
splashIsUp = true;
// show splash screen
splashWindow = new BrowserWindow({
width: width, height: height,
transparent: true, frame: false, alwaysOnTop: true,
focusable: false, fullscreenable: false,
webPreferences: { nodeIntegration: true }
});
splashWindow.loadURL(`file://${__dirname}/splash.html`);
}
// enter UI phase if sails is also ready
if (app && sailsIsReady && gruntIsReady) createWindow();
}
function createWindow() {
if (windowIsLaunching === true) return -1;
windowIsLaunching = true;
// optional: give sails time to get it fully together
setTimeout(function() {
try {
// tell the splash page to close
splashResponse = 'close';
// create main window
mainWindow = new BrowserWindow({show: false, width: width, height: height,
backgroundColor: backgroundColor
});
// hide menu bar where available
mainWindow.setMenuBarVisibility(false);
// maximize the window
mainWindow.maximize();
// bring to the front
mainWindow.focus();
// go to the sails app
mainWindow.loadURL(`${appAddress}:${appPort}/`);
// show javascript & DOM consoles
mainWindow.webContents.openDevTools();
// show browser only when it's ready to render itself
mainWindow.once('ready-to-show', function() {
// get the splash out of the way
splashWindow.setAlwaysOnTop(false);
// show the main window
mainWindow.setAlwaysOnTop(true);
mainWindow.show();
mainWindow.setAlwaysOnTop(false);
app.focus();
});
// setup close function
mainWindow.on('closed', function() {
mainWindow = null;
});
}
catch (e) { console.error(e); }
}, windowCreationDelay);
}
// tell the splash window when it's time to hide & close
if (app) app.on('ready', function() {
var ipcMain = electron.ipcMain;
ipcMain.on('splashPing', (event, arg) => {
try {
event.sender.send('splashPing', splashResponse);
} catch (e) { console.log(e); }
if (splashResponse === 'close') {
//splashWindow = null;
ipcMain.removeAllListeners('splashPing');
}
// console.log(`${arg}||${splashResponse}`);
});
});
// quit when all windows are closed
if (app) app.on('window-all-closed', function() {
if (process.platform !== 'darwin') {
sails.lower(function (err) {
if (err) {
console.log(err);
app.exit(1);
} else
app.quit();
setTimeout(()=>{app.quit();},5000);
});
}
});
// probably for mobile
if (app) app.on('activate', function() {
if (mainWindow === null) {
createWindow();
}
})
if (app) app.on('ready', function() {
nativeTheme = electron.nativeTheme;
nativeTheme.themeSource = 'dark';
});
// sails wants this
process.chdir(__dirname);
// import sails & rc
var sails;
var rc;
try {
sails = require('sails');
sails.on('hook:grunt:done', () => {
gruntIsReady = true;
tryLaunchingForSails();
});
rc = require('sails/accessible/rc');
} catch (err) {
console.error(err);
}
// Start server
try {
sails.lift(rc('sails'), tryLaunchingForSails );
}
catch (e) { console.log(e); }
nativeTheme = electron.nativeTheme;
This is the problem. You need to do:
const nativeTheme = electron.nativeTheme;
Although in this case there's no need for the extra variable - just do electron.nativeTheme.themeSource = 'dark';.
I strongly suggest you use Typescript - it would tell you this:
Edit: I'm sure I mentioned this in the comments but it seems to have been removed somehow: You also need to make sure you are using Electron 7.0.0 or greater - nativeTheme was not available before that.
I am trying to create an application using Electron (formerly Atom Shell). This application wraps an AngularJS application and interacts with endpoints created in nodejs to edit and save the HTML content. I am able to create the application with no issues.
When I try to access "/saveContent" from electron causes close button (Windows close on top right corner) to become unresponsive, however minimize and maximize works fine without issue. If I access any other endpoint through electron this issue doesn't come up. I have tried with both sync file write and otherwise too. So I assume "/saveContent" in main.js is cause of the issue.
If I end node.exe in "Windows task Manager" this closes the whole application.
I have the main process code below
'use strict';
var fs = require("fs");
const util = require('util')
var cheerio = require("cheerio");
var express = require('express');
var exapp = express();
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({extended: false});
exapp.use(bodyParser.json());
const electron = require('electron');
const app = electron.app; // Module to control application life.
const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
const path = require('path')
const url = require('url')
var mainWindow = null;
app.on('ready', function() {
mainWindow = new BrowserWindow({width: 1280, height: 900, title: "2018JL", "nodeIntegration":false});
//mainWindow.loadURL(__dirname + '/app/index.html');
mainWindow.loadURL('http://localhost:5001/');
mainWindow.on('closed', function() {
mainWindow = null;
});
});
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
app.quit();
}
});
exapp.get('/editPage', function(req,res){
if(req){
//console.log("req.query.editURL "+ req.query.editURL);
var url = req.query.editURL;
var editURL = path.join(__dirname + '/app/views'+ url+".html");
fs.exists(editURL, function(fileok){
if(fileok){
fs.readFile(editURL, 'utf8', function (err, data) {
if (err) {
console.log("error.... "+ err);
return console.error(err);
}
//console.log("data "+ editURL);
res.send(JSON.stringify({path:editURL, content:data}));
});
}else{
console.log("file not found");
}
});
}
});
exapp.post('/saveContent', function (req, res) {
//console.log(util.inspect(req, false, null))
if (req) {
//console.log(req.query.url + " ------ " + req.query.content);
var $ = cheerio.load(req.query.content);
var htmlContent = $('body').children();
console.log('htmlContent '+htmlContent);
fs.writeFile(req.query.url, htmlContent, function(err) {
if (err) {
res.send("Error");
}
console.log("End of write file");
res.send("success");
});
}
console.log("End of function .....");
});
exapp.get('/test', function (req, res) {
res.send("Test success ");
});
exapp.use(express.static(__dirname + '/app'));
exapp.listen(process.env.PORT || 5001);
Client code below
$scope.editPage = function () {
$http({method: "GET",
url: "/editPage",
params: {editURL: $location.path()}
}).then(function success(response) {
//var result = JSON.parse(response.data);
//console.log("HTTP Success "+response.data.path);
$scope.showEditor = true;
$scope.editURL = response.data.path;
tinymce.get('contentEditor').setContent(response.data.content);
}, function error(response) {
console.log("HTTP Error " + response.statusText);
});
};
Commenting file write code in '/saveContent' doesn't cause electron close button to become unresponsive.
I replace the code for mainWindow close to as below and works fine
mainWindow.on('close', function(e) {
e.preventDefault();
mainWindow.destroy();
});
https://github.com/electron/electron/blob/master/docs/api/browser-window.md#windestroy
I am trying to put together a very simply multiplayer tic-tac-toe game in swift with a NodeJS backend. When I try and do socket.emit() from my swift client, however, the server does not recognize it.
Client Code:
SocketIOManager:
import UIKit
class SocketIOManager: NSObject {
static let sharedInstance = SocketIOManager()
override init() {
super.init()
}
var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: "http://10.0.1.30:2000")! as URL)
func connectToServer(completionHandler: #escaping (_ userList: [[String: AnyObject]]?) -> Void) {
socket.emit("connectUser")
socket.on("userList") { ( dataArray, ack) -> Void in
completionHandler(_: dataArray[0] as? [[String: AnyObject]])
}
}
func establishConnection() {
socket.connect()
}
func closeConnection() {
socket.disconnect()
}
}
Game Scene:
import SpriteKit
class GameScene: SKScene {
let screenSize = UIScreen.main.bounds
var board = SKSpriteNode(imageNamed: "Board.png")
var users = [[String: AnyObject]]()
override func didMove(to view: SKView) {
SocketIOManager.sharedInstance.connectToServer(completionHandler: { (userList) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
if userList != nil {
self.users = userList!
}
})
})
board.size = CGSize(width: screenSize.width * 2/3, height: screenSize.width * 2/3)
board.position = CGPoint(x: screenSize.width/2, y: screenSize.height/2)
self.backgroundColor = UIColor.white
self.addChild(board)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}
Server Code:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var userList = [];
app.get('/', function(req, res){
res.send('<h1>Tic-Tac-Toe Server</h1>');
});
http.listen(2000, function(){
console.log('Listening on *:2000');
});
io.on('connection', function(clientSocket) {
console.log('a user connected');
clientSocket.on('disconnect', function() {
console.log('user disconnected');
});
clientSocket.on('connectUser', function() {
console.log('User with id ' + clientSocket.id + ' connected');
var userInfo = {};
var foundUser = false;
for (var i = 0; i < userList.length; i++) {
if (userList[i]["id"] == clientSocket.id) {
userInfo = userList[i];
foundUser = true;
break;
}
}
if (!foundUser) {
userInfo["id"] = clientSocket.id;
userList.push(userInfo);
}
io.emit("userList", userList);
io.emit("userConnectUpdate", userInfo);
});
});
The message in particular that is not working is the "connectUser" one, but I have tried to create others to test it and none of them work. It appears as if the server never receives them.
Since you are not using a https (http://10.0.1.30:2000), probably you forgot to set Allow Arbitrary Loads to YES in your Info.plist file at your iOS project:
Anyways I've built a basic example that's working:
Server side:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 5000;
io.on('connection', function(socket) {
socket.on('message', function(data) {
console.log('client sent a message: ' + data);
});
});
http.listen(port, function() {
console.log('server up and running at %s port', port);
});
Client side:
import UIKit
import SocketIO
class ViewController: UIViewController {
let socket = SocketIOClient(socketURL: URL(string: "http://localhost:5000")!)
override func viewDidLoad() {
super.viewDidLoad()
socket.on("connect") { data, ack in
print("socket connected")
self.socket.emit("message", "Hello dear server from iOS.")
}
socket.connect()
}
}
Logs from Server