Musson Industrial's Embr-Periscope Module

I have to update my Blob Server and my Image Streamer for this. Since there won't be any more features in the 8.1 branch, I will simply keep the previous module file available for users not yet on 8.1.49+. No separate branch.

If there are any bug fixes, users will have to upgrade the platform too, just like any IA bugfix.

2 Likes

Release Notice

Embr v8.1 - 2026.6.6

This release bumps the minimum compatible Ignition version from 8.1.33 to 8.1.49, allowing users to run Embr Periscope on 8.1.49+.

Minor Changes

  • f64c92d: Bump minimum compatible Ignition version from 8.1.33 to 8.1.49.
2 Likes

Critical Bug Report

The upgrade to Jython 2.7.4 in Ignition 8.3.8 has impacted usage of Periscope's system.perspective.runJavaScript* functions.

I have a fix coming but I'll need a day or two to properly test things.
Until then, a workaround is available in the GitHub issue.

I'll update this post once a fix is available.

Thanks @jason_wellman for the report!

Fix Available

This has been fixed in:
(8.3) Periscope 0.14.5
(8.1) Periscope 0.16.4

See Embr's GitHub Release page for the latest available versions.

4 Likes

Release Notice

Embr v8.1 - 2026.8.31
Embr v8.3 - 2026.8.31

This release resolves a runJavaScript error reported by @jason_wellman
Thanks Jason!

This error was a result of a pretty naive approach to grabbing the current Periscope component context from the current PyFrame :flushed_face:. The fixed approach has been backported to 8.1, even though it didn't seem to be affected.

5 Likes

Example Use Case, Primary View Middleware

This usage example uses Client Resources to introduce a routing middleware, that allows you to dynamically decide the viewPath used for a Page URL.

The router also subscribes to the session's custom properties, making it possible to create reactive rules.

Usage:

// Client Resource
// middleware.routes
import { router } from "./router";

router.use((path, session, viewPath) => {
  const canViewAdmin = session.custom.readBoolean("permissions.admin", false);

  if ((path === "/admin" || path.startsWith("/admin/")) && !canViewAdmin) {
    return "Middleware/AccessDenied";
  }
});

router.use((path) => {
  if (path === "/virtual/path/to/view") {
    return "Middleware/View1";
  }
});

Video:
In this example, the checkboxes are bidirectionally bound to session.custom.permissions.admin.

  1. When the property is false, the router returns Middleware/AccessDenied as the viewPath for all Page URLS that start with /admin.
  2. When the property becomes true, the router reevaluates and returns no value. Because no value is returned, the default Primary View for the Page URL is returned.
  3. The same thing happens in reverse when the property becomes false.

Another natural example is 404 pages; if no viewPath exists for a given Page URL, supply a default.

Project Export:
clientresource_middleware.zip (7.7 KB)

2 Likes

I hope @pturmel sees this - curious to get his take on the opportunity within this module. At a previous company, Phil built me essentially a view router heavily optimized on his Integration Toolkit functions that allowed dynamic assembly of views and navigation. It was SO cool, and I'm eager to see this become more of a standard approach for Ignition solutions.

As you should know, I watch everything here.

I think the example is terrible, as security filtering applied client-side in javascript is essentially no security at all.

Such UI manipulation, if all the relevant data is already client side, make sense as performance optimizations. If data necessary for the end result is on the gateway, then the gateway should push it. I like the dynamic view component more than this kind of router manipulation. But the 404 diversion looks useful. :man_shrugging:

Sure. But this example still works with View permissions; instead of a user being shown the default "Access Denied" screen, you could divert them somewhere else.

One of the problems with single-page web apps is that the entire thing is client side, even if you intend to hide parts of it from the user. Having server side controls on the data supplied to view is good and necessary, but sometimes just having access to the structure of a view aids potential mis-use. Especially if the user can manipulate the browser to trick client-side security, and the server doesn't also check.

Your dynamic view component can entirely withhold a view's structure from an unauthorized viewer. That's a win in my book.

1 Like

That’s exactly why I think the router example is passable :man_shrugging: It’s not any less secure than letting users hit the default Access Denied screen. But I agree that by itself it is not a method of security.

Hm, I've never really thought about that before…

2 Likes