Transport Image from Client to Gateway with message handler

Hello All,

“I THINK” I am close to have this working but hung up “File” not defined. Thanks in advance!

Client side script:

import base64
import java.io.File
import java.io.ByteArrayOutputStream
from javax.imageio import ImageIO
from java.io import File  # Explicitly import File here

print ("handleImage started")
def sendImage():
    # Define the path to the image file
    image_path = "C:/users/public/pictures/Shiny_KPI.jpg"  # Change to your image path

    # Read the image file
    image_file = File(image_path)
    image = ImageIO.read(image_file)

    # Encode image to base64 string for transmission
    output = ByteArrayOutputStream()
    ImageIO.write(image, "jpg", output)  # Save as jpg
    image_bytes = output.toByteArray()
    base64_image = base64.b64encode(image_bytes).decode('utf-8')

    # Send the request to the gateway
    response = system.util.sendRequest(
        "LMIDRILL_KPI",       # Replace with your actual project name
        "handleImage",     # Name of the message handler you will define
        {"image": base64_image}  # Sending the image
    )

    # Handle the response
    if response['success']:
        system.gui.messageBox("Success: {}".format(response['message']))
    else:
        system.gui.messageBox("Failed: {}".format(response['message']))

# Call the function to send the image
sendImage()

The Gateway side script Message handler (its named (called) “handleImage”:

def handleMessage(payload):
	"""
	This message handler will be called each time a message of this
	type is received.

	Arguments:
		payload: A dictionary that holds the objects passed to this
		         message handler. Retrieve them with a subscript, e.g.
		         myObject = payload['argumentName']
	"""
	logger = system.util.logger("ImageHandler")
	logger.info("Image handler started")
	
	try:
	    # Get the Base64 encoded image from payload
	    base64_image = payload['image']
	    logger.info("Received image payload.")
	
	    # Decode the image
	    import base64
	    from java.awt import Image
	    from java.awt.image import BufferedImage
	    from javax.imageio import ImageIO
	    from java.io import ByteArrayInputStream, File
	
	    image_data = base64.b64decode(base64_image)
	    input_stream = ByteArrayInputStream(image_data)
	    buffered_image = ImageIO.read(input_stream)
	
	    # Save the image to disk
	    output_file = "C:/path/to/save/image/Shiny_KPI_received.jpg"  # Change path as needed
	    ImageIO.write(buffered_image, "jpg", File(output_file))
	
	    logger.info("Image saved successfully.")
	    return {"success": True, "message": "Image received and saved successfully!"}
	
	except Exception as e:
	    logger.error("Error processing image: " + str(e))
	    return {"success": False, "message": "Error processing the image."}

I am having an error on the client side “NameError: global name ‘File’ is not defined”, this should be defined with “import java.io.File” and or “from java.io import File”

Thank You

Are these scripts located in the project library? If not they should be, and the actual handlers/actions should be a single line calling the necessary function in the project library.

You shouldn't perform an import inside of a definition function. All imports should be in the top level of the project library outside of any function definition.

Consider using system.file.readAsBytes. That should return your image files as a byte array, eliminating the need for File and ByteArrayOutputStream. system.file.write can be used for the file write on the gateway, and should be able to handle the decoded byte array.

Additionally, your try block is only catching python exceptions. Add

except java.lang.Throwable as t:
    logger.warn("A java exception occured", t)

to your try block to catch java errors.

OK, I did attempt to define a script in the project library and call it from the client timer function but it failed. Is there something I am missing that needs to be done that I may have missed? Thanks for the suggestions!!

Show how your client timer function is configured. Also, why are you calling this from a timer event instead of a button click or similar?

Elaborate. Did it throw an error, did nothing happen?

Review how your project library script was configured and defined.

Ryan, Thanks for the feedback. The honest truth it I don’t remember. I tried it and it didnt work for some reason but its been a while. I will make another attempt for sure, if I get it trouble i will figure out the cause but you telling me it is supposed to work will give me a good reason to renew my effort.

As for the timer. This function is supposed to be automated. Its a backend function that the user “operator” would never need to interact with. My supervisor wants images of KPI screens sent to him regularly, not to check up on the systems per-se but to “sell” ignition to upper management.

If that's the case then you probably should remove the calls to system.gui.messageBox (I can understand if you were using it to verify that it was working)

You are correct, I will remove them once i am done debugging! Thanks for sending me the article on Project Library, now that I am reading that I see i didn’t fully understand how it worked. This task was supposed to be a little thing and it has turned out to be much more difficult. I appreciate the help! Thank You

I'm going to wade in here with a few more pieces of concrete advice and then dip out, because frankly every time folks try to help you it seems to skate right off.

First:
What you're doing is ridiculously overwrought.
There's no need to invoke even a single import to read a file and send it to the gateway.
system.file.readFileAsBytes reads a file path and returns a byte array.
There's no need to invoke a single import to send a byte array via sendRequest; byte arrays are handled natively, no need to base64 encode.

Similarly, on the receiving side, there's no need for a single import - you can just unpack the bytes out of the payload dictionary and pass them directly to system.file.writeFile.

To wit, all that you have here could be replaced for exactly the same semantics in dramatically less code:


def uploadImage(image_path, dest_path):
	image_bytes = system.file.readFileAsBytes(image_path)
	return system.util.sendRequest(
		"LMIDRILL_KPI", 
		"handleImage",
		{"image": image_bytes, "destination": dest_path}
	)

def handleMessage(payload):
	logger = system.util.logger("ImageHandler")
	logger.info("Image handler started")
	
	try:
		image_data = payload['image']
		system.file.writeFile(payload["destination"], image_data)
	
		logger.info("Image saved successfully.")
		return {"success": True, "message": "Image received and saved successfully!"}
	
	except Exception as e:
		logger.error("Error processing image", e)
		return {"success": False, "message": "Error processing the image."}
image_path = r"C:/users/public/pictures/Shiny_KPI.jpg"  # Change to your image path
output_file = r"C:/path/to/save/image/Shiny_KPI_received.jpg"  # Change path as needed
response = uploadImage(image_path, output_file)
system.gui.messageBox("Success?: {}, Message: {}".format(response["success"], response['message']))

Five minutes of looking through the manual, the forums, or using the autocomplete in the software would have guided you there. Instead, you are blindly trusting an LLM to write Ignition code for you. If you already know what you are doing, this is acceptable. If you do not, an LLM will happily write you code that you do not need, especially within a niche domain such as Ignition.
As you have already experienced, repeatedly, in this thread and your last, your LLM of choice (no matter which one) cannot replace knowledge and experience, and you are wasting everyone's time if you don't actually engage your brain and instead outsource all your thinking to a series of matrix calculations.

That this community chooses to continue to help you is a mark of their very human generosity; I would recommend you not abuse that and do your best to engage your own human brain in good faith.

4 Likes

I’m pretty sure this is related to another recent thread where the OP is trying to use screenshots to make Vision “stream” to a browser.

Edit, sorry had skimmed, op has already clarified that this is indeed the case.

Paul, I am sorry that I have left the impression that I don’t try. My knowledge here in this arena is very low and I am learning allot with the help of this community. I sincerely appreciate all of the assistance. There are simply some contextual gaps in my understanding that make finding the correct data more difficult, as I often don’t know what to ask to solve the problem nevermind where to do to find it. I am not going to give up trying to solve problems just because i don’t understand. I am going to keep going, keep learning. There has been allot of frustration pointed at me in this forum and I get it, if I was at your level of understanding I can see that my questions would be irritating. Just understand, I don’t get on this forum to have someone do my work for me, i am here because I have tried all the solutions I can think of at the time and I am failing. I sincerely appreciate your patience!

2 Likes

The traditional approach to learning a new, complex subject, is to take all of the classes that are easily available (like the free Inductive University) and studying all available documentation (like the free online user manual) and recommended related resources (like Python tutorials).

Then test your understanding of the bits and pieces by trying small examples. You are trying to bypass the normal learning process because someone has pitched "AI can do it" idea to you, and it is killing your progress. You clearly retained nothing from the prior forum topic.

Reading just a bit between the lines, Paul's comment is pure, barely-contained frustration. Expressed in a low-key way, as he is a supremely nice guy. (Way nicer than me.) Not to put words in his mouth, but I would take it as the end of his patience.

While Paul is IA staff, most of us are volunteers. Please consider this long-standing guidance for how to interact with a volunteer community:

It is written for the world of Linux development, but the advice is applicable and timeless.

Phil, I understand your perspective. No one pitched AI to me, I started using AI to try to fill in the blanks, asking it the really stupid questions. And maybe you are right I am trying to skip some steps. This is one small part of my job, I have other things I need to be doing, other challenges I need to deal with. You where right in the last post when you said “Try”, I did need to try harder. This time i really did feel like I had tried as much as I could; what I shared for script today was two days worth of work, sad to say. AI has been wrong at least 50% of the time, I have spent allot of my time trying to figure out how it is wrong. For the record I really did learn allot from the last topic. Now I know that unlike allot of other things I deal with, the manual is really valuable ect… I sincerely appreciate your advice and the link you shared. My apologies to you and Paul for the frustration! Thanks for getting to the point, I have a more realistic understanding of what is expected in this forum.

1 Like