0

Would anyone know when using python-shell if you can keep the python script running when its called from Javascript? Maybe call a method from a Python class from Javascript using python-shell?

For example this code works but it kills the Python script every time its called.

let {PythonShell} = require("python-shell");

let options = {
  mode: "text",
  args: ["read", "12345:2", "analogInput", "2"],
};

PythonShell.run("bac0Worker.py", options, function (err, results) {
  if (err){
    console.log(err)
    console.log("An error happened")
  }else{
      // results is an array consisting of messages collected during execution
    console.log("results: ", results);
    console.log("Python Script Finished");
    }
})

On the Python side I am experimenting with a package called BAC0 for BACnet systems. What I am trying to do is figure out if I can keep the BAC0 script running, I think under the hood of BAC0 there's alot of process going on where it may create lots of unnecessary traffic on a BACnet network if the script is started/stopped a lot.

bac0Worker.py

import sys
import BAC0

BAC0.log_level('silence')
bacnet = BAC0.lite()


def do_things(address,object_type,object_instance):
        try:
            read_vals = f'{address} {object_type} {object_instance} presentValue'
            read_result = bacnet.read(read_vals)
            if isinstance(read_result, str):
                pass
            else:
                read_result = round(read_result,2)
            print(read_result)
        except Exception as error:
            print("read error")


def main():
    # args from Javascript
    first = sys.argv[1]
    second = sys.argv[2]
    third = sys.argv[3]
    fourth = sys.argv[4]

    # get sensor data
    do_things(second, third, fourth)

    # all done    
    bacnet.disconnect()



if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("bacnet app error")
        bacnet.disconnect()

Like I mentioned the code works if I run from Node I can see the sensor value from the BACnet network 71.7 degrees.

C:\Program Files\nodejs\node.exe .\start.js
results:  (1) ['71.7']
Python Script Finished

Hopefully this makes sense, sorry for the odd question but curious if anyone would have any tips for how to keep the Python script running where I could then just pass values (sensor & device addressing info) to BAC0 script to request a BACnet read from the network. In the main function of the Python file if I replace this:

# all done    
bacnet.disconnect()

With:

while True:
    pass

This would keep the Python file alive I just don't know how to pass values to a live Python script using python-shell. Thanks for any tips not a lot of wisdom here best practices. Curious if I should change my do_things function into a Python class with a method called do_things where then this class could get called from Javascript?

2 Answers 2

2

.js

const { PythonShell } = require("python-shell");
let pyshell = new PythonShell("my_script.py");
pyshell.send("hello");
pyshell.on("message", function (message) {
  console.log(message);
});
setTimeout(() => {
  pyshell.send("Another Hello");
}, 3000);
const end = () => {
  pyshell.end(function (err, code, signal) {
    if (err) throw err;
    console.log("finished");
  });
};
// end();

.py

import sys
    
def log():  
    print('hello from python')
while True:
    inp = input(" ")
    if inp=="hello":
        log()

this will keep the process running unless you call the end method from the nodejs or some error occurs. This may incur performance issues because python thingy will be sharing same resources as your nodejs. A better way to do this would be to use a microservice like deploy some Rest Api which handles python specific tasks.

Sign up to request clarification or add additional context in comments.

4 Comments

What does setTimeout(() => { pyshell.send("Another Hello"); }, 10); do?
Could you give me one more tip? How can I modify the pyshell.send("Another Hello"); on a reoccurring interval? For example every 60 seconds pyshell.send("Another Hello");
setTimeout(() => { pyshell.send("Another Hello"); }, timeInMilliSeconds); is just for demo purpose...if your want to run something continuously then replace setTimeout with setInterval. More about setInterval here: developer.mozilla.org/en-US/docs/Web/API/setInterval
Basically the syntax is setInterval( functionYouWantToRun , timeInMilliseconds )
1

Just for fun Ill post what I was able to learn from @sandeep (thanks a million btw), this works. I can print BACnet sensor values which are gathered from Python from running a javascript file on 60 second setInterval(pythonDo, 60000); with keeping the Python file alive:

C:\Program Files\nodejs\node.exe .\start.js
BAC0 start success
 68.47
 68.43
 68.29
 68.25

start.js

const { PythonShell } = require("python-shell");

let pyshell = new PythonShell("bac0Worker.py");

function pythonDo() {
  pyshell.send("read 12345:2 analogInput 2"); 
}

pyshell.on("message", function (message) {
  console.log(message);
});

var data = setInterval(pythonDo, 60000);

bac0Worker.py

import sys
import BAC0

BAC0.log_level('silence')
bacnet = BAC0.lite()
print("BAC0 start success")

# available BACnet actions
SUBSTRING_READ = "read"
SUBSTRING_WRITE = "write"
SUBSTRING_RELEASE = "release"


def bac0_worker(action, BACnet_request, **kwargs):
    value = kwargs.get('value', None)
    priority = kwargs.get('priority', None)

    if action == "read":
        try:
            read_vals = f'{BACnet_request} presentValue'
            read_result = bacnet.read(read_vals)
            if isinstance(read_result, str):
                pass
            else:
                read_result = round(read_result,2)
            print(read_result)
        except Exception as error:
            print("read error")
  
    elif action == "write":
        try:
            write_vals = f'{BACnet_request} presentValue {value} - {priority}'
            bacnet.write(write_vals)
            print(write_vals)         
        except Exception as error:
            print("write error")

    elif action == "release":
        try:    
            release_vals = f'{BACnet_request} presentValue null - {priority}'
            bacnet.write(release_vals)
            print(release_vals)
        except Exception as error:
            print("release error")
    else:
        return "server error on BACnet opts"


def main():
        while True:
            # from javascript
            inp = input(" ")

            for word in inp.split():
                if SUBSTRING_READ == word:
                    stripped_string = inp.replace(SUBSTRING_READ + " ", "")
                    # do a BACnet READ request with BAC0
                    bac0_worker(SUBSTRING_READ,stripped_string)

                if SUBSTRING_WRITE == word:
                    print("WRITE Match Found")

                if SUBSTRING_RELEASE == word:
                    print("RELEASE Match Found")                    



if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("bacnet app error")
        bacnet.disconnect()

1 Comment

Interesting work guys henry, @sandeep.kgp

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.