IEC 61131-3 ST言語

はじめに

IEC 61131-3

IEC 61131-3は、FA(Factory Automation)のプログラミング言語に関する国際規格です。次の特徴があります。

  1. 論理型・整数型・実数型に加え、ビット列型・時間型・時刻型・列挙型・構造体型などの豊富なデータ型
  2. Java™言語ライクなオブジェクト指向もできるマルチパラダイムプログラミング言語
  3. テキスト言語ST、グラフィカル言語LD・FBD・SFCの4言語を定義 ※本サイトでは、ST言語を中心に扱います。
  4. 静的型付けと動的型付けの混在
  5. 動的メモリ確保なし
  6. 物理デバイスのメモリにアクセスする構文
  7. サイクリック実行があることを前提とする言語要素

一方、安全性を重視しているため、関数の再帰呼出しを制限していたり、関数ポインタを含むポインタ型を代替する機能が制約されていたりと機能的な自由度は高くありません。「APPENDIX. IEC 61131-3にないもの」も参照ください。

このページの目的

日本のFAにおけるIEC 61131-3の浸透は十分とは言えません。従来のアドレスプログラミングやラダーによる巻物スタイルのプログラミングは根強く残っており、IEC 61131-3が良いだろうことは感じていても、約20年もの間、IEC 61131-3への移行は進んでいません。

これには、IEC 61131-3規格側の問題、PLCメーカー側の問題、およびユーザー側の様々な事情があります。

  1. ユーザー側に非IEC 61131-3の膨大な資産があり、IEC 61131-3に切り替えてしまうと教育も含め、生産性が急激に下がる。
  2. IEC 61131-3のプログラムを実行したり試してみる手頃な環境がなく、導入を検討する人や学ぼうとする人の機会損失している。
  3. IEC 61131-3仕様の理解が困難であり、PLCメーカーのIEC 61131-3に対する準拠度合いも高くなく、やりたいことができるのか分からない。
  4. IEC 61131-3を活用したときの正しい設計・実装に関する疑問や問題を解消する主な手段がメーカー側の偏った情報(セミナー、マニュアル、電話)しかない。

これらの問題が解消されることで、IEC 61131-3の導入障壁が下がり、IEC 61131-3のレベルが全体的に底上げされていくものと考えます。

本サイトは、上記4.の一助にするために作りました。IEC 61131-3の正しい詳細情報であったり、何ができるのか・できないのかといった情報を提供します。

このページの範囲

IEC 61131-3 3rd Edition.を範囲とします。

基本

プログラミング言語入門で最初の例として示されるHello worldのプログラムです。

PROGRAM Main
	VAR text: STRING[128]; END_VAR

	text := 'Hello, world!';
END_PROGRAM

IEC 61131-3に標準入出力の定義はないため、文字列型変数に代入しているだけにしています。文字列型変数をモニタすることでHello, world!を確認してください。このプログラムを実行するためには、実行するプログラム(例では、MainプログラムPOU)をタスクに割り付け、ビルドし、PLCにダウンロードする必要があります。

プログラムの実行とタスク

特定の関数から始まるといったエントリ関数のようなものはありません。複数のプログラムインスタンスがタスクの優先度に従い並列実行されます。。。これが、IEC 61131-3の規定ですが、各PLCメーカーの実装はそうではありません。優先度の高い一つのタスク、もしくはそのPLCメーカーにとって"メインのタスク"があり、そのタスクに割り付けたプログラムインスタンスがタスクごとに上から順番に実行されます。

プログラムの実行とタスクの実装
IEC 61131-3 PLCメーカーの実装
タスク割付
プログラム1 WITH 定周期タスク1(優先度高)
プログラム2 WITH 定周期タスク1(優先度高)
プログラム3 WITH 定周期タスク2(優先度低)
プログラム4 WITH 定周期タスク2(優先度低)
プログラム5 WITH 定周期タスク3(優先度低)
プログラム6 WITH 定周期タスク3(優先度低)
プログラム7 WITH イベントタスク1
プログラム8 WITH イベントタスク2
定周期タスク1(優先度高):
  - プログラム1
  - プログラム2
定周期タスク2(優先度低):
  - プログラム3
  - プログラム4
定周期タスク3(優先度低):
  - プログラム5
  - プログラム6
イベントタスク1:
  - プログラム7
イベントタスク2:
  - プログラム8
動作 どの定周期タスクのプログラムが最初に実行されるかは不確定。 プログラム1、3、5のいずれかが最初に実行される。

プログラムインスタンスには、プログラム間でデータをやりとりするための引数があります。。。これが、IEC 61131-3の規定ですが、各PLCメーカーの実装はそうではありません。プログラム間でデータをやりとりするには、グローバル変数を使用します。

コメント

ST言語には、1つの行コメントと、2つの複数行コメントがあります。

RightsideIsLinecomment := TRUE; // これは行コメントです。

(* これは
複数行コメント
です。 *)
AbovesideIsBlockcomment := TRUE;

RightsideIsAnotherBlockcomment := TRUE; /* これは
もう一つの
複数行コメント
です。 */

文字集合・エンコーディング

プログラムやコメントで使用できる文字集合は、ISO/IEC 10646に従います。エンコーディングは、各メーカーの実装依存です。

ISO/IEC 10646を符号化文字集合と解釈するか、文字符号化方式と解釈するかで違ってきますが、規格において、"Character set"の項に記載あるため、ここでは前者と解釈します。

キーワード

キーワードは、プログラム全体を構成するための文字列です。大文字・小文字を区別しない、キーワードを識別子として使用できない、などの特性があります。

IEC 61131-3規格にキーワードの具体的な定義はなく、構文規則から推測されるキーワードは次のすべての文字列です。※1

ABSTRACT, ACTION, AND, ARRAY, AT, BOOL, BY, BYTE, CASE, CHAR, CLASS, CONFIGURATION, CONSTANT, CONTINUE, DATE, DATE_AND_TIME, DINT, DO, DT, DWORD, ELSE, ELSIF, END_ACTION, END_CASE, END_CLASS, END_CONFIGURATION, END_FOR, END_FUNCTION, END_FUNCTION_BLOCK, END_IF, END_INTERFACE, END_METHOD, END_NAMESPACE, END_PROGRAM, END_REPEAT, END_RESOURCE, END_STEP, END_STRUCT, END_TRANSITION, END_TYPE, END_VAR, END_WHILE, EXIT, EXTENDS, FINAL, FOR, FROM, FUNCTION, FUNCTION_BLOCK, F_EDGE, IF, IMPLEMENTS, INITIAL_STEP, INT, INTERFACE, INTERNAL, LDATE, LDATE_AND_TIME, LDT, LINT, LREAL, LTIME, LTIME_OF_DAY, LTOD, LWORD, METHOD, MOD, NAMESPACE, NON_RETAIN, NOT, NULL, OF, ON, OR, OVERLAP, OVERRIDE, PRIVATE, PROGRAM, PROTECTED, PUBLIC, READ_ONLY, READ_WRITE, REAL, REF, REF_TO, REPEAT, RESOURCE, RETAIN, RETURN, R_EDGE, SINT, STEP, STRING, STRUCT, SUPER, TASK, THEN, THIS, TIME, TIME_OF_DAY, TO, TOD, TRANSITION, TYPE, UDINT, UINT, ULINT, UNTIL, USING, USINT, VAR, VAR_ACCESS, VAR_CONFIG, VAR_EXTERNAL, VAR_GLOBAL, VAR_INPUT, VAR_IN_OUT, VAR_OUTPUT, VAR_TEMP, WCHAR, WHILE, WITH, WORD, WSTRING, XOR

※1 IEC 61131-3規格上は、IL演算子のCALもキーワードと読み取れますが、本書では上記にIL演算子を含めていません。

識別子

識別子は、変数やデータ型などを識別するための名前であり、次のすべてを満たさなければいけません。

  1. 文字・数字・アンダースコアで構成される。
  2. 数字で始まってはいけない。
  3. アンダースコアで終端してはいけない。
  4. 連続するアンダースコアを含んではいけない。
  5. キーワードではない。

例えば、`abc`、`D2E_F3`、`_hi_jk_lmn`は識別子になりえますが、`5op`、`__qr`、`s__t`、`uvw_`は識別子ではありません。

また、識別子の大文字・小文字を区別しません。`Abc`、`abc`、`ABC`は同じ識別子と解釈されます。

パス

パス、又はパス文字列は、以下の要素で登場する%で始まる文字列です。

プラグマ

プラグマは、{ ~ }で囲われた要素であり、IEC 61131-3で規定しない要素のために使用します。個々のプラグマの仕様は、IEC 61131-3規格では定義しておらず、IEC 61131-3実装者が定義します。

プラグマは、コメントや文字列リテラルを除く様々な場所に登場できると規定されており、以下の例のように「警告を無視する」「要素のドキュメントを埋め込む」「変数に追加属性を与える」「コンパイラの設定を変更する」など様々な用途での活用が想定されます。

{disable-warning: *}

{document: mainプログラムは装置全体を制御します。}
PROGRAM main
    VAR
        v: LINT {register};
    END_VAR
    {line: 1}
    :
END_PROGRAM

プラグマに関するIEC 61131-3の規定については、上述した程度の言及しかなく次に示すことは実装依存です。

※1 文字列リテラルと同じく$でエスケープするのが受け入れられやすいと考えます。
※2 未知のプラグマについては、無視してもコンパイルエラーとならず、プログラムの意味が変わらない範囲での活用が望ましいと考えます。

変数・基本データ型・リテラル

(準備中)

基本データ型

下表に示す通り、整数値、実数値、ビット列値、真偽値、時間、時間軸上の点、文字、文字列など多くの基本データ型があります。

基本データ型の一覧
データ型キーワードビット幅範囲デフォルト初期値リテラル※1の例説明
符号あり整数型SINT8-128~1270-59,
13,
1_234_567,
2#11_01,
-16#2f
INT16-32768~32767
DINT32-2147483648~2147483647
LINT64-9223372036854775808~9223372036854775807
符号なし整数型USINT80~255
UINT160~65535
UDINT320~4294967295
ULINT640~18446744073709551615
浮動小数点型REAL32IEC 60559単精度浮動小数点規格の範囲に従う00.1,
1.0e-6,
-1e3,
-5,
16#ff
LREAL64IEC 60559倍精度浮動小数点規格の範囲に従う
ビット列型BYTE816#00~16#ff16#00BYTE#16#ff,
BYTE#2#1111_1111_1111_1111,
DWORD#16#cafe_babe
WORD1616#0000~16#ffff16#0000
DWORD3216#0000_0000~16#ffff_ffff16#0000_0000
LWORD6416#0000_0000_0000_0000~16#ffff_ffff_ffff_ffff16#0000_0000_0000_0000
ブール型BOOL1FALSE(偽)、又はTRUE(真)FALSETRUE,
BOOL#0
時間型TIME実装依存実装依存0秒TIME#1s,
T#1s,
LTIME#1s,
LT#1s,
LT#1d2h3m4s5ms6us7ns,
LT#1.234567ms,
LT#1m234us567ns
最小単位も含めて実装依存のため、TIME型ではなく、LTIME型の使用を推奨します。
LTIME64-9223372036854775808ナノ秒~9223372036854775807ナノ秒
最小単位はナノ秒
日付型DATE実装依存実装依存実装依存 e.g. 1970年1月1日DATE#1978-9-7,
D#2022-11-07,
LDATE#1978-9-7,
LD#2554-07-21
LDATE641970年1月1日~2554年7月21日
最小単位は1日
1970年1月1日 [範囲]に示す上限は、規格に示される最小単位ナノ秒という言及をベースに64ビット幅で計算したものです。
時刻型TIME_OF_DAY実装依存実装依存実装依存 e.g. 0時0分0秒TIME_OF_DAY#23:59:59,
TOD#23:59:59,
LTIME_OF_DAY#23:59:59.999_999_999,
LTOD#23:59:59.999999999
TOD
LTIME_OF_DAY640時0分0秒~23時59分59.999999999秒
最小単位はナノ秒
0時0分0秒
LTOD
日付時刻型DATE_AND_TIME実装依存実装依存実装依存 e.g. 1970年1月1日0時0分0秒DATE_AND_TIME#1970-1-1-23:59:59,
DT#1970-01-01-23:59:59,
LDATE_AND_TIME#2022-11-07-23:59:59.999_999_999,
LDT#2554-07-21-23:34:33.709551615
DT
LDATE_AND_TIME641970年1月1日0時0分0秒~2554年7月21日23時34時33.709551615s
最小単位はナノ秒
1970年1月1日0時0分0秒
LDT
文字型CHAR8ISO/IEC 10646規格に従う一文字。NULL文字('$00''a',
'→',
'$00' // NULL文字,
文字符号化方式は、UTF-8。
WCHAR16ISO/IEC 10646規格に従う一文字。NULL文字("$0000""a",
"→",
"$0000" // NULL文字
文字符号化方式は、UTF-16。
文字列型STRING8*NISO/IEC 10646規格に従う0文字以上の文字の列。空文字列('''This is a STRING literal.',
'これは文字列リテラルです。',
'abc',
'$61$62$63' // = 'abc',
'' // 空文字列
文字符号化方式は、UTF-8。
WSTRING16*NISO/IEC 10646規格に従う0文字以上の文字の列。空文字列("""This is a STRING literal.",
"これはWSTRING型のリテラルです。",
"abc",
"$0061$0062$0063" // = 'abc',
"" // 空文字列
文字符号化方式は、UTF-16。

※1 リテラルは、値のプログラミング言語における表現です。例えば、IEC 61131-3の真偽値である「真」の値の表現は、「TRUE」や「BOOL#1」があります。

整数型

(準備中)

実数型

(準備中)

ビット列型

ビット列型は、コンピュータサイエンスにおけるビット列(0と1のみから構成されるデータの列)を表現する型です。

表現できるビット数の違いにより、8ビットのBYTE型、16ビットのWORD型、32ビットのDWORD型、および64ビットのLWORD型の4つのビット列型があります。例えば、WORD型は、2#0000_0000_0000_0000から2#1111_1111_1111_1111までの65536(216)パターンのビット列を表現できます。

プログラムコードにおいて、ビット列値は、非負の整数リテラルを使用します。ビット列型固有のリテラルはありません。

wordv := 2#1010_0101_0000_1111;
dwordv := 16#A50F;
wordv := 10; // 16#000A
bytev := BYTE#10; // 16#0A

次の例にも示す負の整数リテラルの代入は、不正なプログラムコードです。負の整数リテラルがシステムにより整数値として解釈され、整数からビット列への暗黙の型変換がないためです。

dwordv := -1; // error

以下は、ビット列型の要素に固有の演算です。これらの演算が整数型の要素には用意されていないことに注意してください。

特にバイナリ転写は実数型のフォーマットであるIEEE 754のビット列を扱うことができます。※1

LREAL_TO_LWORD(10.0); // LWORD#16#4024_0000_0000_0000
DWORD_TO_REAL(16#3DCC_CCCD); // REAL#0.1

もちろん整数型との間で相互変換できます。負整数を2の補数で得たりすることができます。

DWORD_TO_DINT(16#FF); // 255
DWORD_TO_DINT(16#FFFF_FFFF); // -1
DWORD_TO_UDINT(16#FFFF_FFFF); // 4294967295; 2^32-1
DINT_TO_DWORD(-1); // DWORD#16#FFFF_FFFF

※1 執筆時点では、本規定に従う実装をしているベンダーは、Phoenix Contact社のPLCnext Engineerのみかも知れません。他のベンダーの場合、上記例では、整数変換され、lwordv=16#Arealv=1.036831936e+9を得ます。

BOOL型

(準備中)

持続時間型

(準備中)

日付型・時刻型・日付時刻型

(準備中)

文字列型

(準備中)

変数の初期値

(準備中)

変数のその他の属性

(準備中)

CONSTANT属性

(準備中)

AT属性

(準備中)

エッジ属性

(準備中)

保持・非保持属性

(準備中)

特別な変数

実行制御 EN変数・ENO変数

ラダー言語におけるパワーフローや、FBD言語における信号の流れによる実行制御は、IEC 61131-3の特徴の一つです。電気的に導通しているときや信号が入ったときに機能を実行することをソフトウェア的に実現する仕組みとして、EN変数(ENable)・ENO変数(ENable Out)があります。EN・ENOを使用することにより、機能の実行・非実行、即ちPOUの呼び出し・非呼び出しを制御できます。本節に示すことは、ST言語にも適用されます。

EN・ENOは、個々のファンクション、FBインスタンス、およびメソッド内に次のように定義される変数です。

VAR_INPUT EN: BOOL := TRUE; END_VAR
VAR_OUTPUT ENO: BOOL; END_VAR

本変数をベンダーが定義しておくか、ユーザーが定義するかは実装依存ですが、どちらが定義するにしても、本変数には以下で示す特別な動作仕様があります。

重要なことは、POU呼び出し側でENがFALSEであれば、そのPOUは実行されないし、呼び出し側でENOが FALSEと観測されれば、その呼び出し後の出力(出力変数、入出力変数、および戻り値)は意図したものではない可能性があるということです。また、上記動作仕様から、POU内部ではENの値はTRUEであることが分かります。

POU内でユーザーがENOの値を書き換え可能ですが、ユーザーは上記のような動作仕様があることを踏まえておかなければいけません。

なお、ST言語において、EN変数、ENO変数を指定する場合、フォーマルな呼び出し、即ち仮引数指定での呼び出し記法を使用する必要があります。例えば、move(en := cond, in := src, out => dest, eno => result) のように記述します。move(src, dest) のようなインフォーマルな呼び出し記法では、en/enoを指定できません。

⚠️EN変数・ENO変数に関する本規定は有用なものですが、難解さと実装の選択肢の多さにより致命的なポータビリティ低下を招く規定の一つです。筆者個人としては、他ベンダーからの移行可能性を高めるためにも、ベンダーは次に示すユーザー向け仕様を維持する実装が望ましいと考えます。

戻り値変数

(準備中)

ユーザー定義データ型

(準備中)

列挙型

BOOL型と宣言した変数は真と偽の2つの値しか取れないことからも分かる通り、データ型は変数がとり得る値の集合を制約する特性があります。列挙型は、その値の集合をユーザー自身が定義できるデータ型です。例えば、次のプログラムは、eRed, eGreen, eBlueの3つの値しか取れないColor列挙型、eSun, eMon, eTue, eWed, eThu, eFri, eSatの7つの値しか取れないDay列挙型を宣言しています。列挙型を使用することにより、マジックナンバーを使用するよりも可読性が高くなり、変数がとり得る値をシステムが保証することができるなどのメリットがあります。

TYPE
	// 列挙型Colorの宣言、デフォルト初期値: eRed。
	Color: (eRed, eGreen, eBlue);
	// 列挙型Day(曜日)の宣言、デフォルト初期値: eMon。
	Day: (eSun, eMon, eTue, eWed, eThu, eFri, eSat) := eMon;
END_TYPE;

PROGRAM Main
	VAR
		colorv: Color; // 初期値 = Color#eRed
		dayv: Day := Day#eMon;
	END_VAR
	colorv := Color#eGreen;
	colorv := 2; // Error!
	colorv := Day#eSun; // Error!
END_PROGRAM

上記Dayの宣言において、eMonをデフォルト初期値として宣言しています。デフォルト初期値を指定しないとき、デフォルト初期値は、列挙型宣言において最初に登場する値です。上記プログラムにおけるColor列挙型のデフォルト初期値は、Color#eRedとなります。

Color#eRedDay#eSatなどの列挙型の値を列挙子といいます。列挙子のプログラム上の表現は、データ型名#列挙子です。ただし、列挙子のスコープは、その列挙子のデータ型と同じスコープをもつため、列挙型#を省略してもエラーとはなりません。一方、列挙子のこの広いスコープは、他の要素のスコープと重複する可能性を高めます。本書のプログラム例でも示す通り、列挙子名は接頭辞eを付加するなど、他と重複し難い命名規則にすることを推奨します。また、列挙型同士の重複も回避するため、列挙型#を省略しないことを推奨します。

TYPE
	Color: (eRed, eGreen, eBlue);
	Signal: (eBlue, eYellow, eRed);
END_TYPE;

PROGRAM Main
	VAR
		colorv: Color;
	END_VAR
	colorv := eGreen; // OK. colorv = Color#eGreen
	colorv := eRed; // Error! Color#eRedなのか、Signal#eRedなのか区別できない。
END_PROGRAM

コンパイラや実行エンジンは、列挙型の値の実体を整数値で実装することも多く、IEC 61131-3においても列挙子に対し、整数値を設定する構文が存在します。実体の値を指定する列挙型は、単なる列挙型とは区別され、値付き列挙型といいます。

値付き列挙型

(準備中)

構造体型

(準備中)

構造体型変数の初期化

構造体型変数の初期化は、変数宣言時に以下のように書くことができます。

TYPE
	s_t: STRUCT
		m0: DINT;
		m1: ARRAY[0..2] OF DINT;
	END_STRUCT;

	t_t: STRUCT
		n0: DINT;
		n1: s_t;
	END_STRUCT;
END_TYPE;

PROGRAM Main
	VAR
		sv1: s_t := (m0 := 2, m1 := [3, 5, 7]);
		sv2: s_t := (m0 := 11); // 部分的な指定 (m0 := 2, m1 := [0, 0, 0]) と同義です。
		tv: t_t := (
			n0 := 13,
			n1 := (
				m0 := 17,
				m1 := [19, 23, 29]
			)
		);
	END_VAR
END_PROGRAM

文法的に構造体初期化子といいます。

(<メンバ名1> := <初期値1>, <メンバ名2> := <初期値2>, ...)

構造体初期化子をコード部で書くことはできません。以下は不正なプログラムです。

:
PROGRAM Main
	VAR sv: s_t; END_VAR

	sv := (m0 := 2, m1 := [3, 5, 7]); // Compile-time error.
END_PROGRAM

相対位置付き構造体型

(準備中)

絶対位置付き構造体型

(準備中)

範囲型

(準備中)

参照型

(準備中)

配列

(準備中)

配列変数の初期化

配列変数の初期化は、変数宣言時に以下のように書くことができます。

PROGRAM Main
	VAR
		// a[3] = 2
		// a[4] = 3
		// a[5] = 5
		a1: ARRAY[3..5] OF DINT := [2, 3, 5];

		// a[0,0] = 16#00
		// a[0,1] = 16#01
		// a[0,2] = 16#02
		// a[1,0] = 16#10
		// a[1,1] = 16#11
		// a[1,2] = 16#12
		a2: ARRAY[0..1, 0..2] OF DINT := [
			16#00, 16#01, 16#02,
			16#10, 16#11, 16#12
		];

		// ※多次元配列における以下のネスト記法は、規格には示されいません。
		a3: ARRAY[0..1, 0..2] OF DINT := [
			[16#00, 16#01, 16#02],
			[16#10, 16#11, 16#12]
		];
	END_VAR
END_PROGRAM

文法的に配列初期化子といいます。

[<初期値1>, <初期値2>, ...]

配列初期化子をコード部で書くことはできません。以下は不正なプログラムです。

PROGRAM Main
	VAR av: ARRAY[3..5] OF DINT; END_VAR

	av := [2, 3, 5]; // Compile-time error.
END_PROGRAM

以下のように連続指定することができます。

TYPE
	s_t: STRUCT
		m0: DINT;
		m1: DINT;
	END_STRUCT;
END_TYPE

PROGRAM Main
	VAR
		// v1[0]=0
		// :
		// v1[7]=0
		v1: ARRAY[0..7] OF DINT := [8(0)];
		// v2[0]=0
		// :
		// v2[4]=1
		// :
		v2: ARRAY[0..7] OF DINT := [4(0), 4(1)];
		// v3[0].m0=2
		// v3[0].m1=3
		// v3[1].m0=2
		// v3[1].m1=3
		// v3[2].m0=2
		// v3[2].m1=3
		v3: ARRAY[0..2] OF s_t := [3((m0 := 2, m1 := 3))]; // 括弧の数に注意。
		// v4[0,0]=0
		// :
		v4: ARRAY[0..1, 0..2] OF DINT := [6(0)];
	END_VAR
END_PROGRAM

可変長配列

可変長配列は、配列長が実行時に定まる変数の型です。入出力変数の型として使用できます。(※規格上は、入力変数にも適用可能と規定されていますが、規定が不完全です)。

可変長配列を使用することで、あらゆる配列要素数にも対応できる汎用的なライブラリを作成することができます。

// LREAL型配列要素の総和を求めます。
FUNCTION sum: LREAL
	VAR_IN_OUT
		values: ARRAY[*] OF LREAL; // 1次元可変長配列変数の定義
	END_VAR
	VAR
		i: DINT;
	END_VAR
	sum := 0.0;
	// 配列添字の始点・終点は、標準ファンクションのLOWER_BOUD、UPPER_BOUNDで取得します。
	// 実引数の1は、1次元目の始点・終点を取得することを意味します。
	FOR i := LOWER_BOUND(values, 1) TO UPPER_BOUND(values, 1) BY 1 DO
		sum := sum + values[i];
	END_FOR;
END_FUNCTION

PROGRAM Main
	VAR
		v1: ARRAY[0..4] OF LREAL := [10, 20, 30, 40, 50];
		v2: ARRAY[1..2] OF LREAL := [100, 200];
		s1, s2: LREAL;
	END_VAR
	s1 := sum(v1); // 150 (=10+20+30+40+50)
	s2 := sum(v2); // 300 (=100+200)
END_PROGRAM

1次元の可変長配列の定義は、次のように書きます。

<変数名>: ARRAY[*] OF <基底型>

多次元の可変長配列の定義は、カンマで区切って次のように書きます。

<変数名>: ARRAY[*, *, ...] OF <基底型>

実行時に配列次元の始点と終点の情報を取得するには、標準ファンクションLOWER_BOUNDUPPER_BOUNDを使用します。

LOWER_BOUNDUPPER_BOUNDファンクションは、「ANY_INT LOWER_BOUND(arr: ANY ARRAY; VAR_IN_OUT, dim: ANY_INT; VAR_INPUT)」のように2つの引数をとります。引数arrには、通常可変長配列を指定しますが、固定長配列を指定することもできます。引数dimには、どの次元の始点(又は終点)の値を取得するかを1以上で指定します。戻り値の正確な型は未規定です。

PROGRAM Main
	VAR
		a1: ARRAY[3..5] OF LREAL;
		a3: ARRAY[7..11, 13..17, 19..23] OF LREAL;
	END_VAR
	LOWER_BOUND(a1, 1); // 3
	UPPER_BOUND(a1, 1); // 5

	LOWER_BOUND(a3, 1); // 7
	UPPER_BOUND(a3, 1); // 11
	LOWER_BOUND(a3, 2); // 13
	UPPER_BOUND(a3, 2); // 17
	LOWER_BOUND(a3, 3); // 19
	UPPER_BOUND(a3, 3); // 23
END_PROGRAM

直接派生型

(準備中)

ファンクション

(準備中)

ファンクションブロック(FB)

(準備中)

プログラムPOU

(準備中)

クラス

(準備中)

メンバ変数

(準備中)

メソッド

(準備中)

継承クラス・インタフェース

(準備中)

オブジェクト指向ファンクションブロック(OOFB)

(準備中)

名前空間

(準備中)

VAR_CONFIG

VAR_CONFIGは、変数のAT属性の設定と初期値の設定を、プログラム実行前まで遅延できる機能です。

通常、ある変数に対するAT属性と初期値の設定値については、プログラムを記述した時点で定まる設定値を用いてプログラムが実行されます。

例えば、次のファンクションブロックfbにおいて、変数nが示すデバイスは%IW7であり、初期値は2です。これは、どのFBインスタンスでも変わりません。v1.n、v2.nの双方IW7にアクセスします。インスタンスごとにアクセス先を変えたい場合、ファンクションブロックの複製、ソースコードの修正、再コンパイル、そして再テストが必要になることもあります。

FUNCTION_BLOCK fb
	VAR n AT %IW7 : INT := 2; END_VAR
	:
END_FUNCTION_BLOCK

PROGRAM main
	VAR
		v1: fb;
		v2: fb;
	END_VAR

	v1.n := 3; // v2.nも3になる。
	v2.n := 4; // v1.nも4になる。
	x := v1 = v2; // TRUE
END_PROGRAM

VAR_CONFIGは、次の例に示す変数nのようにAT属性の設定値を未決定(*)と設定し、VAR_CONFIG~END_VAR内で表現・設定される追加の設定にて、%IW5や%IW6などの具体的なAT属性と、初期値を設定します。この設定が、mainやfbのソースコードに影響を与えることなく、プログラム実行直前に設定できることがポイントです。本例では、同じソースコード内にすべて記載していますが、実運用上においては、VAR_CONFIG~END_VAR部分は、時間的にもファイル的にも分離されることになります。

FUNCTION_BLOCK fb
	VAR n AT %I* : INT; END_VAR
	:
END_FUNCTION_BLOCK

PROGRAM main
	VAR
		v1: fb;
		v2: fb;
	END_VAR

	v1.n := 3; // VAR_CONFIGにて別のデバイス先に設定されているため、v2.nには影響しない。
	v2.n := 4; // 同様に、v1.nには影響しない。
	x := v1.n = v2.n; // FALSE
END_PROGRAM

CONFIGURATION cfg
	RESOURCE rsc ON plc
		TASK t();
		PROGRAM main WITH t: main();
	END_RESOURCE
	VAR_CONFIG
		rsc.main.v1.n AT %IW5 : INT := 16;
		rsc.main.v2.n AT %IW6 : INT := 17;
	END_VAR
END_CONFIGURATION

AT属性の*指定については、ファンクションブロックだけでなく、プログラムPOU内の変数に対しても使用できます。VAR_INPUT、VAR_IN_OUT、およびVAR_EXTERNALを除く変数に使用できます。プログラム実行前に、*が設定されているすべての変数に対して、AT属性が設定されていなければ異常です。%I*の表現については、他にも特性に応じて、%I*, %QW*などを使用できます。※1

※1 部分的にAT属性を設定するという意味で、IEC 61131-3規格においては、「部分指定」という表現が使用されています。よって、全てを省略する%*という表現は、IEC 61131-3規格に示されません。効用的には、部分指定というより、遅延指定という表現の方が適切と考えていますし、%*という表現もあって良いと考えます。

演算子

IEC 61131-3の演算子の一覧を示します。※1

複数の演算子が混在する式においては、優先順位の高いものから評価されます。同一の優先順位をもつ場合、左から右に評価されます。他のプログラミング言語同様、括弧を使用することで、優先的に評価することができます。

r := 1 + 2; // 3
r := 1 + 2 * 3; // 7 = 1 + (2 * 3)
r := (1 + 2) * 3; // 9
r := -2 ** 4; // 16 = (-2)**4
r := f() * g() * h(); // f→g→hの順に呼ばれる。
r := f() + g() * h(); // g→h→fの順に呼ばれる。
演算子の一覧
演算子 名称 優先順位 結合規則
() 呼び出し 11 実装依存(一般的に左)
. メンバアクセス部分アクセス 実装依存 実装依存(一般的に左)
[] 配列添え字 実装依存 実装依存(一般的に左)
# 列挙子アクセス 実装依存 実装依存(一般的に左)
ref リファレンス 実装依存 実装依存(一般的に右)
^ デリファレンス 10 実装依存(一般的に右)
+ - 単項プラス・単項マイナス 9 実装依存(一般的に右)
not 論理否定ビット否定 9 実装依存(一般的に右)
** べき乗 8
* 乗算 7
/ 除算 7
mod 剰余 7
+ 加算 6
- 減算 6
> >= <= < 比較 5
= <> 等価 非等価 4
and & 論理積ビット積 3
xor 排他的論理和ビット排他的論理和 2
or 論理和ビット和 1
:= => ?= 代入引数代入試行代入 実装依存 実装依存(一般的に右)

※1 優先順位が実装依存となっている演算子は、IEC 61131-3の規定に"演算子"として定義されていないものです。一般的なプログラミング言語で演算子として定義されているものは筆者の判断で含めています。

呼び出し

(準備中)

形式として、仮引数有り呼び出し(フォーマルな呼び出し)と、仮引数無し呼び出し(インフォーマルな呼び出し)があります。一部の引数を省略するためには、仮引数有り呼び出しを使用する必要があります。

pou_instance(in1 := 2, in2 := true, out => v); // インスタンス呼び出し(フォーマル)
move(in := 3, out => intv); // 戻り値のないFUNCTION呼び出し(フォーマル)
strv := concat(in1 := key, in2 := ':', in3 := value); // 戻り値のあるFUNCTION呼び出し(フォーマル)
pou_instance(2, true, v); // インスタンス呼び出し(インフォーマル)
move(3, intv); // 戻り値のないFUNCTION呼び出し(インフォーマル)
strv := concat(key, ':', value); // 戻り値のあるFUNCTION呼び出し(インフォーマル)

メンバアクセス

(準備中)

配列アクセス

(準備中)

リファレンス

(準備中)

デリファレンス

(準備中)

単項プラス・単項マイナス

(準備中)

加算・減算

(準備中)

乗算・除算・剰余算

(準備中)

べき乗

演算子**は、べき乗を計算します。

r := 2 ** 3; // 8 = 2 * 2 * 2
r := 4.0 ** 0.5; // 2 = SQRT(4)
r := 2.0 ** (-3); // 0.125 = 1/(2 ** 3) = 1 / (2 * 2 * 2)
r := 5 ** 0; // 1
r := -2 ** 4; // 16 = (-2) ** 4
r := 2 ** 3 ** 2; // 64 = (2 ** 3) ** 2 = 8 * 8
r := -2 ** 0.5; // 実装依存; 複素数
r := 0 ** 2; // 0
r := 0 ** 0; // 実装依存

数学の2^3^2は、2^(3^2)=512と右から左へ評価されますが、IEC 61131-3の2**3**2は、(2**3)**2=64と左から右に評価されることに注意が必要です。

比較

(準備中)

等価

(準備中)

論理否定・論理積・論理和・排他的論理和

(準備中)

ビット否定・ビット積・ビット和・ビット排他的論理和

(準備中)

部分アクセス

ビット列型変数の部分的なビット列値に簡単にアクセスできます。演算子.を使用し、<ビット列型変数>.%<幅><位置>のように記述します。

boolv0 := wordv.%X0 ; // word型変数のビット0(最下位ビット)
boolv7 := wordv.%X7 ; // word型変数のビット7(最上位ビット)
low_bytev := wordv.%B0 ; // word型変数のバイト0(下位バイト)
high_bytev := wordv.%B1 ; // word型変数のバイト1(上位バイト)
lowlow_bytev := dwordv.%B0 ; // dword型変数のバイト0(最下位バイト)

<幅>は、X(BOOL), B(BYTE), W(WORD), D(DWORD), L(LWORD)のいずれかです。<位置>は、0以上で指定し、ビット列型変数のビット幅を超えないように指定する必要があります。例えば、dword型変数に対する部分アクセスであれば、%X0~%X31, %B0~%B3, %W0~%W1 となります。

単一のビットアクセスの場合には省略記法があり、<ビット列型変数>.<位置>のように記述できます。

boolv6 := wordv.6 ; // wordv.%X6と等価です。

なお、次のようなプログラムについての明確な規定はありません。

wordv.%X0 := TRUE; // 実装依存: 部分アクセス式に対する書き込み。
boolv := WORD#16#FF.%X0 ; // 実装依存: ビット列リテラルに対する部分アクセス。
boolv := (WORD#16#000F AND wordv).%X3 ; // 実装依存: ビット列型の式の結果に対する部分アクセス。
boolv := wordFunction().%X1 ;
wordv := wordv.%W0 ; // 実装依存: 恒等的な部分アクセス。なお、<幅>にLが定義されており、lword.%L0 としか使いようがない。

代入

(準備中)

試行代入

(準備中)

制御文

(準備中)

代入文

(準備中)

x := 2;
x := 3 + 5;
x := y := 7; // Compile-time error! syntax error.

呼び出し文

呼び出しを参照。

IF文

(準備中)

IF x = 0 THEN
	zero_num := zero_num + 1;
END_IF;
IF (x mod 2) = 0 THEN
	even_num := even_num + 1;
ELSE
	odd_num := odd_num + 1;
END_IF;
IF x > 0 THEN
	positive_num := positive_num + 1;
ELSIF x < 0 THEN
	negative_num := negative_num + 1;
ELSE
	zero_num := zero_num + 1;
END_IF;

CASE文

(準備中)

n := 3;
CASE n OF
2:
	// n is 2
	k := 4;
3:
	// n is 3
	k := 9;
4:
	// n is 4
	k := 16;
5..7:
	// n is 5 or 6 or 7.
	k := n * n;
ELSE
	// n is other value.
	k := -1;
END_CASE;
// k = 9

FOR文

(準備中)

sum := 0;
FOR n := 1 TO 4 BY 1 DO
	sum := sum + n;
END_FOR;
// sum = 10 = 1 + 2 + 3 + 4, n = 5.
sum := 0;
FOR n := 1 TO 4 DO
	sum := sum + n;
END_FOR;
// sum = 10 = 1 + 2 + 3 + 4, n = 5.

WHILE文

(準備中)

sum := 0;
n := 1;
WHILE n <= 4 DO
	sum := sum + n;
	n := n + 1;
END_WHILE;
// sum = 10 = 1 + 2 + 3 + 4, n = 5.

REPEAT文

(準備中)

sum := 0;
n := 1;
REPEAT
	sum := sum + n;
	n := n + 1;
UNTIL n = 5 END_REPEAT;
// sum = 10 = 1 + 2 + 3 + 4, n = 5.

CONTINUE文

(準備中)

sum := 0;
n := 1;
WHILE n <= 4 DO
	IF n = 2 THEN
		n := n + 1;
		CONTINUE;
	END_IF;
	sum := sum + n;
	n := n + 1;
END_WHILE;
// sum = 8 = 1 + 3 + 4, n = 5.

EXIT文

(準備中)

sum := 0;
n := 1;
WHILE n <= 4 DO
	IF n = 3 THEN
		EXIT;
	END_IF;
	sum := sum + n;
	n := n + 1;
END_WHILE;
// sum = 3 = 1 + 2, n = 3.

RETURN文

(準備中)

PROGRAM main
	...
	IF n = 0 THEN
		RETURN;
	END_IF;
	...
END_PROGRAM

FUNCTION f: INT
	...
	IF n = 0 THEN
		f := 1;
		RETURN;
	END_IF;
	...
	RETURN 9; // Compile-time error! Syntax error.
END_IF;

標準POU

SINT_TO_INT, SINT_TO_DINT, SINT_TO_LINT, SINT_TO_USINT, SINT_TO_UINT, SINT_TO_UDINT, SINT_TO_ULINT, SINT_TO_REAL, SINT_TO_LREAL, INT_TO_SINT, INT_TO_DINT, INT_TO_LINT, INT_TO_USINT, INT_TO_UINT, INT_TO_UDINT, INT_TO_ULINT, INT_TO_REAL, INT_TO_LREAL, DINT_TO_SINT, DINT_TO_INT, DINT_TO_LINT, DINT_TO_USINT, DINT_TO_UINT, DINT_TO_UDINT, DINT_TO_ULINT, DINT_TO_REAL, DINT_TO_LREAL, LINT_TO_SINT, LINT_TO_INT, LINT_TO_DINT, LINT_TO_USINT, LINT_TO_UINT, LINT_TO_UDINT, LINT_TO_ULINT, LINT_TO_REAL, LINT_TO_LREAL, USINT_TO_SINT, USINT_TO_INT, USINT_TO_DINT, USINT_TO_LINT, USINT_TO_UINT, USINT_TO_UDINT, USINT_TO_ULINT, USINT_TO_REAL, USINT_TO_LREAL, UINT_TO_SINT, UINT_TO_INT, UINT_TO_DINT, UINT_TO_LINT, UINT_TO_USINT, UINT_TO_UDINT, UINT_TO_ULINT, UINT_TO_REAL, UINT_TO_LREAL, UDINT_TO_SINT, UDINT_TO_INT, UDINT_TO_DINT, UDINT_TO_LINT, UDINT_TO_USINT, UDINT_TO_UINT, UDINT_TO_ULINT, UDINT_TO_REAL, UDINT_TO_LREAL, ULINT_TO_SINT, ULINT_TO_INT, ULINT_TO_DINT, ULINT_TO_LINT, ULINT_TO_USINT, ULINT_TO_UINT, ULINT_TO_UDINT, ULINT_TO_REAL, ULINT_TO_LREAL

REAL_TO_SINT, REAL_TO_INT, REAL_TO_DINT, REAL_TO_LINT, REAL_TO_USINT, REAL_TO_UINT, REAL_TO_UDINT, REAL_TO_ULINT, REAL_TO_LREAL, LREAL_TO_SINT, LREAL_TO_INT, LREAL_TO_DINT, LREAL_TO_LINT, LREAL_TO_USINT, LREAL_TO_UINT, LREAL_TO_UDINT, LREAL_TO_ULINT, LREAL_TO_REAL

BYTE_TO_WORD, BYTE_TO_DWORD, BYTE_TO_LWORD, WORD_TO_BYTE, WORD_TO_DWORD, WORD_TO_LWORD, DWORD_TO_BYTE, DWORD_TO_WORD, DWORD_TO_LWORD, LWORD_TO_BYTE, LWORD_TO_WORD, LWORD_TO_DWORD

BYTE_TO_SINT, BYTE_TO_INT, BYTE_TO_DINT, BYTE_TO_LINT, BYTE_TO_USINT, BYTE_TO_UINT, BYTE_TO_UDINT, BYTE_TO_ULINT, WORD_TO_SINT, WORD_TO_INT, WORD_TO_DINT, WORD_TO_LINT, WORD_TO_USINT, WORD_TO_UINT, WORD_TO_UDINT, WORD_TO_ULINT, DWORD_TO_SINT, DWORD_TO_INT, DWORD_TO_DINT, DWORD_TO_LINT, DWORD_TO_USINT, DWORD_TO_UINT, DWORD_TO_UDINT, DWORD_TO_ULINT, LWORD_TO_SINT, LWORD_TO_INT, LWORD_TO_DINT, LWORD_TO_LINT, LWORD_TO_USINT, LWORD_TO_UINT, LWORD_TO_UDINT, LWORD_TO_ULINT

SINT_TO_BYTE, SINT_TO_WORD, SINT_TO_DWORD, SINT_TO_LWORD, INT_TO_BYTE, INT_TO_WORD, INT_TO_DWORD, INT_TO_LWORD, DINT_TO_BYTE, DINT_TO_WORD, DINT_TO_DWORD, DINT_TO_LWORD, LINT_TO_BYTE, LINT_TO_WORD, LINT_TO_DWORD, LINT_TO_LWORD, USINT_TO_BYTE, USINT_TO_WORD, USINT_TO_DWORD, USINT_TO_LWORD, UINT_TO_BYTE, UINT_TO_WORD, UINT_TO_DWORD, UINT_TO_LWORD, UDINT_TO_BYTE, UDINT_TO_WORD, UDINT_TO_DWORD, UDINT_TO_LWORD, ULINT_TO_BYTE, ULINT_TO_WORD, ULINT_TO_DWORD, ULINT_TO_LWORD

LWORD_TO_LREAL, LREAL_TO_LWORD, DWORD_TO_REAL, REAL_TO_DWORD

BOOL_TO_BYTE, BOOL_TO_WORD, BOOL_TO_DWORD, BOOL_TO_LWORD

BYTE_TO_BOOL, WORD_TO_BOOL, DWORD_TO_BOOL, LWORD_TO_BOOL

BOOL_TO_SINT, BOOL_TO_INT, BOOL_TO_DINT, BOOL_TO_LINT, BOOL_TO_USINT, BOOL_TO_UINT, BOOL_TO_UDINT, BOOL_TO_ULINT

TIME_TO_LTIME, LTIME_TO_TIME, DT_TO_DATE, DT_TO_LDATE, DT_TO_TOD, DT_TO_LTOD, DT_TO_LDT, LDT_TO_DATE, LDT_TO_LDATE, LDT_TO_TOD, LDT_TO_LTOD, LDT_TO_DT, TOD_TO_LTOD, LTOD_TO_TOD

CHAR_TO_BYTE, CHAR_TO_WORD, CHAR_TO_DWORD, CHAR_TO_LWORD, WCHAR_TO_WORD, WCHAR_TO_DWORD, WCHAR_TO_LWORD

STRING_TO_WSTRING, WSTRING_TO_STRING, CHAR_TO_WCHAR, WCHAR_TO_CHAR, CHAR_TO_STRING, STRING_TO_CHAR, WCHAR_TO_WSTRING, WSTRING_TO_WCHAR

UPPER_BOUND, LOWER_BOUND

NOT, AND, OR, XOR

ABS, SQRT, LN, LOG, EXP, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, ADD, MUL, SUB, DIV, MOD, EXPT

MOVE

SHL, SHR, ROL, ROR

SEL, MAX, MIN, LIMIT, MUX

LEN, LEFT, RIGHT, MID, CONCAT, INSERT, DELETE, REPLACE, FIND

ADD_TIME, ADD_LTIME, ADD_TOD_TIME, ADD_LTOD_LTIME, ADD_DT_TIME, ADD_LDT_LTIME, SUB_TIME, SUB_LTIME, SUB_DATE_DATE, SUB_LDATE_LDATE, SUB_TOD_TIME, SUB_LTOD_LTIME, SUB_TOD_TOD, SUB_LTOD_LTOD, SUB_DT_TIME, SUB_LDT_LTIME, SUB_DT_DT, SUB_LDT_LDT, MUL_TIME, MUL_LTIME, DIV_TIME, DIV_LTIME, CONCAT_DATE_TOD, CONCAT_DATE_LTOD, CONCAT_DATE, CONCAT_TOD, CONCAT_LTOD, CONCAT_DT, CONCAT_LDT, SPLIT_DATE, SPLIT_TOD, SPLIT_LTOD, SPLIT_DT, SPLIT_LDT, DAY_OF_WEEK

REAL_TRUNC_SINT, REAL_TRUNC_INT, REAL_TRUNC_DINT, REAL_TRUNC_LINT, REAL_TRUNC_USINT, REAL_TRUNC_UINT, REAL_TRUNC_UDINT, REAL_TRUNC_ULINT, LREAL_TRUNC_SINT, LREAL_TRUNC_INT, LREAL_TRUNC_DINT, LREAL_TRUNC_LINT, LREAL_TRUNC_USINT, LREAL_TRUNC_UINT, LREAL_TRUNC_UDINT, LREAL_TRUNC_ULINT, TRUNC_SINT, TRUNC_INT, TRUNC_DINT, TRUNC_LINT, TRUNC_USINT, TRUNC_UINT, TRUNC_UDINT, TRUNC_ULINT

BYTE_BCD_TO_SINT, BYTE_BCD_TO_INT, BYTE_BCD_TO_DINT, BYTE_BCD_TO_LINT, BYTE_BCD_TO_USINT, BYTE_BCD_TO_UINT, BYTE_BCD_TO_UDINT, BYTE_BCD_TO_ULINT, WORD_BCD_TO_SINT, WORD_BCD_TO_INT, WORD_BCD_TO_DINT, WORD_BCD_TO_LINT, WORD_BCD_TO_USINT, WORD_BCD_TO_UINT, WORD_BCD_TO_UDINT, WORD_BCD_TO_ULINT, DWORD_BCD_TO_SINT, DWORD_BCD_TO_INT, DWORD_BCD_TO_DINT, DWORD_BCD_TO_LINT, DWORD_BCD_TO_USINT, DWORD_BCD_TO_UINT, DWORD_BCD_TO_UDINT, DWORD_BCD_TO_ULINT, LWORD_BCD_TO_SINT, LWORD_BCD_TO_INT, LWORD_BCD_TO_DINT, LWORD_BCD_TO_LINT, LWORD_BCD_TO_USINT, LWORD_BCD_TO_UINT, LWORD_BCD_TO_UDINT, LWORD_BCD_TO_ULINT, BCD_TO_SINT, BCD_TO_INT, BCD_TO_DINT, BCD_TO_LINT, BCD_TO_USINT, BCD_TO_UINT, BCD_TO_UDINT, BCD_TO_ULINT, SINT_TO_BCD_BYTE, SINT_TO_BCD_WORD, SINT_TO_BCD_DWORD, SINT_TO_BCD_LWORD, INT_TO_BCD_BYTE, INT_TO_BCD_WORD, INT_TO_BCD_DWORD, INT_TO_BCD_LWORD, DINT_TO_BCD_BYTE, DINT_TO_BCD_WORD, DINT_TO_BCD_DWORD, DINT_TO_BCD_LWORD, LINT_TO_BCD_BYTE, LINT_TO_BCD_WORD, LINT_TO_BCD_DWORD, LINT_TO_BCD_LWORD, USINT_TO_BCD_BYTE, USINT_TO_BCD_WORD, USINT_TO_BCD_DWORD, USINT_TO_BCD_LWORD, UINT_TO_BCD_BYTE, UINT_TO_BCD_WORD, UINT_TO_BCD_DWORD, UINT_TO_BCD_LWORD, UDINT_TO_BCD_BYTE, UDINT_TO_BCD_WORD, UDINT_TO_BCD_DWORD, UDINT_TO_BCD_LWORD, ULINT_TO_BCD_BYTE, ULINT_TO_BCD_WORD, ULINT_TO_BCD_DWORD, ULINT_TO_BCD_LWORD, TO_BCD_BYTE, TO_BCD_WORD, TO_BCD_DWORD, TO_BCD_LWORD

TO_BIG_ENDIAN, TO_LITTLE_ENDIAN, BIG_ENDIAN_TO, LITTLE_ENDIAN_TO

IS_VALID, IS_VALID_BCD

SR, RS

R_TRIG, F_TRIG

CTU_INT, CTU_DINT, CTU_LINT, CTU_UDINT, CTU_ULINT, CTD_INT, CTD_DINT, CTD_LINT, CTD_UDINT, CTD_ULINT, CTUD_INT, CTUD_DINT, CTUD_LINT, CTUD_UDINT, CTUD_ULINT

TP, TP_TIME, TP_LTIME, TON, TON_TIME, TON_LTIME, TOF, TOF_TIME, TOF_LTIME

自作ライブラリ

有用そうな自作のライブラリを公開しています。コンパイル確認・動作確認をしていません。不具合ありましたら、こちらのブログにご連絡いただければ可能な範囲で対応致します。

疑似乱数

Xorshift32

// Xorshift32
FUNCTION xorshift32: DWORD
	VAR_IN_OUT
		state: DWORD;
	END_VAR
	VAR_TEMP
		x: DWORD;
	END_VAR
// {ST}
x := state;
x := x XOR SHL(x, 13);
x := x XOR SHR(x, 17);
x := x XOR SHL(x, 5);
state := x;
xorshift32 := x;
// {END}
END_FUNCTION

FUNCTION nextBits32: DWORD
	VAR_IN_OUT
		state: DWORD;
	END_VAR
	VAR_INPUT
		bits: DINT := 32;
	END_VAR
// {ST}
nextBits32 := SHR(xorshift32(state), 32 - bits);
// {END}
END_FUNCTION

FUNCTION nextBOOL: BOOL
	VAR_IN_OUT
		state: DWORD;
	END_VAR
// {ST}
nextBOOL := nextBits32(state, 1) = DWORD#16#0000_0001;
// {END}
END_FUNCTION

// [16#0, 16#ffff_ffff]
FUNCTION nextDWORD: DWORD
	VAR_IN_OUT
		state: DWORD;
	END_VAR
// {ST}
nextDWORD := nextBits32(state, 32);
// {END}
END_FUNCTION

// [16#0, 16#ffff_ffff_ffff_ffff]
FUNCTION nextLWORD: LWORD
	VAR_IN_OUT
		state: DWORD;
	END_VAR
	VAR_TEMP
		b1, b2: LWORD;
	END_VAR
// {ST}
b1 := DWORD_TO_LWORD(nextBits32(state, 32));
b2 := DWORD_TO_LWORD(nextBits32(state, 32));
nextLWORD := SHL(b1, 32) OR b2;
// {END}
END_FUNCTION

// [0, 2**32-1]
FUNCTION nextUDINT: UDINT
	VAR_IN_OUT
		state: DWORD;
	END_VAR
// {ST}
nextUDINT := DWORD_TO_UDINT(nextDWORD(state));
// {END}
END_FUNCTION

// [0, 2**64-1]
FUNCTION nextULINT: ULINT
	VAR_IN_OUT
		state: DWORD;
	END_VAR
// {ST}
nextULINT := LWORD_TO_ULINT(nextLWORD(state));
// {END}
END_FUNCTION

// A 52-bit based pseudo-random float number, the range is [0.0, 1.0).
// ref. Why is it not 64-bit?
//   --> See https://www.ecma-international.org/ecma-262/5.1/#sec-8.5
FUNCTION nextLREAL: LREAL
	VAR_IN_OUT
		state: DWORD;
	END_VAR
	VAR_TEMP
		n1, n2: ULINT;
	END_VAR
// {ST}
n1 := DWORD_TO_ULINT(nextBits32(state, 26));
n2 := DWORD_TO_ULINT(nextBits32(state, 26));
nextLREAL := ULINT_TO_LREAL(n1 * ULINT#16#0400_0000 + n2)
	/ LREAL#4503599627370496.0; // 2**52
// {END}
END_FUNCTION
使用例
PROGRAM Main
	VAR
		state: DWORD;
		r: LREAL;
	END_VAR
// {ST}
state := DWORD#16#cafeface;
r := nextLREAL(state); // [0.0, 1.0)
r := nextLREAL(state); // [0.0, 1.0)
// {END}
END_PROGRAM

配列

// 配列要素数; LREAL配列
FUNCTION arraySize_LREAL: DINT
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
// {ST}
arraySize_LREAL := UPPER_BOUND(x, 1) - LOWER_BOUND(x, 1) + 1;
// {END}
END_FUNCTION

ソート

バブルソート

// バブルソート; LREAL配列
FUNCTION bubbleSort_LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
	VAR_TEMP
		i, j: DINT;
		t: LREAL;
	END_VAR
// {ST}
FOR i := LOWER_BOUND(x, 1) TO UPPER_BOUND(x, 1) - 1 BY +1 DO
	FOR j := UPPER_BOUND(x, 1) TO i + 1 BY -1 DO
		IF x[j-1] > x[j] THEN
			t := x[j - 1];
			x[j - 1] := x[j];
			x[j] := t;
		END_IF;
	END_FOR;
END_FOR;
// {END}
END_FUNCTION
// バブルソート; DINT配列
FUNCTION bubbleSort_DINT
	VAR_IN_OUT
		x: ARRAY[*] OF DINT;
	END_VAR
	VAR_TEMP
		i, j: DINT;
		t: DINT;
	END_VAR
// {ST}
FOR i := LOWER_BOUND(x, 1) TO UPPER_BOUND(x, 1) - 1 BY +1 DO
	FOR j := UPPER_BOUND(x, 1) TO i + 1 BY -1 DO
		IF x[j-1] > x[j] THEN
			t := x[j - 1];
			x[j - 1] := x[j];
			x[j] := t;
		END_IF;
	END_FOR;
END_FOR;
// {END}
END_FUNCTION

文字列操作

(準備中)

統計関数

平均値

// 平均値
FUNCTION mean: LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
	VAR_TEMP
		i, n: DINT;
		sum: LREAL;
	END_VAR
// {ST}
sum := 0.0;
FOR i := LOWER_BOUND(x, 1) TO UPPER_BOUND(x, 1) DO
	sum := sum + x[i];
END_FOR;
n := arraySize_LREAL(x);
mean := sum / n;
// {END}
END_FUNCTION

分散

// 分散: V(X) = E(X^2) - {E(X)}^2
FUNCTION variance: LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
	VAR_TEMP
		i, n: DINT;
		sum_of_x2: LREAL;
		sum_of_x: LREAL;
	END_VAR
// {ST}
sum_of_x2 := 0.0;
sum_of_x := 0.0;
FOR i := LOWER_BOUND(x, 1) TO UPPER_BOUND(x, 1) DO
	sum_of_x2 := sum_of_x2 + x[i] * x[i];
	sum_of_x := sum_of_x + x[i];
END_FOR;
n := arraySize_LREAL(x);
variance := (sum_of_x2  / n) - (sum_of_x / n)**2;
// {END}
END_FUNCTION

不偏分散

// 不偏分散: s^2 = V(X) * n / (n-1)
FUNCTION sampleVariance: LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
	VAR_TEMP
		n: DINT;
		dof: DINT;
	END_VAR
// {ST}
n := arraySize_LREAL(x);
dof := n - 1;
sampleVariance := variance(x) * n / dof;
// {END}
END_FUNCTION

標準偏差

// 標準偏差: √(分散)
FUNCTION standardDiviation: LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
// {ST}
standardDiviation := SQRT(variance(x));
// {END}
END_FUNCTION

変動係数

// 変動係数: 標準偏差 / 平均
FUNCTION coefficientOfVariation: LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
// {ST}
coefficientOfVariation := standardDiviation(x) / mean(x);
// {END}
END_FUNCTION

標準誤差

// 標準誤差: √(不偏分散 / サンプルサイズ)
FUNCTION standardError: LREAL
	VAR_IN_OUT
		x: ARRAY[*] OF LREAL;
	END_VAR
	VAR_TEMP
		n: DINT;
	END_VAR
// {ST}
n := arraySize_LREAL(x);
standardError := SQRT(sampleVariance(x) / n);
// {END}
END_FUNCTION

APPENDIX

IEC 61131-3にないもの