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メーカーなどIEC 61131-3プログラミング環境提供側)の問題、およびユーザー側の様々な事情があります。

  1. ユーザー側に非IEC 61131-3の膨大な資産があり、IEC 61131-3に切り替えてしまうと教育も含め、生産性が急激に下がる。
  2. IEC 61131-3のプログラムを実行したり試してみる手頃な環境がなく、導入を検討する人や学ぼうとする人の機会損失している。
  3. IEC 61131-3仕様の理解が困難であり、ベンダーの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の規定ですが、各ベンダーの実装はそうではありません。優先度の高い一つのタスク、もしくはそのベンダーにとって"メインのタスク"があり、そのタスクに割り付けたプログラムインスタンスがタスクごとに上から順番に実行されます。

プログラムの実行とタスクの実装
IEC 61131-3 ベンダーの実装
タスク割付
プログラム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の規定ですが、各ベンダーの実装はそうではありません。プログラム間でデータをやりとりするには、グローバル変数を使用します。

コメント

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

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

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

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

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

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

※1 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`は同じ識別子と解釈されます。

パス

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

🛈パス文字列全体の解析処理の比重を字句解析器(Lexer)におくのが良いか、構文解析器(Parser)におくのが良いかは難しい問題です。IEC 61131-3規定に適合するパス文字列の正規表現の例を以下に示しておきます。どちらも下にあるほど文法が厳しいです。字句解析・構文解析では広めにとっておいて意味解析で詳細な意味付けや検査することをお勧めします。

プラグマ

プラグマは、{ ~ }で囲われた要素であり、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型

(準備中)

持続時間型

(準備中)

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

(準備中)

文字列型

(準備中)

変数の初期値

(準備中)

変数の種類

(準備中)

内部変数

(準備中)

一時変数

(準備中)

入力変数・出力変数

(準備中)

入出力変数

(準備中)

外部変数

外部変数は、他のモジュールに定義されるグローバル変数を参照するために使用します。C言語におけるextern変数に相当します。

グローバル変数の用途の一つに、複数のPOU間で共有するデータがあります。あるPOUにて同じ名前のローカル変数定義があるときにエラーになるのと同じように、あるユーザーのシステム全体にて同じ名前のグローバル変数定義があるときにもエラーになります。複数POU間で共有するデータとして使用するグローバル変数をエラーにならないようにシステム全体のどこで定義し、どうPOUで使用するのか、解決策や指針が必要です。

IEC 61131-3におけるグローバル変数は、configuration、又はresourceにのみ定義することができます。POU内においては、どのグローバル変数を使用するかを特定する必要があり、このときに外部変数宣言を使用し、名前によってグローバル変数を特定します。

configuration Cfg
	resource Rsc on Plc
		var_global
			gintv: int;
			gboolv: bool;
		end_var
	end_resource
end_configuration
program Main
	var_external
		gintv: int;
	end_var
	//
	gintv := 2; // Rscのグローバル変数gintvへの書き込み
end_program

program Sub1
	var_external
		gboolv: bool;
	end_var
	//
	gboolv := true; // Rscのグローバル変数gboolvへの書き込み
	gintv := 3; // Compile time error! Unknown symbol 'gintv'.
end_program

program Sub2
	var_external
		gwordv: word; // Link time error! No global variable 'gwordv' found.
	end_var
end_program

外部変数宣言は、グローバル変数を特定するためのリンクのようなものであり、グローバル変数を定義しないことに注意が必要です。

外部変数宣言に関して、次のようなケースはエラーです。

🛈なぜ外部変数宣言のような仕組みが必要なのか補足します。C言語にてextern宣言が必要な理由は、コンパイルの概念があるからです。つまり、C言語で記述された一つのソースファイルをコンパイルしモジュール化することを可能にするために、extern宣言の構文があります。extern宣言により変数の名前やデータ型を人間がコンパイラに教えてあげることにより、コンパイラはシステム全体を知らずともコンパイルできるのです。では、IEC 61131-3にコンパイルの概念はあるのでしょうか?外部変数宣言があるので、そうしたかったのかも知れませんが、実現しているベンダー(POUに対するコンパイルというメニューがあるベンダー)は皆無と思います。これは、IEC 61131-3の機能不足に原因があると考えています。C言語では、コンパイルを実現するためにもう一つの構文があります。関数のextern宣言(関数のプロトタイプ宣言)です。これもまた、関数の戻り値のデータ型や、引数のデータ型を人間がコンパイラに教えるためにあります。IEC 61131-3においても、ファンクションのみならず全てのPOUに対するextern宣言、もしくは、それらをまとめて行うimport構文相当があって初めてコンパイルを実現できます。グローバル変数を定義しているのに外部変数宣言を再度書かなければいけない理由に納得できないのはその通りで、IEC 61131-3の機能不足に一因があります。

グローバル変数 var_global

(準備中)

変数のその他の属性

(準備中)

constant属性

(準備中)

at属性

(準備中)

エッジ属性

エッジ属性は、FBインスタンス呼び出しにおけるbool型入力変数の値の変化を検出します。例えば、信号の立ち上がり検出では、オフからオンに変化したとき、又はオンからオフに変化したときだけ処理を実行したいときに有用です。

r_edge属性・f_edge属性を総称してエッジ属性といい、FBにおけるbool型入力変数に付加できます※1

r_edge属性(Rising EDGE)は、オフからオンに変化したときにオン、即ち、前回呼び出し時の実引数の値がfalseで今回呼び出し時がtrueのとき、当該入力変数の値がtrueになります。f_edge属性(Falling EDGE)は、オンからオフに変化したときにオン、即ち、前回呼び出し時の実引数の値がtrueで、今回呼び出し時がfalseのとき、当該入力変数の値がtrueになります。

function_block REdgeAttrSample
	var_input d: bool r_edge; end_var
end_function_block

function_block FEdgeAttrSample
	var_input d: bool f_edge; end_var
end_function_block

program Main
	var
		r: REdgeAttrSample;
		f: FEdgeAttrSample;
	end_var
	// r_edge変数の例
	r(true);    // d=true、初回呼び出し時にtrueの場合、r_edge変数の値はtrueです。
	r(true);    // d=false
	r(false);   // d=false
	r(false);   // d=false
	r(true);    // d=true

	// f_edge変数の例
	f(false);   // d=true、初回呼び出し時にfalseの場合、f_edge変数の値はtrueです。
	f(false);   // d=false
	f(true);    // d=false
	f(true);    // d=false
	f(false);   // d=true
end_program

※1 en変数にエッジ属性を付加して、いわゆるラダーの微分型命令を実現したいところですが、IEC 61131-3にそのような例はありません。そもそもユーザーにenの宣言を書かせるのか、書かせないのか実装依存ですので、文法的には、FBに対する属性とするのが良いのかも知れません。

保持・非保持属性

(準備中)

特別な変数

実行制御 en変数・eno変数

LD言語におけるパワーフローや、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) のようなインフォーマルな呼び出し記法では、enenoを指定できません。

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

戻り値変数

ファンクションやメソッドを呼び出した結果の戻り値は、戻り値変数の値で決まります。

戻り値変数は、ファンクションやメソッド内に暗黙的に定義されるローカル変数です。

function is_even_num: bool
	var_input in: int; end_var
	//
	is_even_num := (in mod 2) = 0;
end_function

program Main
	var r: bool; end_var
	//
	r := is_even_num(3); // r=false
	r := is_even_num(6); // r=true
end_program

戻り値のないファンクションで、戻り値変数が暗黙的に定義されるか否かは、実装依存です。

function no_return_function
	var_input in: int; end_var
	...
	no_return_function := false; // ?
end_function

ユーザー定義データ型

(準備中)

列挙型

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

可変長配列

可変長配列は、配列長が実行時に定まる変数の型です。入出力変数の型として使用できます※1

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

// lreal型配列要素の総和を求めます。
function sum: lreal
	var_in_out
		values: array[*] of lreal; // 1次元可変長配列変数の定義
	end_var
	var_temp
		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

※1 規格上は「可変長配列をFBの入力変数、出力変数にも適用可能」とありますが、動的メモリ確保が必要となることについての説明なく規定は不完全です。また「可変長配列をファンクションの入出力変数、およびFBの入力変数、入出力変数、出力変数のデータ型として適用可能」とちぐはぐな規定ともなっており、本書では「可変長配列を入出力変数の型として使用可能」と限定しました。

直接派生型

(準備中)

ファンクション

ファンクションは、一般的なプログラミング言語の関数(function)と同等の機能をもつPOUです。ファンクションに引数(入力)を与えて呼び出すことにより、ファンクション内の処理が実行され、呼び出した側では、処理の結果である戻り値(出力)を得ることができます。ファンクションを適切に利用することにより、同じプログラムコードを何度も書く手間を減らし、可読性を高め、管理を容易にします。

// 戻り値のデータ型がlrealのファンクションabsmaxの定義です。
// 2つの引数のうち絶対値の大きい方を結果として得ます。
function absmax: lreal
	// absmaxは、lreal型のinput1とinput2の2つの引数をもちます。
	var_input
		input1: lreal;
		input2: lreal;
	end_var
	//
	// ファンクション名と同じ変数(戻り値変数)の値を、呼び出し側で戻り値として得ることができます。
	if abs(input1) > abs(input2) then
		absmax := input1;
	else
		absmax := input2;
	end_if;
end_function

program Main
	var
		y1, y2: lreal;
	end_var
	//
	// ファンクションabsmaxの呼び出しです。
	// input1に2、input2に3を設定し、absmaxの処理が実行され、y1に3が代入されます。
	y1 := absmax(input1 := 2.0, input2 := 3.0);
	// input1に-7、input2に5を設定し、absmaxの処理が実行され、y2に-7が代入されます。
	y2 := absmax(-7.0, 5.0);
end_program
// 二つのint型変数の値を入れ替えるファンクションです。戻り値はありません。
function swap_INT
	// swap_INTは、in1とin2の2つの引数をもちます。
	var_in_out
		in1, in2: int;
	end_var
	var_temp
		t: int;
	end_var
	//
	t := in1;
	in1 := in2;
	in2 := t;
end_function

program Main
	var
		x1: int := 2;
		x2: int := 3;
	end_var
	// x1=2, x2=3
	swap_INT(x1, x2);
	// x1=3, x2=2
end_program

IEC 61131-3の関数の特徴

一般的なプログラミング言語の関数との違いや特徴を以下に示します。

ファンクション定義の記述方法

ファンクション定義を一般的に次のように記述できます。

function <ファンクション名>:<戻り値のデータ型>
	<引数変数の宣言>
	<変数の宣言>
	<処理>
end_function

<ファンクション名>は、ファンクションを呼び出すときに使用する名前です。この名前を使って、プログラムコードの別の場所からファンクション内の処理を実行することができます。ファンクション内では、ファンクションの実行結果である戻り値を意味する変数の名前にもなります。

<戻り値のデータ型>は、ファンクションの戻り値のデータ型です。コロン(:)以降を省略する場合、戻り値のないファンクションとなります。また、配列の戻り値とする場合、配列型の直接派生型での指定が必要になります※1

<引数変数の宣言>には、以下に示す3つの引数変数を宣言することができます。

<変数の宣言>には、ファンクション内で使用する変数を宣言します。内部変数(キーワードvar)、一時変数(var_temp)、および外部変数(var_external)※2を宣言できます。ファンクションにおいては、内部変数と一時変数の違いはありません※3

<処理>には、ファンクションが実行する処理(プログラムコード)を記述します。制御文を記述できます。<ファンクション名>を名前とする戻り値変数に値を代入しておくことで、呼び出し側でその値を受け取ることができます。<処理>の終端に達するか、return文が実行されると、呼び出し元に制御が戻ります。なお、自分自身を呼び出す再帰呼び出しの可否については実装依存です※4

※1 function fun: array[0..2] of lreal ~ end_functionのような配列戻り値の構文サポートについては実装依存です。
※2 入力が同じであれば、同じ出力を返す関数を一般的に純関数といいます。純関数には、関数外へ副作用がないことに起因する様々なメリットがあります。一方、外部変数宣言を含むファンクションは、ファンクション外部の環境に依存したり影響を与えるため、純関数となりません。
※3 IEC 61131-3は、ファンクション内でのインスタンス変数の定義を禁止しています。インスタンスは状態を持つから、というのが理由のようですが、基本レベルの汎用的なFBやクラスであれば、普通に使いたくなるものです。ベンダーは必ずしも本制約に従う必要はないと考えます。
※4 再帰呼び出しを制限する主な理由は、実行系において、必要量が予測困難で動的なメモリ確保を必要とするためです。

ファンクション呼び出しの記述方法

ファンクション呼び出しの記述方法には、仮引数を記述するフォーマル呼び出しと、仮引数を省略するインフォーマル呼び出しがあります。インフォーマル呼び出しでは、すべての引数の値を記述しないといけないのに対し、フォーマル呼び出しでは、一部又は全ての引数の記述を省略できます。省略した引数の値は、当該変数の初期値やデータ型のデフォルト値が処理系により設定されています。

フォーマル呼び出し

ファンクションのフォーマル呼び出しは、一般的に次のように記述できます。

<ファンクション名>(<仮引数名1> := <実引数値1>, <仮引数名2> := <実引数値2>, ...)

例:

y := absmax(input1 := 13, input2 := -17); // -17
move(in := 3, out => intv); // intvは3。出力変数の演算子には、=>を使用します。

フォーマル呼び出しにおいて、入力変数と出力変数の引数を省略することができます。省略する場合、変数の値は、変数宣言時に指定した初期値、又は初期値を指定していなければ、データ型のデフォルト値です。入出力変数の引数を省略することはできません。また、出力変数の引数のみ演算子に => を使用します(e.g. output2 => intv)。演算子からも分かる通り、呼び出し後に左辺値を右辺に代入します。bool型の出力変数の場合のみ使用できるnot bool_output => boolvの構文では、左辺値を否定した値を右辺に代入します※1

フォーマル呼び出しでのみen変数・eno変数を指定できます。インフォーマル呼び出しでは、en変数・eno変数を指定できません。

y := div(
	in1 := intv1,
	in2 := intv2,
	not eno => maybe_intv2_is_zero);
インフォーマル呼び出し

ファンクションのインフォーマル呼び出しは、一般的に次のように記述できます。

<ファンクション名>(<実引数値1>, <実引数値2>, ...)

例:

y := absmax(13, -17); // -17
move(3, intv); // intvは3

インフォーマル呼び出しの括弧内で引数を記述する順番は、ファンクション定義時の引数変数定義の順番に一致します。en変数・eno変数を含まないことに注意してください。

// 線形合同法による擬似乱数生成器
function LCGrand: ULINT
	var_in_out state: ULINT; end_var
	var_input a, b, m: ULINT; end_var
	//
	state := (a * state + b) MOD m;
	LCGrand := state;
end_function

// Park & Miller提案の定数に従う擬似乱数生成器
function LMrand: ULINT
	var_in_out state: ULINT; end_var
	//
	LMrand := LCGrand(state, 48271, 0, 16#7fff_ffff); // state, a, b, mの順番です。
end_function

※1 POU呼び出しにおけるnot bool_output => boolvの構文は、LD言語やFBD言語の出力変数信号線の"o"の表記に対応します。

ファンクションブロック

※本章には、オブジェクト指向拡張されたファンクションブロックの説明を含みません。

ファンクションブロック(FB)は、ただ一つの処理が関連付けされた構造体、もしくはクラスに相当するPOUです。コンピュータサイエンスにおけるカプセル化の特徴をもちます。FBは、特定の種類のプログラム要素を作成するためのテンプレートとして機能し、属性(データ)と一つの処理をFBにまとめて定義し、管理できます。

// 呼び出すたびにカウントするFBの定義です。
// 属性nと、インクリメントする処理がまとめられています。
function_block Counter
	var_output n: int := 0; end_var
	//
	n := n + 1;
end_function_block

program Main
	var
		// Counter FBのインスタンスの宣言です。
		// c.nは0で初期化されます。
		c: Counter;
		v: int;
	end_var
	//
	c();
	v := c.n; // 1
	c();
	c();
	v := c.n; // 3
end_program

FBに定義した処理の実行は、FB呼び出し、又はFBインスタンス呼び出しと呼ばれるファンクション呼び出しに似た構文を用います。ファンクションとの違いの一つが、呼び出しを、FBインスタンスと呼ぶFBの実体に対して行うことです。FBインスタンスは、構造体の変数と同様、ユーザーが必要なときに定義し、FBインスタンスごとに独立した属性値(FBインスタンスメンバの値)の環境の元で処理を実行させることができます。また、その環境は呼び出しによって変更されることもあり、同じFBインスタンス、同じ入力であっても呼び出しの結果は、必ずしも一致しません。

構造体変数のメンバアクセスと同じように、FBインスタンスのメンバにもドット演算子を使用してアクセスできます(e.g. myfbinstance.outputvar)。ただし、デフォルトでアクセスできるのは、FBの入力変数や出力変数のみです。

// 平均値を計算するFBの定義です。
function_block Mean
	var_input x: lreal; end_var
	var_output output: lreal := 0.0; end_var
	var
		n: dint := 0;
		sum: lreal := 0.0;
	end_var
	//
	sum := sum + x;
	n := n + 1;
	output := sum / n;
end_function_block

program Main
	var
		// Mean FBのインスタンスの宣言です。
		a, b: Mean;
		m_a, m_b: lreal;
	end_var
	//
	a(2); a(3);
	b(2); b(3); b(5); b(7);
	//
	m_a := a.output; // 2.5=(2+3)/2
	m_b := b.output; // 4.25=(2+3+5+7)/4
end_program

FBの注意点

一般的なプログラミング言語の知識を持たれている方は、本節の内容に注意してください。

FB定義の記述方法

FB定義を一般的に次のように記述できます。

function_block <FB名>
	<引数変数の宣言>
	<変数の宣言>
	<処理>
end_function_block

<FB名>は、FBインスタンスを定義するときに使用する名前です。

<引数変数の宣言>には、以下に示す3つの引数変数を宣言することができます。

<変数の宣言>には、FB内で使用する変数を宣言します。内部変数(キーワードvar)、一時変数(var_temp)、および外部変数(var_external)を宣言できます。内部変数は呼び出しにおいて値を保持しますが、一時変数は呼び出しのたびに初期化されます。

function_block FBVarBehaviorSample
	var_output
		o: int := 1;
	end_var
	var
		v: int := 0;
	end_var
	var_temp
		t: int := 0;
	end_var
	//
	v := v + 100;
	t := t + 10;
	o := o + v + t;
end_function_block

program Main
	var
		x: FBVarBehaviorSample;
		y: int;
	end_var
	//
	x(o => y);
	// y=111=1+100+10
	x(o => y);
	// y=321=111+200+10
end_program

<処理>には、FBが実行する処理(プログラムコード)を記述します。制御文を記述できます。<処理>の終端に達するか、RETURN文が実行されると、呼び出し元に制御が戻ります。

FBインスタンス呼び出しの記述方法

FBインスタンス呼び出しの記述方法には、仮引数を記述するフォーマル呼び出しと、仮引数を省略するインフォーマル呼び出しがあります。インフォーマル呼び出しでは、すべての引数の値を記述しないといけないのに対し、フォーマル呼び出しでは、一部又は全ての引数の記述を省略できます。省略した引数の値は、前回呼び出したときの値が保持されるか、初回呼び出し時であれば、当該変数の初期値やデータ型のデフォルト値が処理系により設定されています。

function_block FBArgBehaviorSample
	var_input
		i1: int := 1;
		i2: int;
	end_var
	var_output
		o: int;
	end_var
	//
	o := i1 + i2;
end_function_block

program Main
	var
		x: FBArgBehaviorSample;
		y: int;
	end_var
	//
	x(o => y);
	// y=1; 1+0
	x(in1 := 2, in2 := 3, o => y);
	// y=5; 2+3
	x(in2 := 5, o => y);
	// y=7; 2+5
end_program
フォーマル呼び出し

FBインスタンスのフォーマル呼び出しは、一般的に次のように記述できます。

<FBインスタンス名>(<仮引数名1> := <実引数値1>, <仮引数名2> := <実引数値2>, ...)

例:

ton_instance(in := on, pt := time#5s, q => timeup, et => cur); // 出力変数の演算子には、=>を使用します。
ctu_int_instance(cu := counting, pv := 17, q => countup); // 引数を省略できます。

フォーマル呼び出しにおいて、入力変数と出力変数の引数を省略することができます。省略する場合、変数の値は、変数宣言時に指定した初期値、又は初期値を指定していなければ、データ型のデフォルト値です。入出力変数の引数を省略することはできません。また、出力変数の引数のみ演算子に=>を使用します(e.g. q => trig)。演算子からも分かる通り、呼び出し後に左辺値を右辺に代入します。BOOL型の出力変数の場合のみ使用できるnot bool_output => boolvの構文では、左辺値を否定した値を右辺に代入します※1

フォーマル呼び出しでのみen変数・eno変数を指定できます。インフォーマル呼び出しでは、en変数・eno変数を指定できません。

ctu_int_instance(
	en := no_error,
	cu := using_stock,
	pv := 19,
	not q => stock_available,
	not eno => no_error);
インフォーマル呼び出し

FBインスタンスのインフォーマル呼び出しは、一般的に次のように記述できます。

<FBインスタンス名>(<実引数値1>, <実引数値2>, ...)

例:

ton_instance(on, time#5s, trig, cur); // in, pt, q, et
ctu_int_instance(counting, false, 17, countup, counter); // cu, r, pv, q, cv

インフォーマル呼び出しの括弧内で引数を記述する順番は、FB定義時の引数変数定義の順番に一致します。en変数・eno変数を含まないことに注意してください。

function_block ArgumentsOrderSample
	var_input
		in1, in2: int;
		in3: int;
	end_var
	var_output
		q: bool;
	end_var
	var_input
		opt: int := 0;
	end_var
end_function_block

program Main
	var
		x: ArgumentsOrderSample;
		r: bool;
	end_var
	//
	x(
		2, // in1
		3, // in2
		5, // in3
		r, // q
		0 // opt
	);
end_program

※1 POU呼び出しにおけるnot bool_output => boolvの構文は、LD言語やFBD言語の出力変数信号線の"o"の表記に対応します。

FBインスタンスメンバへのアクセスの記述方法

FBインスタンスを介したメンバへのアクセスは、一般的に次のように記述できます。

<FBインスタンス名>.<ローカル変数名>

例:

// 入力変数へのアクセス
ton_instance.en := true;
ton_instance.in := on;
ton_instance.pt := time#5s;
// インスタンス呼び出し
ton_instance();
// 出力変数へのアクセス
timeup := ton_instance.q;
cur := ton_instance.et;
no_error := ton_instance.eno;

// 上記は、以下のプログラムコードと等価です。
// ton_instance(en := true, in := on, pt := time#5s, q => timeup, et => cur, eno => no_error);

デフォルトでは、内部変数にはアクセスできません。public修飾子のある内部変数にはアクセスできます。

function_block AccessModifierSample
	var
		private_v: int;
	end_var
	var public
		public_v: int;
	end_var
end_function_block

program Main
	var
		x: AccessModifierSample;
	end_var
	//
	x.private_v := 2; // Compile time error! Inaccesible element.
	x.public_v := 3; // ok
end_program

なお、アクセス修飾は静的変数に対してのみ可能です。入力変数・出力変数は暗黙的に常にpublic、外部変数は暗黙的に常にprotected、一時変数は暗黙的に常にprivateです。入出力変数について、アクセス修飾子の概念があるかないかは実装依存ですが、呼び出した内部でのみアクセス可能です。

プログラム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_task with t: Main();
	end_resource
	var_config
		rsc.main_task.v1.n at %IW5 : int := 16;
		rsc.main_task.v2.n at %IW6 : int := 17;
	end_var
end_configuration

at属性の*指定については、FBだけでなく、プログラムPOU内の変数に対しても使用できます。出力変数、静的変数、グローバル変数に使用できます。プログラム実行前に、*が設定されているすべての変数に対して、at属性が設定されていなければ異常です。%I*の表現については、他にも特性に応じて、%I*, %QW*などを使用できます。※1

※1 *を含むパス文字列は、部分的にat属性を設定するという意味で、規格上は「部分指定」という表現が使用されています。全てを省略する%*という表現は、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(); // 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の規定に"演算子"として定義されていないものです。一般的なプログラミング言語で演算子として定義されているものは筆者の判断で含めています。

値渡しと参照渡し

呼び出しにおいて、入力変数(var_input)と出力変数(var_output)は値渡しであり、入出力変数(var_in_out)は参照渡しです※1。入出力変数は、C++言語の参照変数と同等の機能をもちます。

以下の例に示す通り、入出力変数への書き込みは、呼び出し元の変数への書き込みになります。

type
	t: struct
		m: int;
	end_struct;
end_type

function call_by_ref_sample
	var_in_out
		rintv: int;
		rstrv: string;
		rtv: t;
	end_var
	//
	rintv := 4;
	rstrv := 'five';
	rtv.m := 6;
end_function

program Main
	var
		intv: int := 1;
		strv: string := 'two';
		tv: t := (m := 3);
	end_var
	//
	// intv=1, strv='two', tv.m=3
	call_by_ref_sample(
		rintv := intv,
		rstrv := strv,
		rtv := tv
	);
	// intv=4, strv='five', tv.m=6
end_program

参照渡しは、呼び出し元の変数の値を変更する必要がある機能の実現で有用です。また、一般的な参照渡しの特性として文字列型、配列、構造体型などの大きなデータを比較的高速に渡すことができます。

※1 IEC 61131-3には、入出力変数が参照渡しであるとは明確には記載されていません。しかし、呼び出し元の変数に作用あること、入出力変数による多態性の特性あること、ファンクションの可変長配列の宣言が入出力変数にのみ許されていることから参照渡しでの実現が望ましいと判断しています。他の実現方法として、呼び出し直前に値を渡し、呼び出し直後に値を受け取ることで、参照渡しに似た機能を実現できます。この実現方法の場合、呼び出しの処理中には呼び出し元の変数に作用がない特性があります。また、書き込みが一回多いため高速化どころか低速化します。

呼び出し

(準備中)

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

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

メンバアクセス

(準備中)

配列アクセス

(準備中)

リファレンス

(準備中)

デリファレンス

(準備中)

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

(準備中)

加算・減算

(準備中)

乗算・除算・剰余算

(準備中)

べき乗

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

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

TO_SINT, TO_INT, TO_DINT, TO_LINT, TO_USINT, TO_UINT, TO_UDINT, TO_ULINT, TO_REAL, TO_LREAL, TO_BOOL, TO_BYTE, TO_WORD, TO_DWORD, TO_LWORD, TO_TIME, TO_LTIME, TO_DATE, TO_LDATE, TO_TOD, TO_LTOD, TO_DT, TO_LDT, TO_STRING, TO_WSTRING, TO_CHAR, 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

自作ライブラリ

JiecLib@GitHubに有用・無用含めて自作のライブラリを公開しています。ツールの試用期間活用し、オムロン、キーエンスにて動作確認しています。不具合ありましたら、こちらのブログにご連絡いただければ可能な範囲で対応致します。

本書のプログラム例などで示されるIEC 61131-3規格のテキスト表現をIEC 61131-10規格のXML表現に変換するツールJieccも使用ください。いくつかのベンダーでインポートできるようになります。

疑似乱数

Xorshift32

see JiecLib

Mersenne Twister

see JiecLib

配列操作

配列要素数取得

see JiecLib

リングバッファ

see JiecLib

ソート

バブルソート

see JiecLib

文字列操作

文字列操作に関するIEC 61131-3の特徴として、文字のインデクスは0始まりではなく、1始まりです。例えば、FIND('abcde', 'cd') は3を得ます。

🛈ASCII範囲外の文字の操作については、IEC 61131-3規格の範囲では限りがあります。ASCII範囲内で運用することをお勧めします。ASCII範囲外の文字を操作するには、ベンダー提供の組み込みPOUを使用するか、ベンダーの文字列型の文字コードが明確であれば、自作することを検討してください。

文字列の長さを取得する

see JiecLib

指定位置の文字を取得する

指定位置の文字を取得する方法は、ベンダーの準拠状況で異なります。

添字アクセスをサポートしているベンダーの場合:

P番目の文字を取得するには、次のようにします。

var
    strv: string;
    p: dint;
    c: char;
end_var
//
strv := 'abcdefg';
p := 1;
c := strv[p]; // 'a'
p := 2;
c := strv[p]; // 'b'
添字アクセスをサポートしていないベンダーの場合:

see JiecLib

文字列が等しいか調べる

文字列が等しいか調べるには、等価比較演算子=を使用するか、EQファンクションを使用します。

strv := 'xyz';
boolv := strv = 'xyz'; // true
boolv := strv = 'XYZ'; // false
boolv := EQ(strv, 'xyz'); // true

空文字列か調べる

see JiecLib

文字列を結合する

see JiecLib

文字列型配列を指定文字列で結合する

see JiecLib

文字列を指定文字列で分割する

see JiecLib

指定範囲の文字列を切り出す

文字列を切り出すには、MIDファンクションを使用します。

P番目の文字からL文字切り出すには次のようにします。

strv1 := 'abcdef';
p := 2;
l := 3;
strv2 := MID(strv1, l, p); // 'bcd' 2番目の文字から3文字

P番目の文字から末尾まで切り出すには次のようにします。

strv1 := 'abcdef';
p := 2;
strv2 := MID(strv1, LEN(strv1), p); // 'bcdef' 2番目の文字から末尾まで

P1番目からP2番目までを切り出すには次のようにします。

strv1 := 'abcdef';
p1 := 2;
p2 := 5;
strv2 := MID(strv1, p2 - p1 + 1, p1); // 'bcde' 2番目の文字から5番目までの文字

指定範囲の文字列を削除する

P番目の文字からL文字削除するには次のようにします。

strv1 := 'abcdef';
p := 2;
l := 3;
strv2 := DELETE(strv1, l, p); // 'aef' 2番目の文字から3文字を削除

P番目の文字から末尾まで削除するには次のようにします。

strv1 := 'abcdef';
p := 3;
strv2 := DELETE(strv1, LEN(strv1), p); // 'ab' 3番目の文字から末尾までを削除

P1番目からP2番目までの文字を削除するには次のようにします。

strv1 := 'abcdef';
p1 := 2;
p2 := 5;
strv2 := DELETE(strv1, p2 - p1 + 1, p1); // 'af' 2番目の文字から5番目までの文字を削除

指定の文字列を削除する

see JiecLib

両端の空白を削除する

see JiecLib

左端の空白を削除する

see JiecLib

右端の空白を削除する

see JiecLib

文字列を検索する

see JiecLib

文字列を後ろから検索する

(準備中)

文字列が含まれるか調べる

see JiecLib

指定の文字列で始まるか調べる

see JiecLib

指定の文字列で終わるか調べる

see JiecLib

指定位置に文字列を挿入する

P番目の文字の位置に文字列を挿入するには、INSERTファンクションを使用します。

strv := 'abc';
p := 2;
inserted := INSERT(strv, 'xyz', p); // 'axyzbc'

指定の文字列を置換する

see JiecLib

文字列を逆順に並び替える

see JiecLib

文字列を辞書順で比較する

(準備中)

文字の文字コードを取得する

文字の文字コードを取得するIEC 61131-3規格標準のファンクションは提供されていません。

添字アクセスをサポートしているベンダーの場合:

次のようにします。

strv := 'Ab4';
code := BYTE_TO_USINT(CHAR_TO_BYTE(strv[1])); // 16#41 'A'
code := BYTE_TO_USINT(CHAR_TO_BYTE(strv[2])); // 16#62 'b'
code := BYTE_TO_USINT(CHAR_TO_BYTE(strv[3])); // 16#34 '4'

※ASCII範囲内の文字に限定しています。

添字アクセスをサポートしていないベンダーの場合:
⚠️IEC 61131-3標準範囲の方法では、文字列中から目的の1文字を抽出し、文字・文字コード対照表相当から、文字コードを引くことになります。。。ベンダー提供の組み込みPOUを使用しましょう。

see JiecLib

大文字・小文字へ変換する

see JiecLib

文字列を数値へ変換する

(準備中)

数値を文字列へ変換する

(準備中)

文字列のバイトサイズを取得する

(準備中)

文字列をバイト配列に変換する

(準備中)

バイト配列を文字列に変換する

(準備中)

統計関数

see JiecLib

APPENDIX

IEC 61131-3にないもの