Beckhoff#TRY・CATCH・FINALLY・ENTRYを使ってみよう

今回の記事ではBeckhoff 4026で64Bit OSでも対応可能なTRY・CATCH・FINALLY・ENTRYを説明します。

さ、FAを楽しもう。

TRY・CATCH・FINALLY・ENTRY?

これらの演算子はIEC 61131-3規格を拡張したもので、IECコードの例外処理に使用されます。try、catch、finallyは、プログラミングの例外処理に使われる構文であり、JavaやJavaScriptなどの言語でよく見られます。

必須プラットフォーム

注意するのは4024バージョンで32 BitOSのみで使用可能です。

  • TC3.1 Build 4024 for 32-bit runtime systems
  • TC3.1 Build 4026 for 64-bit runtime systems

基本的な使い方

ここでは、これらの仕組みを見ていきましょう。

try block

try blockでは例外をスローする可能性のあるコードを記述します。例外がスローされなければ、catch blockはスキップされる。

catch block

try block内で例外が発生した場合、このブロック内のプログラムが実行されます。これによって例外を処理し、プログラムがクラッシュするのを防ぐことができます。

finally block

finally blockは、例外がスローされたかどうかに関係なく、tryとcatchの後に実行される。通常、クリーンアップ・タスクやリソースの解放に使われます。

キーポイント

先ほど書いてた基本的な使い方からまとめると、TRY・CATCH・FINALLYをうまく使用するにはこちらのポイントに気をつけましょう。

  • try Blockは例外をスローする可能性のあるコードを含み、catch Blockは例外を処理します。
  • finally Blockは例外が発生したかどうかに関係なく実行されるので、クリーンアップ処理に便利です。
  • try、catch、finallyを使うことで、エラーをうまく処理できる堅牢なプログラムを書くことができます。

Statements

こちらはTRY・CATCH・FINALLYのStatementsになります。

__TRY
    <try_statements>

__CATCH(exc)
    <catch_statements>

__FINALLY
    <finally_statements>

__ENDTRY

<further_statements>

最初に __Try の下に表示されるプログラムでエラーの例外が発生しても、PLC プログラムは停止しません。

その代わり、_CATCH以下の命令を実行し、例外処理を開始します。

そして、__FINALLY以下のプログラムが実行されます。注意するのは__FINALLYの下にある命令は常に実行されるので、すなわち、_TRYの下にある命令は例外をスローしなくても実行されます。

例外処理は __ENDTRY で終了し、その後、PLC プログラムは後続の命令 (__ENDTRY より後の命令) を実行し続けます。

__SYSTEM.ExceptionCode

エラー例外を表す IEC 変数のデータ型は __SYSTEM.ExceptionCodeは以下になります。。このような例外は、例えば __TRY, __CATCH, __FINALLY, __ENDTRY によって捕捉されます。ExceptionCodeデータ型を使用して、アプリケーション・コード自体で発生したエラーや例外を分類し、ログに記録することも可能です。

TYPE ExceptionCode :
(
    RTSEXCPT_UNKNOWN                                := 16#FFFFFFFF,
    RTSEXCPT_NOEXCEPTION                            := 16#00000000,
    RTSEXCPT_WATCHDOG                               := 16#00000010,
    RTSEXCPT_HARDWAREWATCHDOG                       := 16#00000011,
    RTSEXCPT_IO_CONFIG_ERROR                        := 16#00000012,
    RTSEXCPT_PROGRAMCHECKSUM                        := 16#00000013,
    RTSEXCPT_FIELDBUS_ERROR                         := 16#00000014,
    RTSEXCPT_IOUPDATE_ERROR                         := 16#00000015,
    RTSEXCPT_CYCLE_TIME_EXCEED                      := 16#00000016,
    RTSEXCPT_ONLCHANGE_PROGRAM_EXCEEDED             := 16#00000017,
    RTSEXCPT_UNRESOLVED_EXTREFS                     := 16#00000018,
    RTSEXCPT_DOWNLOAD_REJECTED                      := 16#00000019,
    RTSEXCPT_BOOTPROJECT_REJECTED_DUE_RETAIN_ERROR  := 16#0000001A,
    RTSEXCPT_LOADBOOTPROJECT_FAILED                 := 16#0000001B,
    RTSEXCPT_OUT_OF_MEMORY                          := 16#0000001C,
    RTSEXCPT_RETAIN_MEMORY_ERROR                    := 16#0000001D,
    RTSEXCPT_BOOTPROJECT_CRASH                      := 16#0000001E,
    RTSEXCPT_BOOTPROJECTTARGETMISMATCH              := 16#00000021,
    RTSEXCPT_SCHEDULEERROR                          := 16#00000022,
    RTSEXCPT_FILE_CHECKSUM_ERR                      := 16#00000023,
    RTSEXCPT_RETAIN_IDENTITY_MISMATCH               := 16#00000024,
    RTSEXCPT_IEC_TASK_CONFIG_ERROR                  := 16#00000025,
    RTSEXCPT_APP_TARGET_MISMATCH                    := 16#00000026,
    RTSEXCPT_ILLEGAL_INSTRUCTION                    := 16#00000050,
    RTSEXCPT_ACCESS_VIOLATION                       := 16#00000051,
    RTSEXCPT_PRIV_INSTRUCTION                       := 16#00000052,
    RTSEXCPT_IN_PAGE_ERROR                          := 16#00000053,
    RTSEXCPT_STACK_OVERFLOW                         := 16#00000054,
    RTSEXCPT_INVALID_DISPOSITION                    := 16#00000055,
    RTSEXCPT_INVALID_HANDLE                         := 16#00000056,
    RTSEXCPT_GUARD_PAGE                             := 16#00000057,
    RTSEXCPT_DOUBLE_FAULT                           := 16#00000058,
    RTSEXCPT_INVALID_OPCODE                         := 16#00000059,
    RTSEXCPT_MISALIGNMENT                           := 16#00000100,
    RTSEXCPT_ARRAYBOUNDS                            := 16#00000101,
    RTSEXCPT_DIVIDEBYZERO                           := 16#00000102,
    RTSEXCPT_OVERFLOW                               := 16#00000103,
    RTSEXCPT_NONCONTINUABLE                         := 16#00000104,
    RTSEXCPT_PROCESSORLOAD_WATCHDOG                 := 16#00000105,
    RTSEXCPT_FPU_ERROR                              := 16#00000150,
    RTSEXCPT_FPU_DENORMAL_OPERAND                   := 16#00000151,
    RTSEXCPT_FPU_DIVIDEBYZERO                       := 16#00000152,
    RTSEXCPT_FPU_INEXACT_RESULT                     := 16#00000153,
    RTSEXCPT_FPU_INVALID_OPERATION                  := 16#00000154,
    RTSEXCPT_FPU_OVERFLOW                           := 16#00000155,
    RTSEXCPT_FPU_STACK_CHECK                        := 16#00000156,
    RTSEXCPT_FPU_UNDERFLOW                          := 16#00000157,
    RTSEXCPT_VENDOR_EXCEPTION_BASE                  := 16#00002000,
    RTSEXCPT_USER_EXCEPTION_BASE                    := 16#00010000
) UDINT ;
END_TYPE

メリット?

try、catch、finallyを使うと、プログラミングにおいていくつかの利点があります。

✓簡素化されたエラー処理

これにより、プログラムをクラッシュさせることなく例外を優雅に処理することができ、エラー管理が容易になります。

✓コードの明確性

Catch Blockの中で例外をどのように処理すべきかを明示することで、コードはより読みやすくなり、またエラーの場合に取られるアクションが明確に示されます。

✓資源管理

finally Blockは、ファイルやデータベース接続などのリソースが適切に解放されることを明確にプログラムでき、リソース漏れを防ぐのに役立ちます。

✓複数の例外処理

異なるタイプの例外を個別にキャッチし、処理することができます。これにより、特定のエラーに合わせた対応が可能になります。

✓より容易なデバッグ

例外が発生すると、例外の詳細をキャプチャしてログに記録できるため、問題の特定とデバッグが容易になります。

✓アプリケーションの安定性の向上

適切な例外処理を実装することで、アプリケーション全体の安定性が向上し、より良いユーザーエクスペリエンスにつながります。

Implementation

これからTRY・CATCH・FINALLY・ENTRYを利用しRUNTIMEの停止につながるエラーをCATCHし、Global Variable Listに記録しておきましょう。

DUT_History

こちらは100個のExceptionCodeを配列として定義し、例外が発生した場合のエラー内容を記録します。nExcIndexは次に記録される配列のIndexになります。

TYPE DUT_History :
STRUCT
    nExcIndex          : UINT:=0;
    aExceptionHistory  : ARRAY[0..99] OF __SYSTEM.ExceptionCode;
END_STRUCT
END_TYPE

F_ExceptionHisotryStack

次はCatchした例外を記録するFunction Blockを作成します。

VAR

こちらはFBのInterfaceになります。VAR_IN_OUTで先程定義したDUT_Historyをそのまま操作します。

FUNCTION F_ExceptionHisotryStack : BOOL
VAR_INPUT
InexcInput           : __SYSTEM.ExceptionCode;
END_VAR
VAR_OUTPUT

END_VAR
VAR_IN_OUT
ioData :DUT_History;
END_VAR
VAR
_MaxIndex:DINT;
END_VAR

Program

こちらはプログラムの中身です。UPPER_BOUNDで配列のIndexを取り出し、これから記録するIndexが最大Indexより小さい場合、Exception 情報を格納し、Indexを+1にします。

_MaxIndex:= UPPER_BOUND(ioData.aExceptionHistory,1);


IF ioData.nExcIndex <=_MaxIndex THEN
ioData.aExceptionHistory[ioData.nExcIndex]:=InexcInput;
ioData.nExcIndex:=ioData.nExcIndex+1;
END_IF

MAIN

最後はMAINプログラムで、無効のポインタ・アクセスと0を除算するエラーが入っています。

PROGRAM MAIN
VAR
History:DUT_History;
END_VAR
VAR
    nCounter_TRY1      : UINT;
    nCounter_TRY2      : UINT;
    nCounter_CATCH     : UINT;   
    nCounter_FINALLY   : UINT;

    exc                : __SYSTEM.ExceptionCode;
    lastExc            : __SYSTEM.ExceptionCode;

    pSample            : POINTER TO BOOL;
    bVar               : BOOL;
    nSample            : INT := 100;
    nDivisor           : INT;
END_VAR

__TRY
    nCounter_TRY1 := nCounter_TRY1 + 1;
    pSample^      := TRUE;                // null pointer access leads to “access violation” exception
    nSample       := nSample/nDivisor;    // division by zero leads to “divide by zero” exception
    nCounter_TRY2 := nCounter_TRY2 + 1;

__CATCH(exc)
    nCounter_CATCH    := nCounter_CATCH + 1;

    // Exception logging
    lastExc := exc;
    F_ExceptionHisotryStack(InexcInput:=exc,ioData:=History);

    // fix the error
//correct the Pointer
IF (exc = __SYSTEM.ExceptionCode.RTSEXCPT_ACCESS_VIOLATION) AND (pSample = 0) THEN
pSample  := ADR(bVar);
//corret the Divide value
ELSIF ((exc = __SYSTEM.ExceptionCode.RTSEXCPT_DIVIDEBYZERO) OR (exc = __SYSTEM.ExceptionCode.RTSEXCPT_FPU_DIVIDEBYZERO)) AND (nDivisor = 0) THEN
nDivisor := 1;
END_IF

__FINALLY
    nCounter_FINALLY := nCounter_FINALLY + 1;
__ENDTRY

Error

こちらは無効のポインタ・アクセスと0を除算するプログラム部分になり、__TRY Blockの中で実行されています。

Catch

__TRY Blockで発生した例外が__CATCHに取られ、1つ目の例外はポインタpSampleの値補正、2つ目の例外は除数nDivisorの値を補正します。またこの2つの例外は、例外履歴を作成するためのグローバル配列に保存されています。

なので、結果的には配列aExceptionHistoryは以下の値を持ちます。

  • aExceptionHistory[0] = RTSEXCPT_ACCESS_VIOLATION
  • aExceptionHistory[1] = RTSEXCPT_DIVIDEBYZERO
  • aExceptionHistory[2] to aExceptionHistory[99] = RTSEXCPT_NOEXCEPTION

Result

この通り、無効のポインタ・アクセスと0を除算するエラーが発生したとしてもTwinCAR Runtimeは止まりません。

Global Variable Listにはプログラム内に発生した例外が保存されてることがわかります。

Footer_Basic

Please Support some devices for my blog

Amazon Gift List

Find ME

Twitter:@3threes2
Email:soup01threes*gmail.com (* to @)
YoutubeChannel:https://www.youtube.com/channel/UCQ3CHGAIXZAbeOC_9mjQiWQ

シェアする

  • このエントリーをはてなブックマークに追加

フォローする