IEC 61131-3は、FA(Factory Automation)のプログラミング言語に関する国際規格です。次の特徴があります。
一方、安全性を重視しているため、関数の再帰呼出しを制限していたり、関数ポインタを含むポインタ型を代替する機能が制約されていたりと機能的な自由度は高くありません。「APPENDIX. IEC 61131-3にないもの」も参照ください。
日本のFAにおけるIEC 61131-3の浸透は十分とは言えません。従来のアドレスプログラミングやラダーによる巻物スタイルのプログラミングは根強く残っており、IEC 61131-3が良いだろうことは感じていても、約20年もの間、IEC 61131-3への移行は進んでいません。
これには、IEC 61131-3規格側の問題、PLCメーカー側の問題、およびユーザー側の様々な事情があります。
これらの問題が解消されることで、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演算子を含めていません。
識別子は、変数やデータ型などを識別するための名前であり、次のすべてを満たさなければいけません。
例えば、`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の例 | 説明 |
---|---|---|---|---|---|---|
符号あり整数型 | SINT | 8 | -128~127 | 0 | -59 ,13 ,1_234_567 ,2#11_01 ,-16#2f
| |
INT | 16 | -32768~32767 | ||||
DINT | 32 | -2147483648~2147483647 | ||||
LINT | 64 | -9223372036854775808~9223372036854775807 | ||||
符号なし整数型 | USINT | 8 | 0~255 | |||
UINT | 16 | 0~65535 | ||||
UDINT | 32 | 0~4294967295 | ||||
ULINT | 64 | 0~18446744073709551615 | ||||
浮動小数点型 | REAL | 32 | IEC 60559単精度浮動小数点規格の範囲に従う | 0 | 0.1 ,1.0e-6 ,-1e3 ,-5 ,16#ff | |
LREAL | 64 | IEC 60559倍精度浮動小数点規格の範囲に従う | ||||
ビット列型 | BYTE | 8 | 16#00~16#ff | 16#00 | BYTE#16#ff ,BYTE#2#1111_1111_1111_1111 ,DWORD#16#cafe_babe | |
WORD | 16 | 16#0000~16#ffff | 16#0000 | |||
DWORD | 32 | 16#0000_0000~16#ffff_ffff | 16#0000_0000 | |||
LWORD | 64 | 16#0000_0000_0000_0000~16#ffff_ffff_ffff_ffff | 16#0000_0000_0000_0000 | |||
ブール型 | BOOL | 1 | FALSE(偽)、又はTRUE(真) | FALSE | TRUE ,BOOL#0 | |
時間型 | TIME | 実装依存 | 実装依存 | 0秒 | TIME#1s ,T#1s ,LTIME#1s ,LT#1s ,LT#1d2h3m4s5ms6us7ns ,LT#1.234567ms ,LT#1m234us567ns | 最小単位も含めて実装依存のため、TIME型ではなく、LTIME型の使用を推奨します。 |
LTIME | 64 | -9223372036854775808ナノ秒~9223372036854775807ナノ秒 最小単位はナノ秒 | ||||
日付型 | DATE | 実装依存 | 実装依存 | 実装依存 e.g. 1970年1月1日 | DATE#1978-9-7 ,D#2022-11-07 ,LDATE#1978-9-7 ,LD#2554-07-21 | |
LDATE | 64 | 1970年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_DAY | 64 | 0時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_TIME | 64 | 1970年1月1日0時0分0秒~2554年7月21日23時34時33.709551615s 最小単位はナノ秒 | 1970年1月1日0時0分0秒 | |||
LDT | ||||||
文字型 | CHAR | 8 | ISO/IEC 10646規格に従う一文字。 | NULL文字('$00' ) | 'a' ,'→' ,'$00' // NULL文字 , | 文字符号化方式は、UTF-8。 |
WCHAR | 16 | ISO/IEC 10646規格に従う一文字。 | NULL文字("$0000" ) | "a" ,"→" ,"$0000" // NULL文字 | 文字符号化方式は、UTF-16。 | |
文字列型 | STRING | 8*N | ISO/IEC 10646規格に従う0文字以上の文字の列。 | 空文字列('' ) | 'This is a STRING literal.' ,'これは文字列リテラルです。' ,'abc' ,'$61$62$63' // = 'abc' ,'' // 空文字列 | 文字符号化方式は、UTF-8。 |
WSTRING | 16*N | ISO/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#A
やrealv=1.036831936e+9
を得ます。
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
ラダー言語におけるパワーフローや、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#eRed
、Day#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_BOUND
、UPPER_BOUND
を使用します。
LOWER_BOUND
、UPPER_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
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
(準備中)
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 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;
(準備中)
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
(準備中)
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.
(準備中)
sum := 0; n := 1; WHILE n <= 4 DO sum := sum + n; n := n + 1; END_WHILE; // sum = 10 = 1 + 2 + 3 + 4, n = 5.
(準備中)
sum := 0; n := 1; REPEAT sum := sum + n; n := n + 1; UNTIL n = 5 END_REPEAT; // sum = 10 = 1 + 2 + 3 + 4, n = 5.
(準備中)
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.
(準備中)
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.
(準備中)
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;
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 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