Is there any way to programmatically monitor the status (quality) of the IP Camera component’s video stream? Seems like there would be some response code attribute available to check if/when things go wrong.
There are two situations I need to catch if possible:
When the number of connect retries has been reached
When (for whatever reason) the stream ends inadvertently.
Is there any hidden properties that can be used in scripting (eg. property change scripts) to catch these events?
The IP camera component has an internally nested FrameProducer class on which you can call getState(). Getting to that is an exercise for the reader, but I’ll give two hints: 1. It’s not going to show up with a dir() call, and 2. it’s internally named parser.
Also there is no mention of the parser attribute in the PMIIPCamViewer class itself.
Programmatically the IP Camera Viewer component’s only exposed interface would be the property-change event script. Any hint on what property to monitor? The component does not allow creation of custom properties. Normally property-change scripts only fire for changes of bindable properties.
I tried getting around this issue by switching from MPEG stream to JPEG stills. Big bonus of doing this is the lower load it creates on the IP-camera app-server.
Unfortunately even using JPG Stills results in occasional exceptions that I would like to trap:
Really all I need is some way to catch any/whatever exception occurs in the component. Usually reloading the screen will rectify whatever the problem is.
Note: Rather than drinking MPEG, I am using a sequence of JPEG stills to reduce bandwidth / load on our already-over-taxed video servers. I have not tested the band-aid to see if it works with the full blown MPEG streaming.
Still getting occasional HTTP 400: Bad Request exceptions that leave the stream stalled.
Interim work around is to put another slower polled band-aid (eg. every 5 minutes) that simply hammers the cam-viewer, issuing the reconnect() whether or not it is necessary.
After a little digging around I got this sorted out. Through the use of reflection I got hold of the parser object and then the getState method inside. The only oddity I found was when displaying the string in a label used for debugging purposes sometimes it was all upper case and other times it was first letter upper, rest lower case (RUNNING or Running).
The script below was attached to a mouseClicked event on the IP Cam viewer for testing but in prod we use a timer to run the check and issue the reconnect() call if need be
from com.inductiveautomation.factorypmi.application.components import PMIIPCamViewer
parserRef = PMIIPCamViewer().getClass().getDeclaredField('parser')
parserRef.setAccessible(True)
cam = event.source
parser = parserRef.get(cam)
# If the url property is blank, parser is None
if parser is not None:
stateRef = parser.getClass().getSuperclass().getDeclaredMethod('getState')
stateRef.setAccessible(True)
# Enum contents: CONNECTING, RUNNING, CANCELED, ERROR
state = stateRef.invoke(parser)
stateRef.setAccessible(False)
parserRef.setAccessible(False)
Looks familiar.... If you look at the Usage Notes for my Image Streamer module, you'll see a formal version of this as a script module, suited to use in a binding. Allows you to copy camera view instances around without needing a separate timer component.