Hello guys, I am trying to create a simple HTTP Server in Ignition that listen on a port for http requests like GET, POST ecc... and send back a response.
Is there a way to implement this in Python or with external libraries like Flask for example?
I attach the example code
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, world!"
@app.route('/api/data', methods=['GET'])
def get_data():
data = {"key": "value"}
return jsonify(data)
if __name__ == '__main__':
app.run(port=8000)
Hello @kcollins1 thanks for your reply. I know there is a dedicated module but for now we can't buy the license for it, so I was thinking about to do this by script if possible
Basically, no. There's no way via scripting to introduce something durable enough that isn't also a massive memory leak.
If you really want to trade engineering time for a solution, you can always develop a custom module that replicates the "load a servlet" part of the Webdev module:
Custom modules can do whatever you want (except on Edge) and are free.
You're right - Maker is just an opt-in flag on the hook. I thought there was some third party module restriction, but the only difference is which first party modules will load.
How big of a memory leak are we really talking about ?
I have an external application that needs to send its data to Ignition every hour via a POST request, I've created a simple HTTP server (code below) that runs in Ignition.
HTTP server code
from com.sun.net.httpserver import HttpServer, HttpHandler, HttpExchange
from java.net import InetSocketAddress
from java.io import IOException
from java.io import InputStreamReader
from java.io import BufferedReader
from java.lang import StringBuilder
# Define a simple HTTP handler
class MyHandler(HttpHandler):
def handle(self, exchange):
try:
if exchange.getRequestMethod().upper() == "POST":
length = exchange.getRequestHeaders().getFirst("Content-length")
content_length = int(length) if length else 0
if content_length > 0:
input_stream = exchange.getRequestBody()
isr = InputStreamReader(input_stream, 'utf-8')
reader = BufferedReader(isr)
buffer = StringBuilder()
while True:
line = reader.readLine()
if line is None:
break
buffer.append(line)
post_data = buffer.toString()
system.util.getLogger("HTTP_SERVER").info("Received POST data:"+ str(post_data))
input_stream.close()
response = "Received POST data: " + post_data
exchange.sendResponseHeaders(200, len(response))
outputStream = exchange.getResponseBody()
outputStream.write(response.encode('utf-8'))
outputStream.close()
if exchange.getRequestMethod().upper() == "GET":
response = "Hello, World!"
exchange.sendResponseHeaders(200, len(response))
outputStream = exchange.getResponseBody()
outputStream.write(response.encode('utf-8'))
outputStream.close()
except IOException as e:
e.printStackTrace()
# Function to start the HTTP server
def start_server():
server_address = InetSocketAddress(7999)
http_server = HttpServer.create(server_address, 0)
http_server.createContext("/", MyHandler())
http_server.setExecutor(None) # Use the default executor
http_server.start()
system.util.globals["http_server"] = http_server
system.util.getLogger("HTTP_SERVER").info("HTTP server started...")
print("HTTP server started on port 7999")
def init():
# Run the server
if "http_server" in system.util.globals and system.util.globals["http_server"] is not None:
system.util.globals["http_server"].stop(0)
print("server stopped")
start_server()
def close():
if "http_server" in system.util.globals and system.util.globals["http_server"] is not None:
system.util.globals["http_server"].stop(0)
What could happen ? If the thread runs for too long, it could take up the whole memory ?
Considering the fact that the application sends its data every hour, would the memory leak be under control if I restart this server say every half an hour or so ?
I have a hard time justifying the use of the WebDev module to my client considering only one client will send its data, and it's only once an hour.
We had tried a NODEJS server connected with Ignition in TCP/IP socket thru scripts and clients communicating with NODEJS server thru REST APIs. Please see demo link. Perhaps you can try something like that! Or simply let you application connect to Ignition on TCP/IP socket if its not a web application.
If you're caching any custom class in system.util.globals, you're implicitly storing the entire Jython interpreter state (and all of its associated memory usage, which includes all parsed project script libraries, all parsed third party libraries, etc), for at least as long as that instance is alive.
Your server creation and shutdown code is also racy - multiple threads could be running script initialization at once (because we create and shut down the whole Jython lifecycle all the time, whenever you change any script related resources), so one lifecycle might pass the first if at the same time as another and both end up attempting to create a server (and bind to the same address) at the same time. You should use Python's builtin setdefault to atomically update the dictionary.
If you really can restart the server every hour, then, sure, I guess you're successfully band-aid patching a memory leak. How maintainable is that in six months, in five years, etc?
Thanks a lot for your reply, you've convinced myself and the client that the WebDev module would prove much more reliable than this crafty solution of mine, also far from being an expert so thanks for the tips regarding python's setdefault!