Works in my lab VMs with 8.3.2. Are you sure you installed the v8.3 module file on the v8.3 server? (Module files for v8.1 will not work on v8.3--not for anyone's module.)
If you did use the right file, please share the wrapper.log content that corresponds to the fault.
com.inductiveautomation.ignition.common.modules.ModuleLoadException: Unable to load hook class "com.automation_pros.blobserve.GatewayHook" for module "com.automation_pros.blobserve".
at com.inductiveautomation.ignition.gateway.modules.ModuleInstance.loadHook(ModuleInstance.java:632)
at com.inductiveautomation.ignition.gateway.modules.ModuleInstance.load(ModuleInstance.java:306)
at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.loadModules(ModuleManagerImpl.java:306)
at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.init(ModuleManagerImpl.java:162)
at com.inductiveautomation.ignition.gateway.IgnitionGateway.startupInternal(IgnitionGateway.java:1120)
at com.inductiveautomation.ignition.gateway.redundancy.RedundancyManagerImpl.startup(RedundancyManagerImpl.java:352)
at com.inductiveautomation.ignition.gateway.IgnitionGateway.initRedundancy(IgnitionGateway.java:814)
at com.inductiveautomation.ignition.gateway.IgnitionGateway.lambda$initInternal$1(IgnitionGateway.java:736)
at com.inductiveautomation.ignition.common.execution.impl.BasicExecutionEngine$ThrowableCatchingRunnable.run(BasicExecutionEngine.java:550)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.automation_pros.blobserve.GatewayHook
at java.base/java.net.URLClassLoader.findClass(Unknown Source)
at com.inductiveautomation.ignition.gateway.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:37)
at com.inductiveautomation.ignition.gateway.modules.ModuleClassLoader.loadClass(ModuleClassLoader.java:104)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
at com.inductiveautomation.ignition.gateway.modules.ModuleInstance.loadHook(ModuleInstance.java:609)
at com.inductiveautomation.ignition.gateway.modules.ModuleInstance.load(ModuleInstance.java:306)
at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.loadModules(ModuleManagerImpl.java:306)
at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.init(ModuleManagerImpl.java:162)
at com.inductiveautomation.ignition.gateway.IgnitionGateway.startupInternal(IgnitionGateway.java:1120)
at com.inductiveautomation.ignition.gateway.redundancy.RedundancyManagerImpl.startup(RedundancyManagerImpl.java:352)
at com.inductiveautomation.ignition.gateway.IgnitionGateway.initRedundancy(IgnitionGateway.java:814)
at com.inductiveautomation.ignition.gateway.IgnitionGateway.lambda$initInternal$1(IgnitionGateway.java:736)
at com.inductiveautomation.ignition.common.execution.impl.BasicExecutionEngine$ThrowableCatchingRunnable.run(BasicExecutionEngine.java:550)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
The red faulted does take me to that error. There isn’t anything above that error in the wrapper log that is relevant except that the gateway was restarted. I uninstalled and the restarted the gateway, reinstalled and restarted the gateway again and it still has the error. perhaps the download didn’t go well and i should download again.
Thanks for the help. IT security was blocking the installation without any popup or notification that it was doing that. I called support and was able to get it installed.
Appreciate the work on your module, great work. We are trying to build something similar in our Perspective project: we have files stored on our server that we can load as blob content, and want to display the file in the browser. We have created a WebDev endpoint to serve the blob, but we run into authentication issues there. We are using SSO in our project, yet when we try to reach the endpoint in the browser an authentication prompt is still made. This is not workable for us. We are wondering how you present the blob content in the browser without using the webdev module.
Say we have blob content like this: blob = system.file.readFileAsBytes(file_path). How do you then display this in the browser?
I’m running into an “issue” with the Blob server and videos. Issue in quotes because it’s working fine, but writing a warning to the Gateway log every time a video is accessed. This is just an annoyance at the log entries, and not impacting functionality.
If I use a text file everything works as expected.
The video is a ~6 MB .webm and it plays fine when accessing it through the Blob server in a browser.
This warning is showing up in the gateway log though:
Unable to handle blob request path /blob/…
org.eclipse.jetty.io.EofException: null
at org.eclipse.jetty.io.SocketChannelEndPoint.flush(SocketChannelEndPoint.java:116)
at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:422)
at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:377)
at org.eclipse.jetty.io.SelectableChannelEndPoint$2.run(SelectableChannelEndPoint.java:67)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:375)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.io.IOException: Broken pipe
at java.base/sun.nio.ch.FileDispatcherImpl.writev0(Native Method)
at java.base/sun.nio.ch.SocketDispatcher.writev(Unknown Source)
at java.base/sun.nio.ch.IOUtil.write(Unknown Source)
at java.base/sun.nio.ch.IOUtil.write(Unknown Source)
at java.base/sun.nio.ch.SocketChannelImpl.write(Unknown Source)
at java.base/java.nio.channels.SocketChannel.write(Unknown Source)
at org.eclipse.jetty.io.SocketChannelEndPoint.flush(SocketChannelEndPoint.java:110)
I would think that an artifact of size limits in perspective. Nothing my module can do about that. Also, large blobs do not play nice with NQ caches. Ergo my recommendations for segmenting videos.
I'm having the same issue as @rpavlicek that I can't update the pictures using .refreshBinding().
I have also tried using a throw-away parameter dateExtract(now({session.custom.updateTime}), 'second') but the problem with this one is that once a minute has passed, it uses the cached images from the last get-request. I could probably change it into now({session.custom.updateTime}) but then I will just fill the cache with old pictures and you recommended to NOT do this.
I'm doing a "surveillance system" on a production line by taking pictures from IP-cameras using a Gateway Timer Event. Then I present the pictures from the SQL table in a picture component in Perspective which is passed into a Flex repeater to be able to pass the cameraID into the right component.
How would I go about your recommendation to insert rows into the SQL table instead of updating?
Right now my table has a cameraID column and a image_blob column. My url uses the cameraID as the only parameter. So even if I add a thrid column, named ID which is primary key, my url won't change.
Do I need to fetch the new ID and pass that into the url instead? Or will it work using the cameraID because the SQL-query is not the same?
There's no good answer. You are going to abuse the browser's image cache one way other other. Instead of using just the second as the throwaway parameter, consider using the full timestamp milliseconds.
Or add a column for image ID and autoincrement it on insert. Run a timer that prunes old images to clean up the DB.