Dear Community User! We have started the migration process.
This community is now in READ ONLY mode.
Read more: Important information on the platform change.

cancel
Showing results for 
Search instead for 
Did you mean: 
SOLVED

Read real values and use in ThreeJS

Read real values and use in ThreeJS

RobotART_Walter
Long-established Member

Hello All,

I have created a 6-axis model of a robot arm with the ThreeJS library. This works quite well, I have now a complete arm with all the sepate links and joints. 
Currently I set the axis angles 'hardcoded' to a specific value but what I now want to do is read the 6 axis values from the plc app (coming from a physical robot) and use these values instead, to create a 'digital twin'.

What is the easiest way to achieve this? I tried using the Item manager but could't get it to work. 

 

Thank you in advance!

 

 

 

10 REPLIES 10

bostroemc
Occasional Contributor

First I would suggest serializing the robot positions (in your PLC application) into a single JSON string:

position_json.png

This will allow you to subscribe to a single item in your WebIQ application, plus it will ensure consistancy of the position data.

To build the JSON you could employ one of the official libraries - CODESYS provides one - but in this case it suffices to build it by hand:

build_json.png

In WebIQ, map the single JSON string (here: Position) over OPC UA.  A local script to subscribe to this one item, then parse it and reconstruct the real array is shown below:

 

(function () {
    var MODULE_NAME = "robot_position",
        ENABLE_LOGGING = false,
        RECORD_LOG = false,
        logger = shmi.requires("visuals.tools.logging").createLogger(MODULE_NAME, ENABLE_LOGGING, RECORD_LOG),
        fLog = logger.fLog,
        log = logger.log,
        module = shmi.pkg( MODULE_NAME );

    // MODULE CODE - START
    /* private variables */
    /* private functions */
    /**
     * Implements local-script run function.
     *
     * This function will be called each time a local-script will be enabled.
     *
     * @param {LocalScript} self instance reference of local-script control
     */
    module.run = function (self) {
        //Place your Code here
        var im = shmi.requires("visuals.session.ItemManager"),
            userManager = shmi.visuals.session.UserManager,
            itemHandler = im.getItemHandler(),
            tok = null;

        itemHandler.setValue = function(v) {
            let _v = JSON.parse(v); // parse string into a json object
           // for demonstration: just write values into WebIQ variables
            for (let i = 0; i < 6; i++) {
                // adapt example here to set three.js robot positions
                im.writeValue(`positions[${i}]`, parseFloat(_v.Position[i]));
            }

        };
        // subscribe item means: Every time, when item value changes function itemHandler is called
        tok = im.subscribeItem( "position", itemHandler);

        /* called when this local-script is disabled */
        self.onDisable = function () {
            tok.unlisten();    // remove subscription
            self.run = false; /* from original .onDisable function of LocalScript control */
        };
    };

    // MODULE CODE - END
    fLog("module loaded");
})();

 

Modify it as required to tie into your three.js model.
Sample output (crude) would look like:

output.png

WebIQ project attached.

webiq-sk
Frequent Contributor

Can you please elaborate on "I tried using the Item manager but could't get it to work."

What specifically does not work? Please note that WebIQ does not currently supports structures as ExtensionObject and arrays that have not been flattened to single items in OPC-UA.

RobotART_Walter
Long-established Member

Thank you,

 

I managed to succesfully read the floating numers now.

I can show them the same way like your example.

The only thing I not fully understand is how to set my THreeJS rotation to the the positions[i] values. 

Hardcoded it looks like this:

RobotART_Walter_2-1682073879676.png

 

how can I access the positions[] and copy them to my a1 variable?

 

 

 

RobotART_Walter_0-1682073557899.png

 

bostroemc
Occasional Contributor

The array I called positions was simply meant to represent the robot angles - what you are calling a1, a2, ..., a6.  Assuming these are globals accessible by the ItemManager, you can write to them using the writeValue member functions as in the example.

For example, replace:

 

im.writeValue(`positions[${i}]`, parseFloat(_v.Position[i]));

 

with 

 

im.writeValue(`a1`, parseFloat(_v.Position[0]));
im.writeValue(`a2`, parseFloat(_v.Position[1]));
.
.

 

You will need to decide how best to integrate the sample code into your own code, but I suspect you will want to handle everything in the same routine: parse the JSON, assign the robot posture variables (i.e sFrame.rotation.x, etc.) and force redraw of the three.js model.  If this is not possible or feasible, note the ItemManager provides read access to variables via method readValue.

webiq-sk
Frequent Contributor

Please note that you should *not* use readValue and writeValue unless you are using virtual items only unless you have these items subscribed somewhere at that same moment. Otherwise, readValue will only return the last value read which is not necessarily the latest value from the PLC. For everything else you have to use readDirect and writeDirect - please read the documentation for more info on this.

Please also note by doing it this way when you have multiple HMI instances (i.e. browser windows) accessing the same HMI this code will be executed non-simultaneously several times - once for each browser window. Therefore, it is recommended to use a separate NodeJS script that parses the JSON and sets the item values globally only once. It has to use the API for that.

You can find an example in our customer area on how to create such an API client: https://www.smart-hmi.com/user/download/deliver/xmp/Additional%20Software/webiq-server-api-example-n... 

RobotART_Walter
Long-established Member

Thank you!,

 
I implemented it this way and now it works quite nicely. It first seemed like the values where not updating and therefor the threeJS received a null and was not updating my objects, however this was due to a mistake from my side. But now  it works the way it should.

I will take a look on what @webiq-sk mentioned and rewrite the function to use the readDirect function. 

 

 

bostroemc
Occasional Contributor

Just to clarify, in the example above writeValue and readValue were not used with items tied to PLC variables, but rather with helper variables only.  I understand and accept your point that these should have been declared as virtual items.  The only PLC variable in question - the JSON string - was read in a subscription.

 

webiq-sk
Frequent Contributor

"Helper variables" that are not virtual items still reside on WebIQ Server and not in the HMI and are only copied to the HMI - if these items are not subscribed anywhere readValue will simply return the last read value only.

Please always keep in mind that any HMI might be opened multiple times on different devices and you might want to share (items) or not share (virtual items) specific values with other HMI instances. Using readValue/writeValue only shares them somewhat in certain circumstances and is therefore not recommended for non-virtual items.

bostroemc
Occasional Contributor

Please re-read my post above: I acknowledge your point that the helper vars should have been declared as virtual items.

webiq-sk
Frequent Contributor

Actually I should probably have written it this way to state my point more clearly:
- if you ever might want to use this on more than one system (browser) at once you should use non-virtual items only and a single NodeJS script that parses the JSON and sets the non-virtual items accordingly
- if you only want to use this on a single system (browser) now and always using virtual items is sufficient

 

Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist