How can I do this thing with a JS app in a browser?

I'm TRYING to use JavaScript to develop an app for prototypically accurate control of model trains, using JMRI (Java Model Railroad Interface).

JMRI is on a computer connected to my model train layout, and basically makes it easy to do things with computers and model trains.

Now, here's what I want to do.
You probably know model trains are (basically, there's some digital voodoo magic involved now that allows you to run multiple trains on a single electrical circuit, but that's besides the point) controlled by a single speed slider and a direction switch. Real diesel locomotives are not controlled by a single speed slider. I could explain how they are controlled but it's not necessary for this so if you're wondering Google it :P

ANYWAY, I'm trying to use JMRI and some JavaScript to control a model train engine realistically through a web browser. I want to have a web interface that mimics an actual locomotive cab control setup, with a separate lever for braking, notching the throttle, the reverser, etc. I don't need help building the interface, I need help getting it all to communicate with JMRI on the PC connected to the layout.

JMRI has a page explaining their ways of doing this, but I don't completely understand how to send commands to this console using JavaScript.

http://jmri.sourceforge.net/help/en/html/web/JsonServlet.shtml

The "JMRI Web Server" program it mentions here is running on my layout PC and is accessible at http://jmri.mylocaldomainhere:12080/ if anyone needs that info for helping me.

How would I get JavaScript to send a command to the WebSockets server?

Thanks, any and all help is appreciated, if you need any additional info about how I've got things set up let me know. Like I said I don't need help designing the whole program, I basically need someone to tell me how to send a command to the websockets server so I can do something like this with a function:

websocketscmd(command)

and it'll execute it on the layout PC and return the result

TL;DR How do I execute one of these commands with JavaScript?

I was wondering about that way of doing it but I couldn't figure out how to get it to make the server do what I want. For example the little table of commands has this:

{"type":"throttle","data":{"throttle":"CSX754","speed":0.25}}

which supposedly sets the speed of CSX754 to 25%. How would I do that with the GET/REST way?

Sorry for the noobish questions lol

When I go to that URL, it gives me a page with this on it, with no styling or anything else. Just this text:

{"type":"error","data":{"code":404,"message":"Unknown object type {\"type\":\"throttle\",\"data\":{\"throttle\":\"CSX754\",\"speed\":0.25}} was requested."}}

What web server would i run that script on? the same one JMRI's webgui is running on?

I have a couple of web servers here already, I was just wondering where that was meant to be run and if its location sensitive at all.

So to run it on another server, would I just change "/json" to "ws://jmri.my domain:12080/json"

FYI 12080 is the port the JMRI web server runs on, I could change it if that's going to cause huge problems but that's the default so I left it there.

I'll try that script in it's own HTML file tomorrow and see what happens.

You can try the following to send commands:

   var modelname = "CSX754";
   var newThrottle = 0.25;
   var ws = new WebSocket("ws://your-ws-url-here:12080");
   var obj = {
     "type":"throttle",
     "data":{
       "throttle":modelname,
       "speed":newThrottle
     },
   };
   ws.onopen = function() {
     ws.send(JSON.stringify(obj));
   };

As you can see, you need to wait till the WebSocket becomes available to send messages. It won't work otherwise.
If you also want to receive & parse the Server's answer, try the following:

   ws.onmessage = function(event) {
     var data = JSON.parse(ev.data);
     console.dir(data);
   }

The console.dir prints the received data in a nice way using your browser's console. (On chrome: right-click the window -> Inspect elements -> Console)

The documentation of JMRI says it also has an interactive JSON console on /json/. It might come in handy at some point, if you get it working!

Okay, well I think that at least connects okay, because when I run the code I get nothing outputted to Chrome's JS console. If I add the bit to make it log the server's responses to the console, Chrome gives me an error in the JS console:

Uncaught ReferenceError: ev is not defined

Here is the full HTML page I was using to test this. I was using Brackets with live preview if that makes a difference.

<html>
<head>
<title>LocoThrottle-JS</title>
</head>
<body>
<script type="text/javascript">
var modelname = "CSX754";
var jmri_host = "10.10.39.85"
var jmri_port = 12080
   var newThrottle = 0.25;
   var ws = new WebSocket("ws://" + jmri_host + ":" + jmri_port + "/json/");
   var obj = {
     "type":"throttle",
     "data":{
       "throttle":modelname,
       "speed":newThrottle
     },
   };
   ws.onopen = function() {
     ws.send(JSON.stringify(obj));
       console.log = "I added this as a marker so I can see where the code is when its running."
   };
    
     ws.onmessage = function(event) {
     var data = JSON.parse(ev.data);
     console.dir(data);
   }
    
    
    
</script>

    
    
    
    
</body>


</html>

If I take out the bit about making the server's response get logged, I get nothing in the console. No errors, no log entries, nothing. Just blank. So I assume that part works. (I'm pretty sure it's at least connecting okay because I found out the hard way that ws://my-server:12080/json is not the same as ws://my-server:12080/json/ with the trailing slash :P)

Thank you again for your help!

Ah, wow, so apparently I'm a bigger moron than I thought :P I realized what was (I assume) throwing the error involving EV. Did you mean for ev.data to say event.data?

Okay, I got a WebSockets connection going, and was able to contstruct a proof of concept page last night. It's super primitive and ugly HTML but I can connect, disconnect, toggle track power, and notch up/notch down the locomotive engine sound. Thank y'all so much!

Also everyone is right. The JavaScript console in Chrome is a gift from heaven XD

Okay, I've run into a wall. I can't get where I can use the data from the WebSocket reply. I keep trying to get it to save it as a variable, but I can't get it to work right. Is there a way to have a function that's basically:

function sendcommand(command) {
ws.send(command)

//stuff to get reply data from command
reply data = data
return data;
}

Obviously that's pseudo-code, but I imagine you get the idea. Is that possible?

Hi,

I’m also struggling with a similar problem… trying to change JMIR turnouts from a web browser.
?? Did you get your code working? If so, can you share it please?

Thanks!

Tom

I did get this working, but I have to warn you I haven’t touched this code in 3-4 years so my memory of it is a little rusty. JMRI’s websockets api is pretty thorough and functional though.

If you are just trying to manually change turnouts from a web browser, you can use the JMRI web interface. But if you are trying to change turnouts from an app you’re writing in a web browser, you will need the WebSockets API.

Here is JMRI’s docs: https://www.jmri.org/help/en/html/web/JsonServlet.shtml

Here is the app I eventually wrote after asking that question: https://github.com/k4kfh/ZephyrCab

It is worth noting that you cannot write a function that takes a WebSockets message and then returns a WebSockets reply. It’s a very asynchronous communication system. What you need to do is write a function that will handle every possible reply you might get, and that way your program can handle any information it receives from JMRI.