HOME > ソフテックだより > 第61号(2008年3月5日発行) 技術レポート「見やすい・わかりやすいラダーにするために4 〜構造化プログラミング〜」

「ソフテックだより」では、ソフトウェア開発に関する情報や開発現場における社員の取り組みなどを定期的にお知らせしています。
さまざまなテーマを取り上げていますので、他のソフテックだよりも、ぜひご覧下さい。

ソフテックだより(発行日順)のページへ
ソフテックだより 技術レポート(技術分野別)のページへ
ソフテックだより 現場の声(シーン別)のページへ


ソフテックだより 第61号(2008年3月5日発行)
技術レポート

「見やすい・わかりやすいラダーにするために4 〜構造化プログラミング〜」

1. はじめに

いままでの技術レポートで「見やすい・わかりやすいラダーにするために」ということでラダープログラムを作る際に工夫していることをご紹介させていただきました。
今回はラダープログラムの構造化プログラミングについてソフテックが実践している手法をご紹介させていただきます。
ちなみにラダーとはラダー言語のことで、多くのPLC(programmable logic controller)で採用されている言語です。

(1). 構造化プログラミングとは

構造化プログラミングとは、1960年代後半にオランダの情報工学者エドガー・ダイクストラ(Edsger Wybe Dijkstra)氏らによって提唱されており、以下のように説明できるそうです。(論文「構造化プログラミング」の翻訳版から)

  • 一つの入口と一つの出口を持つプログラムは、順次・選択・反復の3つの論理構造によって記述できる。
  • プログラム全体を大まかな機能単位に分割し、その中をさらに細かい機能単位に分割していくことにより、記述内容を段階的に詳細化していく。
  • 分割された機能単位は必ず一つの入口と一つの 出口を持ち、単位の連結・組み合わせは必ず3つの基本構文のみを使って行なう。
  • この原則に従うことにより、大規模なプログラムを効率よく、少ないミスで設計・記述できる。

構造化プログラミングは1つの設計思想です。
1つの設計思想に則って製作されたソフトは効率よく、少ないミスで設計・記述できるほか、可読性が高く、急な仕様変更にも柔軟に対応することができます。

構造化プログラミングといえばC言語などの高級言語の話でラダーでは無縁と思われる方もおられるかもしれません。
しかし、ソフテックでは『複数人での製作』、『資産の引継ぎ』、『現地での立ち上げ』といったそれぞれのフェーズを経てくる中で必要に駆られて構造化プログラミングを採用するに至っています。

今回はその一部をご紹介させていただきたいと思います。

2. FOR〜NEXT文

[FOR〜NEXT文]は反復処理を記述するときに大変有効です。
しかし、ラダーの[FOR〜NEXT文]はもともと他の高級言語に比べると非常に見づらい仕様になっています。さらに[FOR〜NEXT文]の使い方によって見づらさが大きく左右されます。

(1). インデックス修飾に気をつける

[FOR〜NEXT文]の中では[インデックスレジスタ(Z0 など)]を使用してメモリアドレスをインデックス修飾することで反復処理を実現します。
ここで最も気をつけなければならないことは『インデックス修飾で間違ったアドレスを参照する』ことです。

ここで躓いてしまうと全く予測できないアドレスに不定な値を書き込んでしまい(メモリ破壊)、そういったバグを見つけることには大変な労力が必要です。

以下の工夫でこの問題は大きく軽減できます。

  • インデックスレジスタを直接参照しない
    インデックスレジスタを直接参照しない(×の例)
    インデックスレジスタを直接参照しない(○の例)

    上の例では[インデックスレジスタ(Z0)]を直接操作するのではなく、[データレジスタ(D0)]をインクリメントしています。
    このようにすることで[FOR〜NEXT文]毎にインデックス修飾値を持つことができ、別の[FOR〜NEXT文]で使用した[インデックスレジスタ]の値を気にしなくて良くなります。

    [FOR〜NEXT文]のループ回数が変化する場合には、その値に上下限のリミットを設けることも重要です。メモリ破壊の原因はループ回数が予定よりも多くなってしまったという場合が多いです。

    また、上記の例には当てはまりませんが『[インデックスレジスタ]の値を複数スキャンにまたがって保持する』ということは最もやってはならないことです。
    (点数の少ない[インデックスレジスタ]を一つの処理で占有してしまうため。)

  • 使用する[インデックスレジスタ]にルールを設ける
    使用する[インデックスレジスタ]にルールを設ける(×の例)
    使用する[インデックスレジスタ]にルールを設ける(○の例)

    [FOR〜NEXT文]では必ず先頭アドレスから連番で使用するようにします。
    また、サブルーチン、割り込みで使用する[インデックスレジスタ]も専用のアドレスを決めておきます。

    [インデックスレジスタ]の使用には最新の注意が必要です。
    これらのルールに則ってコーディングすることで、コーディングミスを軽減することができます。別の人に見てもらう場合にも、コーディングが統一されている方がその人のストレスを軽減できます。

    ここに紹介した方法を取り入れることで、問題が発生したときにインデックス修飾の間違いによるメモリ破壊を疑う心配はなくなります。また、疑う場合でも条件が限られるので余計な労力をかける必要がなくなります。

(2). FOR〜NEXT文の処理はシンプルに

ラダーの[FOR〜NEXT文]はもともと見づらいものです。しかし、以下の工夫によって [見やすい]そして[変更しやすい]ソフトにすることが出来ます。

  • 全てのループで[共通の判定]とループ毎の[個別の条件]を切り分ける。
  • 1つの[FOR〜NEXT文]を記述する場合、[入力部]を設ける。

[入力部]には[個別の条件]を記述します。

[入力部]には[個別の条件]を記述します(×の例)
[入力部]には[個別の条件]を記述します(○の例)

[FOR〜NEXT文]のなかで無理やり条件を作りこむことで非常に見づらいソフトになってしまいます。

ループさせたい条件をよく解析してみると実は非常に単純で、複雑な条件は[入力部]で一つのビットにまとめることができ、コーディングが楽になります。また、急な変更にも柔軟に対応できます。

(3). ブレイクやジャンプを使用しない

『データの検索』といった処理を実現するために [FOR〜NEXT文]を使用することがあります。

このときに[ブレイク命令]や[ジャンプ命令]の使用は避けるべきです。
主に『処理速度の向上』を目的としてこれらを使用するケースがあります。

しかし、[ブレイク命令]や[ジャンプ命令]を使って[FOR〜NEXT文]の処理速度を変えてしまうと『ループ回数が少ないときは問題が無いが、ループ回数が増えるとプログラム全体のスキャンタイムが長くなり、外部入力信号を取りこぼしてしまう。』という危険性が出てきます。

これはループ回数が多くなるほどこの影響が大きくなります。
処理速度の向上を図ったのに、逆にタイミングに依存する際どいバグを残してしまうというのはとても残念なことです。
PLCは『処理速度の向上』よりも『処理速度の安定性』の方が重要な場合が多いです。

(4). [FOR〜NEXT文]のまとめ

[FOR〜NEXT文]は『コーディング負荷の軽減』『プログラム容量の軽減』といった意味では非常に便利である反面、『見づらい』『変更しにくい』といった要素もあります。
しっかりとした設計思想の統一ができていなければ、数年前に自分で作ったソフトですら解析が困難になります。他の人に見てもらうとなると少しでも見やすくしておきたいものです。

上記を考慮するだけで一気に『見やすい』『変更しやすい』『制限の少ない』ソフトになったのではないでしょうか。

3. サブルーチン

[サブルーチン]もある意味では反復処理です。
特定の処理が複数のプログラムで必要な場合に、その処理を一本化できるところに利点があります。
また、コーディングやデバック、試験の効率を高めるために、ある程度まとまった処理をサブルーチン化して1つの処理を簡潔にする目的で使用する場合もあります。

(1). インデックス修飾に気をつける

[FOR〜NEXT文]のところでも紹介しましたが、インデックス修飾はメモリ破壊を起こす可能性があるので大変重要です。

通常処理で使用するインデックスレジスタとサブルーチンや割り込み処理で使用するインデックスレジスタは分けておく必要があります。

(2). サブルーチンとのインターフェイスは簡潔に

ラダーのサブルーチンも高級言語のように[引数]と[戻り値]を明確に定義しておく必要があります。

ラダーのサブルーチンも高級言語のように[引数]と[戻り値]を明確に定義しておく必要があります(○の例)

せっかく処理の共通化を図ったサブルーチンも[引数]と[戻り値]のインターフェイスになるメモリアドレスを用意していなければ汎用性に欠けてしまい、他の処理での流用に障害が出てしまいます。

サブルーチンへの引数も、渡しやすい並びになるように呼び出し元で配慮することもプログラムを簡潔にする方法の一つです。

サブルーチンへの引数も、渡しやすい並びになるように呼び出し元で配慮することもプログラムを簡潔にする方法の一つです(×の例)
サブルーチンへの引数も、渡しやすい並びになるように呼び出し元で配慮することもプログラムを簡潔にする方法の一つです(○の例)

また、サブルーチンで使用するメモリは『20WORD』『40WORD』などキリのよい量で区切っておき、その中で『 [引数]は先頭、その次に[戻り値]』とここにもルールを持たせることで『見やすさ』が向上します。

(3). サブルーチンのまとめ

ラダーではサブルーチンを用いずに制御の順番にベタ書きする習慣が多いようです。

サブルーチンは『データレンジの変換』や『複雑な方程式の計算』など複数個所で流用可能な計算式では非常に有効です。
ただし、高級言語のような感覚で全てをサブルーチン化してしまうとモニタを行いにくくなったり、可読性が悪くなったりとラダーの良さを損なうことになります。

適材適所での使用が肝心です。

4. 外部入出力

[外部入出力デバイス(Xデバイス、Yデバイス)]はPLCとハードウェアのインターフェイスです。

PLCはパソコン上で動作するソフトウェアと異なり、ハードウェアと密接しているので、この[外部入出力]によるインターフェイスには点数の余裕を見込んでおく必要があります。

社内で製作したソフトを現地のハードと組み合わせて試運転を進めていくと、どうしても修正が必要な箇所が出てくるものです。

そこで『[外部入出力] を直接参照しない』という工夫が必要になってきます。

(1). 論理の反転と検出の遅延

メカに取り付けられた[位置センサ]やタンクに取り付けられた[レベルセンサ]などの入力信号は、試運転を進めていくと急遽『ONで検出』か『OFFで検出』を変更しなくてはならないことがあります。
また、『入力信号の検出から一定時間の遅延をもってラダーに取込む』といったことが必要になることもあります。

『入力信号の検出から一定時間の遅延をもってラダーに取込む』といったことが必要になることもあります(×の例)
『入力信号の検出から一定時間の遅延をもってラダーに取込む』といったことが必要になることもあります(○の例)

このように[外部入力信号]を直接参照せずに、一旦内部リレーを経由することでプログラムの柔軟性を増すことができます。
[外部出力信号]についても同様です。

プログラムの最初と最後にこのような『入力専用のプログラム』と『出力専用のプログラム』を用意しておくと見やすくなります。

(2). シリアル通信

シリアル通信で相手機器との交信でやり取りする電文については、少なからず固定の部分があります。

これもプログラムの途中で定義するのではなく、先に紹介した『入力専用のプログラム』と同じところ(または専用のプログラム)に定義しておくことで、変更が必要になった際に『変更が必要な箇所を調べながら修正していく』といった手間が無くなり、『変更し忘れ』という心配もなくなります。

これはシリアル通信に限ったことでは無く、[FOR〜NEXT文]のループ回数やシステム全体で使用する定数なども対象にできます。

(3). 外部入出力のまとめ

ここでは外部入出力に的を絞ってご紹介しましたが、肝心なことは『変更が入りそうなところを予測し、それに備えた余裕のある設計をする。』ということです。

そういった意味では『機能ごとに割り当てるメモリの容量』や『割り込み処理の周期』、『ネットワークデバイスの通信領域』なども該当します。

5. 最後に

いかがでしたか?
構造化プログラミングを実現する上で最も重要な要素は『設計』です。
プログラム全体をどのように切り分けるのか?どこまでを最小単位の機能とするのか?機能ごとに使用するメモリアドレスは?フラグの管理は?外部機器とのハンドシェイクは?などなど・・・。
コーディング前にどこまで設計するかで、コーディングや現地での立ち上げでソフトの自由度が増すと考えています。
そういった意味ではラダーも高級言語も変わりはありませんね。

今回紹介したテクニックはごく一部です。また全ての場面にマッチした方法とは言えないところもあります。ただし、今回のテクニックを応用すれば、どのようなシステムでも仕様にあった適切なソフトをコーディングできるはずです。

今回ご紹介させていただいた『見やすい・わかりやすいラダーにするために4〜構造化プログラミング〜』がこれからPLCのソフト開発に携わる方の参考になれば幸いです。

(M.F.)


関連ページへのリンク

関連するソフテックだより

ページTOPへ