Skip to content

kmu2030/ModeManagerLib

Repository files navigation

ModeManagerLib

ModeManagerLib provides a simple initialization system (ModeManager) and an external control interface via OPC UA for Omron's NX/NJ controllers. A set of programs is called a mode, and the program to be launched is determined by selecting a mode. For details, please check "Sysmac Studioで簡単な初期化システムを作る".

The OPC UA external control interface allows for mode operations from a terminal's PowerShell or directly from an application, as follows. External Operations via OPC UA

By defining a mode as shown below and executing the mode activation POU, the program specified in the mode will be started.

IF P_First_Run THEN
    SPEC_USE_MQTT := TRUE;
    SPEC_USE_KINTONE := TRUE;

    ModeManager_init(Context:=iModeManager,
                     // The name of the program under which the mode manager runs.
                     Name:='ProgramLoader',
                     // Whether the mode manager should terminate the running program on successful mode activation.
                     // The default is TRUE (stop).
                     OnSuccessStop:=TRUE,
                     // Whether the mode manager should terminate the running program on failed mode activation.
                     // The default is TRUE (stop).
                     OnFailStop:=FALSE,
                     // Whether to leave an alarm log in the controller on failed mode activation.
                     // The default is FALSE (No logs).
                     OnFailAlarm:=TRUE,
                     // User log alarm code.
                     // The default is 1.
                     AlarmCode:=1);

    ModeSet(Context:=iModeManager);
        Mode(Context:=iModeManager,
             // Mode identifier.
             Name:='modeselect',
             // Whether this is the default mode.
             // The default is FALSE (not default).
             Default:=TRUE);
            ModeUnit(Context:=iModeManager,
                     // The name of the program that is the mode unit.
                     Name:='DeviceIn',
                     // Whether to enable P_First_Run.
                     // The default is TRUE (enabled).
                     FirstRun:=TRUE,
                     // Whether to stop the program on failed mode activation.
                     // The default is TRUE (stop).
                     OnFailStop:=TRUE);
            ModeUnit(Context:=iModeManager, Name:='DeviceOut');
            ModeUnit(Context:=iModeManager, Name:='TelnetService');

        Mode(Context:=iModeManager, Name:='setup');
            ModeUnit(Context:=iModeManager, Name:='DeviceIn');
            ModeUnit(Context:=iModeManager, Name:='DeviceOut');
            ModeUnit(Context:=iModeManager, Name:='Machine1Setup');
            ModeUnit(Context:=iModeManager, Name:='Machine2Setup');
            ModeUnit(Context:=iModeManager, Name:='TelnetService');

        Mode(Context:=iModeManager, Name:='run');
            ModeUnit(Context:=iModeManager, Name:='DeviceIn');
            ModeUnit(Context:=iModeManager, Name:='DeviceOut');
            ModeUnit(Context:=iModeManager, Name:='Machine1Ctrl');
            ModeUnit(Context:=iModeManager, Name:='Machine2Ctrl');
            IF SPEC_USE_MQTT THEN
                ModeUnit(Context:=iModeManager, Name:='MqttMachineStatSender');
            END_IF;
            IF SPEC_USE_KINTONE THEN
                ModeUnit(Context:=iModeManager, Name:='KintoneClientService');
                ModeUnit(Context:=iModeManager, Name:='KintoneAlarmCollector');
            END_IF;

        Mode(Context:=iModeManager, Name:='diagnosis');
            ModeUnit(Context:=iModeManager, Name:='DeviceIn');
            ModeUnit(Context:=iModeManager, Name:='DeviceOut');
            ModeUnit(Context:=iModeManager, Name:='DiagnosisCtrl');
            ModeUnit(Context:=iModeManager, Name:='Machine1Diagnosis');
            ModeUnit(Context:=iModeManager, Name:='Machine2Diagnosis');
            ModeUnit(Context:=iModeManager, Name:='DiagnosisReporter');
            ModeUnit(Context:=iModeManager, Name:='TelnetService');
            IF SPEC_USE_MQTT THEN
                ModeUnit(Context:=iModeManager, Name:='MqttMachineStatSender');
            END_IF;
            IF SPEC_USE_KINTONE THEN
                ModeUnit(Context:=iModeManager, Name:='KintoneClientService');
                ModeUnit(Context:=iModeManager, Name:='KintoneAlarmCollector');
            END_IF;
    ModeSetClose(Context:=iModeManager);
    
    // Select the mode.
    ModeManager_selectMode(Context:=iModeManager, Name:=RUN_MODE);
END_IF;

// Activates the selected mode.
ModeManager_activate(Context:=iModeManager);

// When you output logs to the controller yourself.
(*
IF ModeManager_activate(Context:=iModeManager) THEN
    IF ModeManager_hasError(Context:=iModeManager) THEN
        ModeManager_getAlarmInfo(Context:=iModeManager,
                                 ErrorID=>iErrorID,
                                 RunMode=>iRunMode);
        SetAlarm(Code:=1, Info1:=iErrorID, Info2:=iRunMode);
    END_IF;
END_IF;
*)

In the controller task settings, set the program that contains the code to activate the mode to Enable, and the program that you want to start to Stop, as shown below.

Sample program task settings

POU/Program/ProgramLoader is the program POU that executes the mode activate POU.

If you set the RUN_MODE global variable to "diagnosis" and run it in the simulator, only the program in the mode specified as follows will be launched.

Task execution monitor when the sample program is started in diagnosis mode

If mode activation/deactivation fails, the following error occurs.

ErrorID Description
0x0000..0x0FFF The program specified in the mode unit Name does not exist.
0x2000 The program specified by Name in the mode manager does not exist.
0x3000..0x3FFF The program specified by the mode unit Name is not running.
0x4000..0x4FFF Failed to stop the program specified by the mode unit Name.
0x8000 The mode manager was initialized with a Name of an empty string.
0x81** Defined a mode whose Name is an empty string.
0x82** Defined a mode unit whose Name is an empty string.

If you have specified that an alarm log should be left when mode activation fails, a log of the specified alarm code will be left and the Error ID will be recorded in additional information 1.

Mode Deactivate Function

ModeManagerLib has a limited function to deactivate currently active modes. When using, please keep the following in mind.

  • Dependencies are not taken into account.
  • Deactivate in the same order as activated.
  • When a mode unit (program) in a mode fails to stop, subsequent mode units will not stop.

As long as you can determine that each is an independent program and can be stopped, there is no problem in deactivating the mode. To deactivate the mode, use the following POU, which is similar to ModeManager_activate.

FUNCTION ModeManager_deactivate(ModeManagerContext) : BOOL;

Below is an example of using ModeManager to dynamically switch programs in manual testing on an actual device. It monitors changes in the mode value and switches the running program.

CASE iState OF
    // STATE_INIT
    0:
        Clear(iRunMode);
        Clear(iPrevRunMode);
        
        ModeManager_init(Context:=iContext,
            Name:='Test_ManualTest',
            OnSuccessStop:=FALSE,
            OnFailStop:=FALSE,
            OnFailAlarm:=FALSE);
        
        ModeSet(iContext);
            // NOP is to not start any programs.
            Mode(Context:=iContext, Name:='NOP', Default:=TRUE);
            
            Mode(Context:=iContext, Name:='EdgeFunction');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_EdgeFunction');
            Mode(Context:=iContext, Name:='Rpc');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Rpc');
            Mode(Context:=iContext, Name:='Select');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Select');
            Mode(Context:=iContext, Name:='Insert');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Insert');
            Mode(Context:=iContext, Name:='BulkInsert_json');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_BulkInsert_json');
            Mode(Context:=iContext, Name:='BulkInsert_csv');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_BulkInsert_csv');
            Mode(Context:=iContext, Name:='Update');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Update');
            Mode(Context:=iContext, Name:='Upsert_json');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Upsert_json');
            Mode(Context:=iContext, Name:='Upsert_csv');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Upsert_csv');
            Mode(Context:=iContext, Name:='SingleUpsert_json');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_SingleUpsert_json');
            Mode(Context:=iContext, Name:='SingleUpsert_csv');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_SingleUpsert_csv');
            Mode(Context:=iContext, Name:='Delete');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Worker_Delete');            
            Mode(Context:=iContext, Name:='ServiceKeyAccess');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Check_ServiceKeyAccess');
            Mode(Context:=iContext, Name:='ServiceKeyAccessWithKey');
                ModeUnit(Context:=iContext, Name:='Test_ManualTest_Check_ServiceKeyAccessWithKey');
        ModeSetClose(iContext);
        
        iState := STATE_ACTIVE;
    
    // STATE_ACTIVE
    10:
        IF iRunMode <> iPrevRunMode
            AND iRunMode <> ''
        THEN
            iPrevRunMode := iRunMode;
            IF ModeManager_selectMode(
                   Context:=iContext,
                   Name:=iRunMode)
            THEN        
                Inc(iState);
            END_IF;
        END_IF;
    11:
        IF ModeManager_activate(Context:=iContext) THEN
            IF ModeManager_hasError(Context:=iContext) THEN
                iState := STATE_ERROR;
            ELSE
                Inc(iState);
            END_IF;
        END_IF;
    12:
        IF iRunMode <> iPrevRunMode THEN
            Inc(iState);
        END_IF;
    13:
        IF ModeManager_deactivate(
               Context:=iContext,
               IfActive:=TRUE)
        THEN
            iState := STATE_ACTIVE;
        END_IF;
END_CASE;

Operating Environment

The following environment is required to use this project.

Item Requirement
Sysmac Studio Latest recommended

Development Environment

This project was built in the following environment.

Item Version
Sysmac Studio Ver.1.63

Sample Program Usage Procesure

The sample program (POU/Program/ProgramLoader) is used in the following procedure.

  1. Modify the Task Settings
    Modify the task settings as follows.

    • Set the initial state of the POU Program/ProgramLoader in the PrimaryTask to Run
    • Set the initial state of the POU/Program/Services in the PrimaryTask to Stop
  2. Run the simulator
    Run the simulator and change to Program mode.

  3. Set the mode in the RUN_MODE global variable
    In the Watch window, set the startup mode to the RUN_MODE global variable.

  4. Change the simulator to operation mode
    Switch the simulator to operation mode and confirm that the program for the specified mode is running.

External Control Interface via OPC UA

ModeManagerLib contains a Function Block (FB) that exposes OPC UA nodes (control nodes) for controlling the ModeManager. By publishing this FB on an OPC UA server, you can use an OPC UA client to control the ModeManager.

The control nodes provide ModeManager operational functions beyond just mode definitions. This allows you to change modes without using the IDE and enables control from an application. Control node operations are protected by access control on both the controller and the OPC UA server. For a reference on control node operations, client examples and tests are available in the control/ directory.

When performing a mode change on an actual device via a control node, it may be necessary to publish additional information to evaluate whether the mode change is possible and if it has completed successfully, then evaluate it in conjunction with the result of the control node operation. The ModeManager itself only evaluates the operational status of the program POU. It does not evaluate whether a mode change is possible or if it was completed successfully. The result of a control node operation can only be evaluated as the mode status when the operational status of the program POU matches the mode status.

Operations via control nodes involve a wide range of content for both the client and the server. The complexity is further increased by the combination of stack-specific content and general OPC UA content. Therefore, the information provided here is insufficient. Be sure to check the manufacturer's manual for the controller's OPC UA server functionality. If you have a preferred OPC UA stack for the client, you can use it by referencing control/ModeManagerController.ps1.

Operating Environment

The following are required for external mode operations via OPC UA.

Item Requirement
Controller NX1 (Ver. 1.64 or later), NX5 (Ver. 1.64 or later), NX7 (Ver. 1.35 or later), NJ5 (Ver. 1.63 or later)
Sysmac Studio Ver. 1.62 or later

The following is required for using the client.

Item Requirement
PowerShell 7.5 or later

Development Environment

The client was built in the following environment.

Item Version
Controller NX102-9000 Ver. 1.64 HW Rev.A
Sysmac Studio Ver. 1.63
PowerShell 7.5.2
Pester 5.7.1

Client Composition

The client is composed of the following.

  • ModeManagerController.ps1
    The client's main script.
  • ModeManagerController.Tests.ps1
    Tests for the client using Pester and ModeManagerLib.smc2.
  • ModelTestController.ps1
    Operates the test POU (POU/Program/ModelTest) of ModeManagerLib.smc2.
  • PwshOpcUaClient/
    PwshOpcUaClient. For usage, refer to the PwshOpcUaClient repository.
  • Use-ModeManagerController.ps1
    Loads a series of assemblies, classes, and cmdlets into the session via dot sourcing.
  • New-ModeManagerController.ps1
    Defines a cmdlet to generate a client that holds PwshOpcUaClient.
  • Import-Env.ps1
    Defines a cmdlet to load a .env file into the session.

The following define cmdlets for single operations.

  • Activate-Mode.ps1
    Activates the specified mode.
  • Deactivate-Mode.ps1
    Deactivates the current mode.
  • Change-Mode.ps1
    Changes to the specified mode. If the current mode is active, it's deactivated, and then the specified mode is activated.
  • Get-RunMode.ps1
    Retrieves the current mode as a string. Returns an empty string if no mode is active.
  • Get-RunModeStat.ps1
    Retrieves the status of the current mode.

Client Usage Procedure

The general procedure for using the client is as follows.

  1. Set up PwshOpcUaClient
  2. Load PwshOpcUaClient and the client into the PowerShell session
  3. Execute the client code

Sample Program Usage Procedure

The sample program for external OPC UA operations is located in POU/Program/Services. It can be used with both the simulator and an actual device. The procedure for using it with the simulator is outlined below. If you're using an actual device, you'll need to authorize the client certificate on the OPC UA server.

Common Information

The sample program uses the simulator's OPC UA server and PwshOpcUaClient's OPC UA client. Therefore, if you use signing or signing and encryption during session establishment or message exchange with the server, the server and client may each reject the certificates. By trusting the rejected certificate, you won't be rejected on subsequent connections. If there are no issues with the certificate, perform the following operation.

  • When PwshOpcUaClient rejects the server certificate:
    If the server certificate is the one you intended, move it from PwshOpcUaClient/pki/rejected/certs to PwshOpcUaClient/pki/trusted/certs.

Creating a .env file with the parameters commonly used by the client and loading it into the PowerShell session reduces the burden of operation. Create a .env file like the one below and load it into the session when using the client.

# OPC UA server endpoint
OPC_UA_ENDPOINT=opc.tcp://localhost:4840
# User to access the OPC UA server
OPC_UA_CLIENT_USER=taker
# Password for the user
OPC_UA_CLIENT_PASSWORD=chocolatepancakes
# Node ID of the control node
MODE_MANAGER_NODE=ns=2;Programs.Services.ModeManager
# Separator for the control node (Simulator: '.', Controller: '/')
MODE_MANAGER_NODE_SEPARATOR=.

Usage Procedure with the Simulator's OPC UA Server

  1. Modify the Sysmac project
    Open the Sysmac project and perform the following:

    • In Task Settings, set the initial state of PrimaryTask's Services to Run.
    • In Task Settings, set the initial state of PrimaryTask's ProgramLoader to Stop.
    • In the OPC UA Server settings, under "Function Block Instance," confirm that Services\* is published with the Maintainer role.
  2. Start the simulator

  3. Set up PwshOpcUaClient
    This step is unnecessary if it's already set up. Run the following in PowerShell.

    ./PwshOpcUaClient/setup.ps1
  4. Configure the simulator's OPC UA server
    Register the following user on the simulator's OPC UA server and change the security settings.

    User Role
    taker Administrator

    Configure the simulator's OPC UA server as shown in the following image.

    Configure the simulator's OPC UA server

  5. Create the .env file
    Create a .env file based on your environment, as mentioned in the "Common Information" section.

  6. Execute the operation cmdlet
    Run the following in PowerShell.

    . ./Use-ModeManagerController.ps1
    Import-Env
    Activate-Mode -Mode setup

    If true is returned, the operation was successful. The specified mode will be activated, as shown in the following image.

    Activate Mode via OPC UA

License Information

Code that uses PwshOpcUaClient is under the GPLv2 license. All other code is under the MIT license.

About

Simple initialization system for NX/NJ in Sysmac Studio

Topics

Resources

License

Stars

Watchers

Forks