IEC 61131-3 インタプリタ Jier

Jier とは

Jierは、IEC 61131-3テキストをそのままインタプリタ実行できるツールです。PLCへ書き込まずに手元のPCでIEC 61131-3プログラムの動作を確認できます。

Jiecc--runオプション(jiecc --run)として呼び出します。

Windows 7以降、およびLinux(WSL) Ubuntu 22.04.5 LTS以降の64bit OSにて動作するコマンドライン版があります。

Jierのダウンロードと実行方法

Jieccのダウンロードページからjiecc.zip(Windows)またはjiecc.tgz(Linux)をダウンロードすると、jiecc.exe(またはjiecc)が含まれています。--runオプションでJierを呼び出せます。

IEC 61131-3テキストファイルを実行するには、次の形式でコマンドを入力します。

$ jiecc.exe --run <入力IEC 61131-3テキストファイルパス>
jiecc --run の基本形式(Windows)

例えば、次のIEC 61131-3テキストファイルを実行すると:

program __main__
var
	str : string;
end_var
{st}
str := 'Hello, world!';
{end}
end_program
hello.txt — 最小実行例
$ jiecc.exe --run .\hello.txt --verbose
cycle 0: {en: True, eno: True, str: 'Hello, world!'}

--- Interpreter stopped after 1 cycles ---
実行結果(stdout)— --verbose 指定時

デフォルトでは変数状態の出力は行われません。--verboseオプションを指定すると、スキャンサイクルごとに cycle N: {変数名: 値, ...} の形式で変数の状態が出力されます。 program __main__ はデフォルトで 1 スキャンサイクルのみ実行して終了します(詳細は後述)。

jiecc.exe --versionで、バージョンを確認できます。

実行モデル

Jierには、IEC 61131-3プログラムの書き方に応じて2つの実行モードがあります。

program __main__ による簡易実行

configurationブロックを書かず、program __main__という名前のプログラムPOUを定義すると、1スキャンサイクルだけ実行して終了します。手軽に動作を確認したい場合に使用します。

// counter.txt
program __main__
var
	count : dint := 0;
end_var
{st}
count := count + 1;
{end}
end_program
counter.txt — program __main__ の例
$ jiecc.exe --run .\counter.txt --verbose
cycle 0: {en: True, eno: True, count: 1}

--- Interpreter stopped after 1 cycles ---
1 スキャンサイクル実行(--verbose 指定時)

出力に含まれる en(enable 入力)・eno(enable 出力)は、IEC 61131-3規格のPOUが持つ標準変数です。--verboseを省略した場合、変数状態は出力されません。

configuration によるスキャンサイクル実行

configurationブロックでリソース・タスク・プログラムインスタンスを定義すると、タスクのintervalで指定した周期でスキャンサイクルが繰り返し実行されます。Ctrl+Cで停止します。

var_globalを使うと、複数のプログラム間でデータを共有できます。

// scan_counter.txt
program Counter
var_external
	g_count : dint;
end_var
{st}
g_count := g_count + 1;
{end}
end_program

configuration MainConfig
	resource MainRes on plc
		var_global
			g_count : dint;
		end_var
		task tsk(interval := t#100ms, priority := 1);
		program counter_prog with tsk: Counter();
	end_resource
end_configuration
scan_counter.txt — configuration を使ったスキャンサイクル実行の例
$ jiecc.exe --run .\scan_counter.txt --verbose
cycle 0: {g_count: 1, en: True, eno: True}
cycle 1: {g_count: 2, en: True, eno: True}
cycle 2: {g_count: 3, en: True, eno: True}
...
^C
--- Interpreter stopped after 3 cycles ---
スキャンサイクル実行(--verbose 指定時、Ctrl+C で停止)

--verbose使用時のスキャンサイクル出力には、var_globalで宣言したグローバル変数と、最後に登録されたプログラムインスタンスのローカル変数が含まれます。

以下に、2つのプログラムがグローバル変数を共有する例を示します。

// producer_consumer.txt
program Producer
var_external
	shared : dint;
end_var
{st}
shared := shared + 10;
{end}
end_program

program Consumer
var
	observed : dint;
end_var
var_external
	shared : dint;
end_var
{st}
observed := shared;
{end}
end_program

configuration MainConfig
	resource MainRes on plc
		var_global
			shared : dint;
		end_var
		task tsk(interval := t#100ms, priority := 1);
		program producer_prog with tsk: Producer();
		program consumer_prog with tsk: Consumer();
	end_resource
end_configuration
producer_consumer.txt — var_global による複数プログラム間のデータ共有
$ jiecc.exe --run .\producer_consumer.txt --verbose
cycle 0: {shared: 10, en: True, eno: True, observed: 10}
cycle 1: {shared: 20, en: True, eno: True, observed: 20}
cycle 2: {shared: 30, en: True, eno: True, observed: 30}
...
^C
--- Interpreter stopped after 3 cycles ---
Producer が更新した shared を Consumer が読み取る(--verbose 指定時)

デバッグ機能

--verbose — サイクル状態ダンプ

--verboseを指定すると、スキャンサイクルごとに変数の状態(cycle N: {...})と実行サマリー(--- Interpreter stopped after N cycles ---)を標準出力(stdout)に出力します。デフォルトはoffです。

$ jiecc.exe --run .\counter.txt --verbose
cycle 0: {en: True, eno: True, count: 1}

--- Interpreter stopped after 1 cycles ---
--verbose 指定時の出力

出力形式は cycle N: {変数名: 値, ...} です。STRING型変数はIEC 61131-3の$エスケープ形式でシングルクォートで表示されます(例: 'Hello$NWorld')。

--trace — ステートメント実行トレース

--traceオプションは引数なしまたはPOU名を指定して使用します。

jiecc --run hello.txt --trace               # 全PROGRAMをトレース
jiecc --run hello.txt --trace main          # 'main' POU のみトレース
--trace の使い方

フィルタの一致対象: POU名のみ(大文字小文字を区別しない)。フィルタに一致するPROGRAMの各トップレベルステートメントが実行されるたびに、ステートメントの種類と結果をstderrに出力します。同一POUを複数インスタンス化した場合、全インスタンスがまとめてトレースされます(TRACE注入はPOU定義体レベルで行われるため)。フィルタが存在するPOUに一致しない場合はエラー終了します(終了コード 1)。

次のプログラムを例に説明します。

// trace_demo.txt
program __main__
var
	count : dint;
end_var
{st}
count := count + 1;
count;
puts(dint_to_string(count));
{end}
end_program
trace_demo.txt — --trace の例(代入文・変数参照・関数呼び出し文)
$ jiecc.exe --run .\trace_demo.txt --trace
実行コマンド(stdout / stderr は別ストリーム)

標準出力(stdout):

1
stdout — puts の出力

トレースログ(stderr):

[TRACE cy=0 prog=__main__] ASSIGN -> NIL
[TRACE cy=0 prog=__main__] PLACE -> 1
[TRACE cy=0 prog=__main__] POUCALL -> True
stderr — トレースログ(1サイクル、3ステートメント)

ステートメント種別と結果値の読み方:

トレース行の形式は次の通りです。

[TRACE cy=<サイクル番号> prog=<プログラム名>] <ステートメント種別> -> <結果値>
トレース行のフォーマット

--watch — グローバル変数の変化監視

--watchオプションに監視するVAR_GLOBAL変数名をカンマ区切りで指定すると、その変数への書き込みが発生したサイクルをstderrに出力します。*を指定するとリソース内の全VAR_GLOBALを一括監視します。

jiecc --run hello.txt --watch g_count          # 特定の変数を監視
jiecc --run hello.txt --watch g_count,g_status # 複数の変数をカンマ区切りで指定
jiecc --run hello.txt --watch *                # 全VAR_GLOBALを監視
--watch の使い方

スキャンサイクル出力(stdout)の例(--verbose併用時):

cycle 0: {g_count: 1, en: True, eno: True}
cycle 1: {g_count: 2, en: True, eno: True}
cycle 2: {g_count: 3, en: True, eno: True}

--- Interpreter stopped after 3 cycles ---
stdout(--verbose 指定時)

watchログ(stderr)の例(--watch g_count 指定時):

[WATCH cy=0 global] g_count: 0 -> 1
[WATCH cy=1 global] g_count: 1 -> 2
[WATCH cy=2 global] g_count: 2 -> 3
stderr — watchログ

watch行の形式は次の通りです。

[WATCH cy=<サイクル番号> global] <変数名>: <変更前の値> -> <変更後の値>
watchログ行のフォーマット

--watchで監視できるのはVAR_GLOBAL(CONFIGURATIONのvar_global)のスカラ変数のみです。プログラムローカル変数・配列要素・構造体フィールドへの書き込みは監視対象外です。なお、発火条件は値の変化ではなく書き込み発生時です(同値再代入でも出力されます)。

PUTS — 組み込み出力関数

putsはIEC 61131-3プログラム内からデバッグ出力を行う組み込み関数です。stdpouに自動的に含まれているため、宣言なしで使用できます。

program __main__
var
	counter : dint := 0;
end_var
{st}
counter := counter + 1;
puts(dint_to_string(counter));
puts('loop done');
{end}
end_program
puts の使用例
$ jiecc.exe --run .\example.txt
1
loop done
実行結果(stdout)

出力先は標準出力(stdout)です。サイクル出力(cycle N: {...})より先に書き出されます。戻り値はbool型(常にTRUE)。

Python POU({foreign}

Jierは{foreign 'モジュール.クラス名'}プラグマを使って、Pythonで実装したクラスをPOUとして使用できます。これにより、テスト用モック・アルゴリズムの段階的実装・既存Pythonライブラリの活用が可能です。

Pythonで実装するクラスのインタフェースは次の通りです。

# my_pou.py
class Multiply:
    """FUNCTION の Python 実装例(無状態)"""
    def execute(self, a, b):
        return a * b

class Accumulator:
    """FUNCTION_BLOCK の Python 実装例(有状態)"""
    def __init__(self):
        self._total = 0

    def execute(self, add_val, reset):
        if reset:
            self._total = 0
        self._total += add_val
        return {'total': self._total}
my_pou.py — Python 実装クラスの例
// IEC 側の宣言
function {foreign 'my_pou.Multiply'} Multiply : dint
var_input
    a : dint;
    b : dint;
end_var
end_function

function_block {foreign 'my_pou.Accumulator'} Accumulator
var_input
    add_val : dint;
    reset   : bool;
end_var
var_output
    total : dint;
end_var
end_function_block
IEC 側の宣言({foreign} pragma)

FUNCTIONの場合はexecute()がスカラ値を返し、FUNCTION_BLOCKの場合は出力変数名をキーとするdictを返します。インスタンスの状態(インスタンス変数)は自動的に保持されるため、FBのスキャンサイクル間でデータを維持できます。

セキュリティ上の注意: {foreign}は任意のPythonモジュールを読み込んで実行します。信頼できるソースのSTプログラムのみを実行してください。

コマンドラインオプション

Jierはjiecc --runとして呼び出します。--runに加えて、プリプロセス関連オプション(-D-I-E等)やその他のオプションが使用できます。オプションの詳細はJieccのオプション一覧を参照してください。

Jier固有オプション

オプション説明
--max-cycles N 最大スキャンサイクル数を指定します。program __main__使用時のデフォルトは1、CONFIGURATIONあり時のデフォルトはCtrl+Cで停止するまで無制限です。
--trace [FILTER] PROGRAMのステートメントが実行されるたびに [TRACE] 行をstderrに出力します。FILTERを省略すると全PROGRAMが対象。FILTERにはPOU名(同一POUの全インスタンスがまとめてトレースされます)を指定できます。一致するPOUが存在しない場合はエラー終了します。
--watch VAR,... 監視するVAR_GLOBAL変数名をカンマ区切りで指定します。変数への書き込み時に [WATCH] 行としてstderrに出力します。* を指定するとリソース内の全VAR_GLOBALを一括監視します。
--ir-audit warn|fail IRコントラクト検査モードを指定します。warn(デフォルト): 違反をstderrに警告して実行継続。fail: 違反で例外を発生させます。
--strict-ir --ir-audit=failのエイリアスです。
--verbose 各スキャンサイクルの変数状態ダンプ(cycle N: {...})と実行サマリーを標準出力に表示します。デフォルトはoffです。

Jierオプション一覧

Jier 7.1.0のオプション一覧は以下の通りです。

usage: jier.py [-h] [--version] [--output OUTPUT] [--define-macro DEFINE_MACRO] [--syspath SYSPATH] [--target TARGET] [--line-separator LINE_SEPARATOR]
               [--retro_caps] [--max-include-depth MAX_INCLUDE_DEPTH] [--max-expansion-depth MAX_EXPANSION_DEPTH] [--max-if-nesting MAX_IF_NESTING]
               [--recursion-limit RECURSION_LIMIT] [--preprocess-only] [--max-cycles MAX_CYCLES] [--trace [FILTER]] [--watch WATCH] [--ir-audit {warn,fail}]
               [--strict-ir] [--verbose] [--pp-output-pragma-style PP_OUTPUT_PRAGMA_STYLE] [--remove-comments] [-dM] [-dD] [--silent]
               [--undefine-macro UNDEFINE_MACRO] [-include FORCE_INCLUDE] [-w] [-Werror] [-P] [-M] [-MM] [-MD] [-MMD] [-MF MF] [-MT MT] [--set SET]
               [filepath ...]

Jier is an IEC 61131-3 interpreter.

It interprets and executes IEC 61131-3 programs in scan cycle mode.

Typical usage:
    jier <IEC 61131-3 text filepath>

For additional details, visit:
https://www.graviness.com/iec_61131_3/jier.html

positional arguments:
  filepath              the IEC 61131-3 text file path.

options:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  --output, -o OUTPUT   the output file path for preprocessed output (used with -E). Default: STDOUT (outputs to standard output.)
  --define-macro, -D DEFINE_MACRO
                        define a macro. -D <macro> to set <macro> to 1 or -D<macro>=<value> to assign a specific value. e.g. -D DEBUG, -D V=10.
  --syspath, -I SYSPATH
                        add the directory SYSPATH to the list of directories to be searched for (s)include files.
  --target, -t TARGET   conversion target. e.g. OMRON, KEYENCE, MITSUBISHI, CODESYS. Default: STD.
  --line-separator, -N LINE_SEPARATOR
                        newline code for the output. e.g. LF, CRLF, CR. Default: LF.
  --retro_caps          output IEC 61131-3 keywords in uppercase.
  --max-include-depth MAX_INCLUDE_DEPTH
                        the maximum depth of the nested #include. The default is 100.
  --max-expansion-depth MAX_EXPANSION_DEPTH
                        the maximum macro expansion depth. The default is 256.
  --max-if-nesting MAX_IF_NESTING
                        the maximum #if/#elif nesting depth. The default is 256.
  --recursion-limit RECURSION_LIMIT
                        the maximum recursion depth allowed by the program. Applied to both Python and jiepp.exe. The default is the system default value.
  --preprocess-only, -E
                        preprocessed only.; do not convert.
  --max-cycles MAX_CYCLES
                        maximum number of scan cycles to execute. Default: 1 cycle when program __main__ is defined without a configuration; unlimited (runs until Ctrl+C) otherwise.
  --trace [FILTER]      emit [TRACE] lines to stderr. Default: off. Without FILTER, traces all PROGRAMs. FILTER: POU name to trace (all instances of that POU are traced together), or resource name (emit [TRACE] lines for all VAR_GLOBAL changes in that resource).
  --watch WATCH         comma-separated list of VAR_GLOBAL/VAR_EXTERNAL variable names to watch. Use * to watch all VAR_GLOBAL in the resource. Emits a [WATCH] line to stderr when a watched variable changes.
  --ir-audit {warn,fail}
                        IR contract audit mode. warn: report violations to stderr and continue. fail: raise exception on violations. Default: warn.
  --strict-ir           alias for --ir-audit=fail. Enable strict IR validation.
  --verbose             show cycle state dump (cycle N: {...}) and run summary. Default: off.
  --pp-output-pragma-style PP_OUTPUT_PRAGMA_STYLE
                        The format of preprocessor output pragmas. Possible values: annotated (*{~}*) or standard {~}. Default: annotated.
  --remove-comments, -nC
                        remove comments in preprocessed output. The default is False.
  -dM                   generate a list of #define directives for all the macros defined during the execution of the preprocessor, including predefined macros. must be invoked with -E.
  -dD                   emit {#define}/{#undef} directives inline in preprocessed output. must be invoked with -E.
  --silent              activate silent mode to suppress error messages.
  --undefine-macro, -U UNDEFINE_MACRO
                        undefine a macro. Processed after -D (explicit undefine overrides define).
  -include FORCE_INCLUDE
                        force-include FILE before processing input (as if #include "FILE" appeared first).
  -w                    suppress all preprocessor warnings.
  -Werror               promote preprocessor warnings to errors.
  -P                    suppress line markers in preprocessed output.
  -M                    output Makefile dependency rules (requires -E).
  -MM                   output Makefile dependency rules, excluding system includes (requires -E).
  -MD                   write dependency rules to .d file; keep normal output. Mutually exclusive with -M/-MM.
  -MMD                  like -MD but exclude system includes. Mutually exclusive with -M/-MM.
  -MF MF                write dependency rules to FILE (requires -M, -MM, -MD, or -MMD).
  -MT MT                set the target name in dependency rules (requires -M, -MM, -MD, or -MMD).
  --set SET             set and lock options. e.g. creation-datetime=1999-12-31T23:59:59.999+11:59.
Jier 7.1.0 オプション一覧

サポートされている標準関数

Jierは以下の標準関数をサポートしています。関数名は大文字・小文字を区別しません(IEC 61131-3仕様に準拠)。

文字列関数

関数名戻り値型説明
concat(s1, s2, ... s16)string最大16個の文字列を連結します。
len(IN)dint文字列の文字数を返します。
left(IN, L)string文字列の左側 L 文字を返します。
right(IN, L)string文字列の右側 L 文字を返します。
mid(IN, L, P)string位置 P から L 文字分の部分文字列を返します。
delete(IN, L, P)string位置 P から L 文字削除した文字列を返します。
find(IN1, IN2)dintIN1 内で IN2 が最初に現れる位置を返します(見つからない場合は0)。
insert(IN1, IN2, P)stringIN1 の位置 P に IN2 を挿入した文字列を返します。
replace(IN1, IN2, L, P)stringIN1 の位置 P から L 文字を IN2 で置換した文字列を返します。

数値・算術関数

関数名戻り値型説明
abs(IN)lreal絶対値
sqrt(IN)lreal平方根
ln(IN)lreal自然対数
exp(IN)lreal自然指数(e^IN)
sin(IN)lrealサイン(ラジアン)
cos(IN)lrealコサイン(ラジアン)
tan(IN)lrealタンジェント(ラジアン)
asin(IN)lrealアークサイン
acos(IN)lrealアークコサイン
atan(IN)lrealアークタンジェント
expt(IN, EXPONENT)lreal冪乗(IN ^ EXPONENT)
trunc(IN)lint小数部を切り捨て整数化
min(IN1, IN2)lreal最小値
max(IN1, IN2)lreal最大値

ビット演算関数

関数名戻り値型説明
shr(IN, N)dint右論理シフト
shl(IN, N)dint左論理シフト
ror(IN, N)dint右循環シフト
rol(IN, N)dint左循環シフト

配列関数

関数名戻り値型説明
lower_bound(IN, DIM)dint配列の次元 DIM の下限インデックスを返します。
upper_bound(IN, DIM)dint配列の次元 DIM の上限インデックスを返します。

型変換関数(抜粋)

関数名戻り値型説明
dint_to_string(IN)stringdint を文字列に変換
udint_to_string(IN)stringudint を文字列に変換
lint_to_string(IN)stringlint を文字列に変換
ulint_to_string(IN)stringulint を文字列に変換
lreal_to_string(IN)stringlreal を文字列に変換
bool_to_string(IN)stringbool を文字列に変換
lword_to_string(IN)stringlword を文字列に変換
dword_to_string(IN)stringdword を文字列に変換
dint_to_lreal(IN)lrealdint を lreal に変換
lreal_to_dint(IN)dintlreal を dint に変換(切り捨て)
dint_to_lint(IN)lintdint を lint に変換
lint_to_dint(IN)dintlint を dint に変換
bool_to_dint(IN)dintbool を dint に変換(false=0, true=1)
dint_to_bool(IN)booldint を bool に変換(0=false, その他=true)

その他の型変換関数(int/sint/uint/usint/real/udint/dword/byte/word 等を含む多数のX_TO_Y関数)についても対応しています。

時間関数

関数名戻り値型説明
time_to_usec_dint(IN)dinttime値をマイクロ秒(dint)に変換
date_to_string(IN)stringdate値を文字列(YYYY-MM-DD形式)に変換
tod_to_string(IN)stringtime_of_day値を文字列(HH:MM:SS形式)に変換
dt_to_string(IN)stringdate_and_time値を文字列に変換

Jierの仕様

JierのIEC 61131-3言語要素のサポート状況は下表の通りです。

サポート要素一覧
機能サポート状況
ST(構造化テキスト)言語Yes
LD(ラダー図)/ FBD / SFC / IL 言語未サポート
program / function_block / function pouYes
configuration / resource / taskYes
var / var_input / var_output / var_in_out / var_tempYes
var_global(configurationのvar_global)Yes
var_externalYes
基本型: 符号有り整数型; sint, int, dint, lintYes
基本型: 符号無し整数型; usint, uint, udint, ulintYes
基本型: 実数型; real, lrealYes
基本型: ビット列型; byte, word, dword, lwordYes
基本型: boolYes
基本型: stringYes
基本型: time, date, time_of_day, date_and_time, ltimeYes
ユーザー定義型: struct(構造体)Yes
ユーザー定義型: enum(列挙型)Yes
配列(array); 多次元・非ゼロ下限含むYes
再帰関数(直接・相互再帰)Yes
en / enoYes
形式パラメータ呼び出し(ファンクションの名前付き引数)Yes
プリプロセス機能(-D / -I / {#include} 等)Yes
物理I/Oデバイス(PLC入出力変数)未サポート
意味解析レベルの型検査限定的

ST言語のプログラムコードはJieccと同様に{st}〜{end}プラグマで囲んで記述します(Jieccの記述方法参照)。

Jierは物理的なPLCのI/Oポート(入力変数・出力変数のハードウェアマッピング)には対応していません。ソフトウェアロジックのみのシミュレーションを行います。