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.
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.
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.
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.
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;
The following environment is required to use this project.
Item | Requirement |
---|---|
Sysmac Studio | Latest recommended |
This project was built in the following environment.
Item | Version |
---|---|
Sysmac Studio | Ver.1.63 |
The sample program (POU/Program/ProgramLoader
) is used in the following procedure.
-
Modify the Task Settings
Modify the task settings as follows.- Set the initial state of the
POU Program/ProgramLoader
in thePrimaryTask
to Run - Set the initial state of the
POU/Program/Services
in thePrimaryTask
to Stop
- Set the initial state of the
-
Run the simulator
Run the simulator and change to Program mode. -
Set the mode in the RUN_MODE global variable
In the Watch window, set the startup mode to theRUN_MODE
global variable. -
Change the simulator to operation mode
Switch the simulator to operation mode and confirm that the program for the specified mode is running.
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
.
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 |
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 |
The client is composed of the following.
- ModeManagerController.ps1
The client's main script. - ModeManagerController.Tests.ps1
Tests for the client using Pester andModeManagerLib.smc2
. - ModelTestController.ps1
Operates the test POU (POU/Program/ModelTest
) ofModeManagerLib.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.
The general procedure for using the client is as follows.
- Set up PwshOpcUaClient
- Load PwshOpcUaClient and the client into the PowerShell session
- Execute the client code
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.
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 fromPwshOpcUaClient/pki/rejected/certs
toPwshOpcUaClient/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=.
-
Modify the Sysmac project
Open the Sysmac project and perform the following:- In Task Settings, set the initial state of
PrimaryTask
'sServices
to Run. - In Task Settings, set the initial state of
PrimaryTask
'sProgramLoader
to Stop. - In the OPC UA Server settings, under "Function Block Instance," confirm that
Services\*
is published with the Maintainer role.
- In Task Settings, set the initial state of
-
Start the simulator
-
Set up PwshOpcUaClient
This step is unnecessary if it's already set up. Run the following in PowerShell../PwshOpcUaClient/setup.ps1
-
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.
-
Create the .env file
Create a.env
file based on your environment, as mentioned in the "Common Information" section. -
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.
Code that uses PwshOpcUaClient is under the GPLv2 license. All other code is under the MIT license.