SDK transfert data from gateway to perspective

@PGriffith, @PerryAJ

In a module, I need to transfert data from the gateway scope to a perspective client.

  • Is it possible to send a message to a client session/page/view with the sdk ?
    The message will be consume with a script message handler.

  • Is it possible to write a session custom prop with the sdk ?

Not sure if this is what you’re looking for, but there’s an older post that shows how to add listeners for when a session is opened. You might be able to add/modify some properties in that instance.

Hope this helps!

@PGriffith, @PerryAJ
in javadoc:
http://files.inductiveautomation.com/sdk/javadoc/ignition80/8.0.14/index.html
there is no package com.inductiveautomation.perspective

Perry will know more about the Javadoc situation, but the answer to both of your questions should be yes. As in @KGarner's linked thread, you'll need to have a dependency on the Perspective module in your module's code to be in the right classloader scope, and after that get the PerspectiveGatewayContext.

You'll then have to either know how to get into the session you want (or, and it'll be much easier, just send your message to all sessions). You'll need to 'target' this session by it's ID; see com.inductiveautomation.perspective.gateway.session.PerspectiveSessionMonitor.findSession().
That gives you an Optional<InternalSession>, which extends com.inductiveautomation.perspective.gateway.api.Session. Session has a queue method which returns an ExecutorService. All operations against Perspective properties must go through this queue - dispatch lambdas using submit, submitOrRun, etc.

Once you've got a session, you need to decide the scope you're going to send to, and what to actually send. I'd recommend starting at the whole session. Construct a com.inductiveautomation.perspective.gateway.event.UserScopeMessageEvent - for the PyObject, just create a PyDictionary or PyStringMap from a hashmap. Then, get into the queue and dispatch an event to the session:
session.queue().submit(() -> session.getEventBus().post(message)));

Sending to pages or views requires accessing the pageEventBus or viewEventBus, respectively - you'll need to drill in from the Session object. That should be enough to get you started.

  • Is it possible to write a session custom prop with the sdk ?

Yes - similarly, you'll need to have a Session, and any operations you do must go through the queue. You'll need to get the custom property tree, via com.inductiveautomation.perspective.gateway.api.PropertyTreeOwner#getPropertyTreeOf - the InternalSession is a PropertyTreeOwner, so just session.getPropertyTreeOf(PropertyType.custom). Then there are a few public write overloads, such as:

com.inductiveautomation.perspective.gateway.property.PropertyTree#write(java.lang.String, com.inductiveautomation.ignition.common.gson.JsonElement, com.inductiveautomation.perspective.common.property.Origin, java.lang.Object)

The plain Object parameter should just be a reference to wherever you're calling the write from.
Writing to a session parameter that doesn't exist will implicitly create it.

3 Likes

Thanks a lot @PGriffith for all these clarifications :+1:
I will test this as soon as possible !

I’m looking into why the perspective javadocs aren’t included, will follow up.

Just to follow up - we are building/uploading new javadocs with Perspective packages included. Should be available within an hour of this post. We will publish them going forward as well.

-Perry

3 Likes

Thanks @PerryAJ
Packages com.inductiveautomation.perspective.* are now available in javadoc 8.0.14 :grinning:

Dears,
thank you for this post.
Which is the dependency to add to the module pom, in terms of groupId and artifactId?

Thank you so much.

Not tested, but probably something like:

        <dependency>
            <groupId>com.inductiveautomation.ignitionsdk</groupId>
            <artifactId>perspective-gateway</artifactId>
            <version>${ignition-sdk-version}</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>

and perhaps:

        <dependency>
            <groupId>com.inductiveautomation.ignitionsdk</groupId>
            <artifactId>perspective-common</artifactId>
            <version>${ignition-sdk-version}</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>

to confirm by @PGriffith

Thank you @mazeyrat!
I am able to import the com.inductiveautomation.perspective.gateway package.
I follow the code snippet of @PGriffith, then when I install the module the following error occurs:

java.lang.NoClassDefFoundError: com/inductiveautomation/perspective/gateway/api/PerspectiveContext

It seems the module is not able to interact with the Perspective context in the startup phase.
Any suggestion will be appreciated.

Thank you.

have you the dependency with the perspective module in the build.pom

I suppose something like:

                    <depends>
                        <depend>
                            <scope>G</scope>
                            <moduleId>com.inductiveautomation.perspective</moduleId>
                        </depend>
                        <depend>
                            <scope>D</scope>
                            <moduleId>com.inductiveautomation.perspective</moduleId>
                        </depend>
                    </depends>

see gradle example:

moduleDependencies = [
                [scope: "G", moduleId: "com.inductiveautomation.perspective"],
                [scope: "D", moduleId: "com.inductiveautomation.perspective"]
        ]
1 Like

Dear @mazeyrat,
it works fine! Thank you for your kind support and help.

Best,
Andrea

Must be the message to call a Session Message Handler?
I run a java method using the snippet by @PGriffith, the code run with success, but the session message handler is not triggered.

    PerspectiveContext perspectiveContext = PerspectiveContext.get(this.context);
    for (UUID sessionId: GatewayHook.perspectiveSessionListener.getSessionIds()
         ) {
        if(perspectiveContext.getSessionMonitor().findSession(sessionId).isPresent()){
            InternalSession session = perspectiveContext.getSessionMonitor().findSession(sessionId).get();
            PyDictionary payloadMap = new PyDictionary();
            payloadMap.put("sessionId", sessionId.toString());
            payloadMap.put("paramUUID", paramUUID);
            UserScopeMessageEvent message = new UserScopeMessageEvent("test", payloadMap);

            session.queue().submit(() -> session.getEventBus().post(message));
        }
    }

Thanks a lot.

There’s no logging on the event bus itself, unfortunately. You do have a message handler configured on the target, with the same key defined, right?
You can clean up the code a little bit and make sure it’s actually calling your message:

    PerspectiveContext perspectiveContext = PerspectiveContext.get(this.context);
    for (UUID sessionId: GatewayHook.perspectiveSessionListener.getSessionIds()) {
        perspectiveContext.getSessionMonitor().findSession(sessionId).ifPresent(session -> {
            PyDictionary payloadMap = new PyDictionary();
            payloadMap.put("sessionId", sessionId.toString());
            payloadMap.put("paramUUID", paramUUID);
            UserScopeMessageEvent message = new UserScopeMessageEvent("test", payloadMap);
            LoggerEx.newBuilder("messageHandler").build().infof("Calling handler: %s", message)
            session.queue().submit(() -> session.getEventBus().post(message));
        });
    }

Thank you @PGriffith: it works!

1 Like

@PGriffith
That’s fine !
For a script function exposed by a module and called in perspective, can we obtain in the script function the context session Id ? (in order to interact with this session in the script function). I would like to avoid a sessionId parameter…

Seems to be ok with:

InternalSession.SESSION.get().getSession().getSessionId()

Is it the right way to do things ?

If you’re calling the InternalSession.SESSION threadlocal directly, then you don’t need to go through the rest of the Perspective context - you can directly call whatever you want to - ie,

InternalSession.SESSION.get().getEventBus().post();

Also, a healthy disclaimer that while it’s unlikely we’ll change things, the InternalSession threadlocal is not considered part of our API surface, so it could break in some future version without notice.

Is there any other method to retrieve the sessionId ?

Can we restrict the UserScopeMessageEvent for be send to a page and/or view

image