Communication between os and octoprint


#1

if I have a c/c++ app running on the same linux where octoprint is running, any direct way to communicate with it or talking trough http API is the only way (just like I'm on any other system)?


#2

There are countless ways, I'm sure.

Is it a run-and-done sort of CLI app or is it a service?

  • CLI:

    1. For a command line app which can return information, you might want to just invoke it and parse the response.
    2. A long time ago, an executable (of any kind to include batch files) could be created as a CGI interface. Basically, you call it and it returns HTML with a header to mimic how web interfaces now work. For quite some time though, all things were CGI.
  • Service:

    1. expose an HTTP REST API
    2. websockets
    3. named pipes

#3

looks like question was unclear :frowning: as you gave me example how to expose my app to the octoprint, I need to solve the problem other way around, mine console app needs to fetch data from octoprint...

ATTM I'm connecting to localhost:5000 and i fetch data using rest API octoprint is exposing but the question is if there's a "faster" way to talk to octoprint from the same system (some in-process comm, or a file socket or .... dunno) .. I don't have a problem using API but just checking if there's another way to do it too ...

it's yet another gui for octoprint, just one running without xorg, on a 320x240 touch screen lcd attached to the rpi (attm having issues making it run on opi as that's a target board since I don't use rpi's for octoprint at all, but development is on rpi3 till I get the lcd to properly work on opi, having some weird issues with armbian) ... nothing major, just to show basic status info I need per printer and offer pause/resume/cancel function "quickly" .. probbly not very useful to anyone but myself, but to bunch of my printers that has no display I think it will be very useful... as I said the API works ok but if there's a way that's resource optimized...

how does the web app talk to "backend" ? using the same rest api or ?


#4

Oh... alright then. Yeah, I had things backwards there.

You and I are in the same boat, btw. I'm in a skunkworks project to make a 10" display work for a 3D printer.

Likewise, I'm not use X11 or the Desktop (X windows) system. I'm using Kivy, for what it's worth.

To the best of my knowledge, an OctoPrint plugin will be required which then offers some Python-based variables to the Kivy app. The Kivy (Python) app then will be able to display things like the temperature in the interface as well as allowed buttons to be pressed which result in actions within OctoPrint.

Unfortunately there is no easy "Hello, World"—like beginner app to view to see how all this happens. It's a deep dive and no way of getting around that.


#5

:slight_smile: I'm not a python guy so here pure c (maybe c++ we'll see how things go) and MGRX (draw to framebuffer in linux console) + TSLIB (handle touch sensor calibration, readout, normalization, signal conditioning..) ... attm fetching data using rest ... no need for any additional plugin, the idea is not to load the board with additional processing :smiley: ... for now the major problem I have is that I'm few decades behind modern embeded linux so I'm having issues doing basic stuff with armbian (like turning on the SPI and having the fb module do it's thing) especially as with armbian bunch of stuff don't work on mainline kernel so I don't know if I'm doing something wrong or the darn thing just don't work yet :frowning: ... with rpi and raspbian it works all like a charm but, as I mentioned, I use opi boards due to higher cpu power and better price & availability in eastern europe. the rest is not a big deal I'm able to fetch all data I want, i just wanted to check if there's a simpler way so I can do it immediately and not after I implement everything trough rest :smiley:


#6

Assuming that you can't/won't write a Python-based wrapper plugin, then I think you're going to need to talk to that REST API.

Honestly, I wrote a very decent mobile app that only used the REST API. It was very functional. I suppose you have to make some compromise about widgets which are supposed to be updating (temperature gauge). Polling the API taxes the system, I'm sure.

You could also think in terms of reversing things. The OctoPrint interface also includes Events. In theory, you could have a small executable which is registered with an event. This could facilitate communication in some cases.


#7

I really dislike python so I will try to avoid it wherever I can. Will check out events but I need to look at that jscript front end is using to see how it talks to octoprint, I should be doing exactly the same thing..


#8

She's using Knockout.js as the means of data-driven content.

For example in this widget:
05%20PM

<span title="Total print time so far">Print Time</span>
:
<strong data-bind="text: printTimeString">-</strong>

The data-bind key/value is Knockout syntax. It's the snippet which represents the line which reads "Print Time: -" in the display. Here is the template code that backs this on the repository.

<span title="{{ _('Total print time so far') }}">{{ _('Print Time') }}</span>
:
<strong data-bind="text: printTimeString"></strong><br>

I've added a couple of hard returns to make this easier to see. To the best of my knowledge, the inner syntax _('Some text') should go through the language module to expand into a locale-specific string of text.

The moustache-like syntax of {{ something }} just writes the content as HTML from the template. (A single set of french braces in JavaScript would make something an object.)

On the client side of things, here is the JavaScript file.

        self.printTimeString = ko.pureComputed(function() {
            if (!self.printTime())
                return "-";
            return formatDuration(self.printTime());
        });

That ko is a reference to Knockout. It's sort of the glue between pushing server-side variables into slots within JavaScript for the presentation side of things. It also allows events (pushing a button) to bubble back up through that interface to handlers. That return "-" then explains why we see just a hyphen when a job hasn't yet been started for the Print Time field in the widget.

And of course, there's a Python file which matches up to this. Search for "printTime".


I know. That's a lot of info. I just spent last year as a software development instructor so I'd rather throw lots at you and let you take what you can use.


#9

thanks, that saves some time digging :slight_smile: ... not "a lot" :smiley: ... I'd say "just enough" :slight_smile: ... I'm 30+ years in development but 99% of it is on the backend side :smiley: .. I was never too interested in frontend :smiley: but from time to time, like now, I have to do that too... and technology moves at impossible rates between my frontend ventures :blush:

anyhow looking trough it knockout helps to deal with data in javascript, I'm trying to figure out how it fetches it ... when I click on connect button, it calls api ( /api/connection) then it reads settings ( /api/settings ) /api/printerprofiles then checks connection status /api/connection and a list of files /api/files?recursive=true but .. now it's fetching temperature data trough something .. probbly winsocket as I don't see any fetches happening to get the temp data / read the terminal stream.. that's done by knockout's data layer I assume ... I see debug info

Starting dependency resolution... packed_core.js:12949:9
...
Initial application setup done, connecting to server... packed_core.js:13363:9
Connected to the server packed_core.js:11099:9
...
Application startup complete

this "connecting to server" is what I need to find to better understand how to replicate this behavior in C/C++ :slight_smile: .. @foosel drawn the schematic here https://gist.githubusercontent.com/foosel/0cdc3a03cf5311804271f12e87293c0c/raw/abc84fdc3b13030d70961539d9c132ae39c32085/octoprint_web_startup.txt :smiley:

anyhow api calls for major "functions" is the way to go since the web ui is doing it too, so the work I done so far is ok, it's the push from the server that I need to solve further (to handle M117 plugin for e.g. display temp graph..) :slight_smile:


#10

weird ... the data going trough websocket is weirdly encoded


#11

Maybe Unicode used ?


#12

thats no unicode ... it uses socksjs .. I never tried it before and I see there's no c/c++ library for it... only js, python..


#13

Check out line 58 here. So is this how's it's packing the data? Yeah.


#14

octoprint/static/js/app/main.js, line 708. Welcome to the world of viewmodels.


#15

Thanks, I hate you :smiley: :smiley: :smiley: with all this info I now have no excuse not to finish the project :smiley:


#16

need sleep :frowning:

pi@orangepione:~$ OctoPrint/venv/bin/python
Python 2.7.9 (default, Aug 13 2016, 17:56:53)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from octoprint.vendor.sockjs.tornado import protowebsocket
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name protowebsocket
>>>
>>> b = bytes(b'\xE4\x5B\x4D\x6F\xC2\x30\x0C\xFD\x2B\xBE\x01\x12\x0A\x13\x1F\x83\xF5\x32\x2E\xBB\x73\xD9\x79\x2A\xAD\x3B\x2A\xD1\xA4\xAB\x61\xB0\x7F\x3F\xBF\x24\x8C\x0A\x81\x86\xB6\x0B\xD3\x4E\x95\xA5\xC6\xB5\xAD\x28\xA9\xDF\xF3\xBB\xC0\x1D\x3C\x5B\xDE\xD7\x41\x3B\xE1\x71\x68\xDA\xAD\x30\x42\x06\x14\xFA\x44\x6A\xD0\xA7\x7A\xCD\xA9\x30\xC6\xEE\x04\x7F\x08\x70\xEF\x8F\x3B\xA3\xCE\xA8\xD0\xB5\xB9\xE6\x55\xAE\xE5\x20\xC1\x78\xDA\x67\x5C\xA3\x65\x49\xA8\x93\xF3\x3B\x8E\xB4\x86\xE1\x4A\xA2\x7F\x16\x81\xB4\x00\x06\x2D\xB7\x7A\xCD\xF2\x66\xDB\x58\x0D\xC5\x3A\x02\x46\x4A\xDD\xB8\x2C\x6F\x41\xC1\xA4\x1F\xAA\xB0\x19\x34\x1C\x4A\xB3\x0C\x4E\x9C\xF5\x21\x3E\xF6\x3A\x34\xD7\xF8\xAA\xCA\xD4\x1F\xC9\x0B\xFC\x02\x96\x4D\xB4\xAB\x84\x32\x61\x11\xE2\x17\x66\x3A\x0C\x9D\x17\xE9\x9B\x39\xE6\xE1\x9A\xD7\x41\xC8\x38\xD4\x02\x39\xE9\xFD\x2F\xE5\x32\x94\x44\x34\x73\x72\x05\x6D\x56\xA5\x98\x2B\x15\x1B\x47\xC9\x44\x54\x51\x04\xA8\x98\xBA\x1E\xC1\xBF\xC9\x52\xF5\xDA\xA2\x1A\x8D\x89\x02\xE9\xD0\xF7\x4F\xE4\x98\xBB\x9D\xFD\x12\xAE\x5C\xC5\x39\x8D\xCD\xDD\x6C\xFA\x70\x3F\xFC\x2F\x9C\xD3\x29\xD3\x3A\x19\x9D\x65\x48\xFF\xC0\x5E\xF8\x0D\x3F\xD6\x7A\xE7\x67\x04\x59\xB4\xCF\x32\x64\xD1\xFA\x96\x22\x43\x77\x73\x0B\x0C\xD9\x27\x00')
>>>
>>> isinstance(b, unicode)
False
>>> isinstance(b, bytes)
True
>>>
>>> b.encode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
>>>

the msg arriving trough websocket does not conform to what I expect from this source .. now this is decing what should come from js to server not from server to js but weird it's not same..

on the other hand preparing to send msg does more/less same thing I don't see where it's getting garbled :frowning:

json_encode = lambda data: simplejson.dumps(data, separators=(',', ':'))

need to check (after I catch some zzzz 6.30am here) maybe this is gz'd and fiddler is not showing data properly (weird if that's true but not having much other ideas now)


#17

damn bloody kasperski anti brain :frowning: ... everything is clear and visible now

a[{"current":{"logs":["Send: M105"],"offsets":{},"serverTime":1546714392.261933,"busyFiles":[],"messages":[],"job":{"averagePrintTime":null,"lastPrintTime":null,"user":null,"file":{"origin":null,"name":null,"date":null,"path":null,"display":null,"size":null},"estimatedPrintTime":null,"filament":null},"temps":[],"state":{"text":"Operational","flags":{"cancelling":false,"pausing":false,"operational":true,"paused":false,"printing":false,"resuming":false,"sdReady":true,"error":false,"ready":true,"finishing":false,"closedOrError":false}},"currentZ":null,"progress":{"completion":null,"printTimeLeft":null,"printTime":null,"printTimeLeftOrigin":null,"filepos":null}}}]

a[{"current":{"logs":["Recv: ok T:20.4 /0.0 @0 B:30.0 /0.0 @0"],"offsets":{},"serverTime":1546714392.768785,"busyFiles":[],"messages":["ok T:20.4 /0.0 @0 B:30.0 /0.0 @0"],"job":{"averagePrintTime":null,"lastPrintTime":null,"user":null,"file":{"origin":null,"name":null,"date":null,"path":null,"display":null,"size":null},"estimatedPrintTime":null,"filament":null},"temps":[{"time":1546714392,"bed":{"actual":30.0,"target":0.0},"tool0":{"actual":20.4,"target":0.0}}],"state":{"text":"Operational","flags":{"cancelling":false,"pausing":false,"operational":true,"paused":false,"printing":false,"resuming":false,"sdReady":true,"error":false,"ready":true,"finishing":false,"closedOrError":false}},"currentZ":null,"progress":{"completion":null,"printTimeLeft":null,"printTime":null,"printTimeLeftOrigin":null,"filepos":null}}}]

this I CAN work with :smiley:

@OutsourcedGuru - it's your fault I'm making yet another gui for octoprint!!! if you didn't point me in the right direction I'd probbly give up :slight_smile:


#18

:evil laugh:

I know, I'm like that... always the enabler. I myself have a ton of half-started projects on a never-ending stack. I'd love to finish a few of them but I've just dropped another onto the top of all this. <_<


#19

I manage to clear up almost all projects of any worth (30% or more done) off my task list by end of '18 by not starting new ones for most part of the '18 ... now I have few projects that needs to get going as they wait for a while but are not yet in the 30%+ stage so I'm making priorities but I separate them into two buckets - those I can do without moving from my basic seat in front of 4 screens and those that require me to move to the other part of my room where all the electronic equipment is, soldering stations, acid baths and some mechanic tools (like mini lathe, mini milling machine ..) or garage (where bigger / dirty tools are) ... so this "gui" went kinda on top of the list of the "can do without moving my big fat bottom from my aeron chair :smiley: " ..

anyhow this looks like it's going to be fun little project :smiley: since I'm really making it up to "works for me" phase and then if anyone likes to refactor add plugin support and stuff - I release most of work as "unlicence", just need to get the approval from oracle before I do so noone has issues with the code after it's released


#20

Mine looks great so far (sorry I can't show it here). The new 10" capacitive touch screen display is a vast improvement over the tiny ones I've used in the past. It supports four different languages as well.

I'm coding on my MacBook and then push the folder over to the Pi. I've added inotify-tools to watch that folder on the Pi and auto-reload the interface. The rsync is then added as a custom command in Visual Studio Code.

  1. edit something on the Mac and save
  2. do the keystroke to push to the Pi
  3. the watcher auto-reloads the interface on the Pi

It's a sweet little workflow, if I do say so myself. (It's only good for development since it kills all python programs, as written below.)

/usr/local/bin/python-watch

#!/bin/sh

python /home/pi/project/__init__.py &

while inotifywait -e modify -r /home/pi/project; do
    command=`ps -x -ocommand |grep python | grep -v grep | grep -v python-watch`
    echo "$command"
    killall python
    python /home/pi/project/__init__.py &
done