2019-11-06

『リコールを起こさないソフトウェアのつくり方』のKindle 版リリース

リコールを起こさないソフトウェアのつくり方』のKindle 版が出ました。紙の本は絶版になってしまい、技術評論社から PDF版では買えたのですが、技術評論社の編集者にお願いして kindle 版もリリースしてもらいました。

下記は、この本が最初に出版されたときのブログ記事の抜粋です。

【『リコールを起こさないソフトウェアのつくり方』はじめにより】
日本のソフトウェア製品やソフトウェア搭載機器がグローバルマーケットで支持され続けるためには、「品質がよく割安感のある商品」を市場にアウトプットしていかなければいけないと思っています。リコールを繰り返すような品質の悪いソフトウェアではダメなのです。

一方、ソフトウェア開発の側面を見ると、欧米で体系化されたソフトウェア品質改善の方法論の多くが日本のソフトウェアプロジェクトに紹介されています。ところが、日本のソフトウェア開発現場の多くでそれらが役に立っておらず形骸化しているように感じています。また、大部分の方法論は欧米で実践されているはずですが立ち遅れているといわれる日本のソフトウェア搭載製品のほうが品質が高いという事実があります。

それはなぜなのでしょうか。それならば、日本のソフトウェアプロジェクトはこのままの方法で開発を続けていればよいのでしょうか。この命題を解明し、ソフトウェア工学をどのように日本のソフトウェアプロジェクトに適用すると有効なのかを示すことができなければ、日本のソフトウェアエンジニアは決して幸せにはなれないと思っています。これが本書を書こうと思った動機です。

本書は、たとえば、次のような方に有用です。
  •  ソフトウェア開発の初級者の方
  •  新人でこれから目指すべき道筋が見えていない方
  •  ハードウェアエンジニアで「なぜ、ソフトウェアは問題を起こすのか」を常々疑問に思っている方
  •  ソフトウェアプロジェクトマネージメントの技術を学んでいて、どうして自分のプロジェクトでうまくいかないのか悩んでいる方
  •  組織内でソフトウェア品質問題に頭を抱えている方
その他にも、ソフトウェアの品質管理、構成管理、再利用資産、安全設計などに興味を持っている方にもおもしろく読んでいただけると思います。 まず、ソフトウェアエンジニアの新人や初級クラスの方は、Part1のソースコードのケーススタディを見て、自分がいまどのレベルにいて、何を学ばないとまずいのかを考えてみてください。きっと答えが見つかるはずです。中堅、ベテランのエンジニアのみなさんは、Part1でソフトウェアの本質的な危うさを認識し、日本人の特性を理解したうえで、日本におけるプロジェクトマネージメントの適用方法を Part2で学んでください。

そして、プロジェクトリーダーやソフトウェアアーキテクトとしての責務を負っている方は、ソフトウェア再利用資産の抽出方法を Part3で修得し、現行のソフトウェア製品群の開発効率と品質向上に役立ててください。
最後に、自組織のソフトウェア製品やソフトウェア搭載製品が市場で問題を起こしており、その問題を解決する責務を負っている方は、本書の最初から最後までを通して読み、ソフトウェアと日本人の特徴を理解し、自組織において何に手をつければよいのかを理解してほしいと思います。
本書が、初級や中級のソフトウェアエンジニア、アーキテクト、マネージャ、リーダーの方にソフトウェア品質の改善活動の参考書として役立つことを期待しています。

2010 年 3月 酒井由夫
【引用終わり】

リコールを起こさないソフトウェアのつくり方』目次

Part1 ソフトウェアの危うさの本質を体感してみよう
Chapter 1 ソフトウェアの危うさを知ろう
Chapter 2 危ないソフトウェアのプログラム例とケーススタディ
Chapter 3 ソフトウェアはなぜ危ないのか
Chapter 4 ソフトウェアの品質を高く保持するために
coffee break アメリカ人と日本人

Part2 日本的ソフトウェアプロジェクトの管理はここから
Chapter 5 ソフトウェア開発の理想と現実
Chapter 6 日本のソフトウェアプロジェクトに求められる取り組み
Chapter 7 ソフトウェア構成管理
Chapter 8 ソフトウェア変更管理
Chapter 9 レビュー
coffee break 問題解決能力:自ら考え行動する力

Part3 ソフトウェア資産化の技術がリコール防止につながる
Chapter 10 ソフトウェア開発のプラットフォーム
Chapter 11 ソフトウェアシステムとソフトウェア搭載機器の価値
coffee break 3 ものづくり戦略とソフトウェア品質:品質=Qualityの話
Chapter 12 再利用資産を抽出するためのアプローチ
Chapter 13 再利用資産を抽出する手順
coffee break 4 UML導入のススメ
Chapter 14 再利用資産の抽出のケーススタディ
Chapter 15 再利用資産の抽出後のアプローチ
coffee break 5 テストカバレッジ
Chapter 16 安全性が求められるシステムに対するアプローチ
Chapter 17 安全アーキテクチャの検討
Chapter 18 ソフトウェアを資産化して品質と開発効率を高める

2019-10-20

ソフトウェア系国際規格の認証の現状について

機能安全規格の IEC 61508(Functional safety of electrical/electronic/programmable electronic safety-related systems) や ISO 26262 (Road vehicles — Functional safety )や、医療機器ソフトウェアのソフトウェアライフサイクルプロセス規格 IEC 62304(Medical device software - Software life cycle processes)に 「適合しました!」とか、「準拠しています!」といったニュースリリースをよく見る。

しかし、ブログでは『汎用ソフトウェア製品(製品開発プロセス)は IEC 62304(JIS T 2304) に適合できない』で書いたように、医療機器ソフトウェアでは、汎用ソフトウェア製品や汎用ソフトウェア製品の開発組織に対して IEC 62304 は適用できないということを解説してきた。

その趣旨は、医療機器業界では 医療機器ソフトウェアに対して主に患者のリスクを低減することを目的としてリスクベースアプローチを求めており、その考え方のもとにライフサイクルプロセスとタスクの要求を課しているため、どのような使用目的に使うのかが定まっていない汎用ソフトウェア製品に IEC 62304 を適用しようとすることは意味がないということであった。

今年2019年7月にさらなる意味がないことを示すできごとがあったので、それを説明したい。

IEC 62304 と医療機器規制との関係

まず、IEC 62304 医療機器ソフトウェア-ソフトウェアライフサイクルプロセス と医療機器規制との関係について整理しておきたい。

左図にあるように、医療機器は日本の場合、医薬品医療機器等法(通称:薬機法)で規制されており、医療機器に搭載される、または、そのものが医療機器となるソフトウェアは IEC 62304(JIS T 2304)への適合が求められる。正確には法律で国際規格が指定されているのではなく、法律が省令を省令が通知をといったようにブレークダウンされた中で IEC 62304(JIS T 2304)が医療機器ソフトウェアの要件となっている。

IEC 62304 はもちろん、医療機器製造業者が製品のソフトウェアに対して適合していることを示す必要がある。

一方で、機能安全規格 IEC 61508 や ISO 26262 は、ソフトウェア部品やソフトウェア部品を供給するサプライヤーに対しても これらの規格の一部に適合することを推奨している。

ここで注意して欲しいのは、標準規格には、強制規格と任意規格があり、ISO, IEC, JIS は任意規格であり、規制に使うかどうかは、各国の規制当局が決めるということだ。

IEC 62304 は医療機器規制の中で規制に実質的に使われている。ちなみに、IEC 26262 は今のところ規制に使われていない。ISO 26262 への適合を求めているのは、自動車OEM(メーカ)がサプライヤに対して求めているのだ。規制当局ではないから、規制要件ではない。

規格を規制に使うということは、別の見方をすれば、規格の適合を持って認可した規制当局にも責任が生じるということだ。医療機器の場合、規制当局は機器が患者危害に至るような問題を抱えていないかどうかが最大の関心事であるから、規格適合要件は、患者リスクが許容以下に低減されていることの証明でなければいけない。

ところで、自動車業界は市場規模が大きいので、ISO 26262 が国際規格となった以降、規格認証ビジネスの市場が出来上がった。

「ISO 26262 に適合しています!」というハードウェア部品やソフトウェア部品が増えてきて、ISO 26262 の適合証明を取ることが自動車業界で部品を売るための条件になりつつある。

その際、部品メーカ、特にソフトウェア部品のメーカは ISO 26262 と IEC 61508 と IEC 62304 だいたい同じだろうと考え、いっぺんに3つのプロセス規格に適合してしまえば、それぞれの業界のアピールできると考えた。

クリティカルなソフトウェアに対する規格は、どれも同じようなものだと考えた訳だ。そもそも、クリティカルなソフトウェアが安全であるかどうか確証を持って言えるわけがない。ソフトウェア起因の不具合の起こり方は特殊なので、プロセスをマネージメントして対応するしかないのだが、そのときに、どんな危害に至る可能性があるのかを想定して対応することが IEC 62304 では求められている。

だから、IEC 62304 を安全規格と分類するのはおかしい。Medical device software - Software life cycle processes(医療機器ソフトウェアのソフトウェアライフサイクルプロセス)というタイトルにあるように、IEC 62304 は 医療機器向けのライフサイクルプロセス規格であり、製品安全規格ではない。(冒頭の図を参照のこと)

ちなみに IEC 62304 は医療機器製造業者が適合を示す規格なので、ソフトウェア部品に適合証明を出すのはおかしいのに、ソフトウェア部品の製造業者に IEC 62304 の適合証明を出す 認証機関がいる。

自分は、そのような認証機関は、規格趣旨を理解していないか、もしくは、規格趣旨を理解していながら、単に金儲けがしたいために認証書を出しているのだと考えている。

そのような認証機関にはショックなできことが 2019年7月に起こった。

IEC 62304 の認証に起こったできごと

IECEE ※1 が 2019年7月26日付けの通知で IEC 62304 をCBスキームから取り下げた(Withdrawal of standards)のだ。

【※1 IECEE CBスキームについて JEITA資料より引用】

IECEE CBスキームについて
IECEEとは?
  • IEC電気機器・部品適合性試験認証制度の略称(通称CBスキームと呼ばれる)
  • IEC規格に基づく1回の適合証明試験結果を加盟53カ国(MB*)の72認証機関(NCB*)で、重複試験無しに受入れることを目的としたデータの相互活用制度
  • 制度はIECEE01(基本ルール)、IECEE02(手順ルール)及びIECEE03(CB-FCS)に 基づき運営され、これらは年1回開催されるCMC*会議にて承認のうえ改正される
  • IECEEスローガン:“One standard, one test performed anywhere, accepted everywhere !”
◆IECEEの国際法上の位置づけは?
  • WTO-TBT協定により;
  • 強制・任意分野に関わらず、国際規格・制度を適合性評価手続きの基礎とする
  • 上記手続きによって得られた評価結果は加盟国間で相互に認め合う
  • 旨の規定があり、CB制度はこれを満足するメカニズムとして認知されている

◆本制度における日本のプレゼンスは?
  • 日本は1981年にJISC(日本工業標準調査会)がMBとして加盟し、現在はJET、JQA、TUV-RhJ、UL Japanの4NCBが参加
  • 日本はIECEE国内審議委員会(事務局:JEITA)が受け皿となり、日本の対処方針を 決定のうえCMC会議に参画し意見反映


【引用終わり】

 IEC 62304 は IECEEが発行する TRF(Test Report Form)を使用し、試験を実施して適合を示すことができた。(IEC 62304の TRF が廃止されたため過去形)

 IECEE が発行する TRF(Test Report Form)を使って、CB試験所が IEC 62304 の認証を行った場合、その CBレポートは 多くの国で規制要件適合の証明として受入れられてきた。

 ところが、IECEE は IEC 62304 の TRF を廃棄してしまった。なぜか。その理由は、CB認証のルールを定めた OD-2037(関係運用文書) には「証明書Product Standardsのみ記載する」というルールがあり、IEC 62366 や IEC 62304 は Product Standardではないため、CBスコープから外されたと想像される。(予想)

 そして、冒頭の図にある IEC 60601-1 医療電気機器-第1部:基礎安全及び基本性能に関する一般要求事項 の TRF Rev.N に IEC 62304 TRF の内容が組み込まれた。

 これによって、IEC 62304 単独の CBレポートは発行できなくなった。(医療機器の安全規格である IEC 60601-1 の試験を TRF で行うと自動的に IEC 62304 のTRF の要求も満たすことになる。)

ハードウェアを含む医療機器が IEC 62304 への適合を示すには、IEC 60601-1 の TRF(現時点の最新版は Rev.N)を使用すればよい。

ただし、ソフトウェアそのものが医療機器となる単独の医療機器ソフトウェア(SaMD)では、CBレポートが発行できなくなったので、左図のプライベートレポートを使うしかなくなった。

実際、まったく独自のレポートを受け入れている国もあるので、大きな問題にはならないだろう。

また、CBスキームではなく、ISO/IEC 17025 の認定を受けた試験所で作成したレポートを受け入れる国はある。(※現時点では、日本の医療機器規制では上図のどれも受け入れている。ISO/IEC 17025 は試験所認定があるので、実質的には CBスキームの TRFレベルの試験が行われる。)

ただし、ISO/IEC 17025 の認定を受けた試験所も、IECEE が発行していた IEC 62304 のTRFを使っていたので、今後は、過去に発行されていた IEC 62304 の TRF を模倣した独自のレポートを使うことになるだろう。

今後の IEC 62304 の認証

IECEE が IEC 62304 の TRFを破棄し、IEC 60601-1 の TRF Rev.N の附属書に取り込んだことにより、CBスキームによる IEC 62304 単独の認証ができなくなったので、ソフトウェア部品メーカが IEC 61508 と ISO 26262 と IEC 62304 への適合を3つ並べてニュースリリースするケースがなくなると思われる。

そもそも、規格適合は 1. 実質的に安全や信頼が高いことを示す 2. 権威を示して売り上げを上げたい といった目的があり、規格適合を3つならべてニュースリリースしているの 2の目的だろうから、IEC 62304 が CBスキームから外れたことで、プライベート認証しかなくなり IEC 62304 単独の規格認証の権威はなくなったので、アピールもできなくなったはずだ。

今後、IEC 62304 単独の認証証を掲げる ソフトウェア部品メーカが現れたあら、それはプライベート認証であり、Intended Use(意図する使用)が定義されていないのに、IEC 62304 箇条7 ソフトウェアリスクマネジメントプロセス に適合を出してしまっているとみた方がいい。「認証」ではなく「準拠」となっている場合、おそらく箇条7が適合できていないのだろうが、箇条7ができていないということはソフトウェア開発においてリスクベースアプローチができていることが確認されていないということだから、IEC 62304 適合の意味はない。

IECEE のサイト(参照情報)




2019-08-09

Raspberry Pi3を使ったリアル波形描画(9) アーキテクチャの設計思想について

本特集記事の「Raspberry Pi3を使ったリアル波形描画」を実現するにあたり、採用したアーキテクチャの設計思想を説明しようと思う。

なお、今回より、Qt で作成したリアル波形描画のシステム(基本フレームワークだけで、フィルタや心拍検出等の中身はスケルトンにしてある)について解説するのだが、実際に動いた実績のあるソースコードを見ながら解説を読んでもらった方がよいと思う。そこで、Qt でコンパイルできるソースファイル一式と Qt の設定画面を PDF にしたものを希望する方が入手できるようにした。(Qt の設定画面を付けたのは、同じ環境にするのは意外と大変なので)

リアル波形描画のソースコード入手方法

入手方法は第一回目の記事『Raspberry Pi3を使ったリアル波形描画(1) イントロダクション』で Windows で実行できるデモファイルを配布したときと同様に、Google Drive の共有設定で共有できるフォルダにパスワード付きの圧縮ファイルを格納してダウンロードできるようにし、パスワードはメールの自動返信機能で送るようにした。

なお、最近、WEBブラウザのセキュリティが強化され、ファイルをダウンロードするときにWEBブラウザによっては、いろいろ聞いてくるようになったようだ。gmailのアドレスを持っていなくてもダウンロードできるはず。共有フォルダがからファイルをダウンロードする手順については IE11 と Microsoft Edge ではできることを確認したので、上手くいかなかったときは、これらのWEBブラウザで試して欲しい。

【リアル波形描画のソースファイルの入手方法】
次のメールアドレスに件名:ソースファイル送信 と書いてメールをお送りください。(件名は”ソースファイル送信”でなければ返信されません。メール本文は何でも構いません。
メールアドレス:CriticalSoftwareConsulting[あっとまーく]gmail.com

リアル波形描画のソースコードについて

本ソースコードは 2019年8月時点で Qt 5.12.2 / Qt creator 4.9.0 にてコンパイル・リンクし、Windows 10上で 動作しているものである。

自分のパソコンに Qtをインストールし、コンパイル/リンクして、実際に動くことを確認した上で、どのようなソースコードで動いているのか確認してみて欲しい。

どんな環境であれ、「実際に動いている」という実績は設計者に取って絶大な安心感につながる。裏返せば、ソフトウェアは一行どころか、1文字変更しただけで、システムが動かなくなるということもある。だから、一度、ちゃんと動いているという確信を得た上で、ソフトウェアを修正しては動作を確認しという作業を繰り返していれば、上手くいかなかったときに少しの後戻りでまたちゃんと動いていたところに戻ることができる。

※設計せずにコーディングをするのはダメという金言をよく聞くが、小さい実験システムでも、まずは動くコードを押さえて安心した上で、コードも設計書もきれいにするというのが自分のスタイルだ。小さいながらも「動くコード」がない状態で、設計書だけを書き進めていって実装する壁にぶつかって止まるのはイヤだ。

さて、「Raspberry Pi3を使ったリアル波形描画アプリ」のユーザーインターフェースは次のようになっている。

画面の説明







これがリアル波形描画のソフトウェアをWindows 上で実行したときの画面になる。
  1. 波形表示領域:上部の3種類のデジタルフィルタを通した後の波形。
  2. デバッグ用波形表示領域:内部処理が意図通りに動いているかどうかを確認するための表示領域。
  3. 入力ソースの切り替え(DEMO, A/D変換器, SIN波):画面に映っているのはDEMO用の三角波。
  4. 波形の切り替え(三角波1, 三角波2, 疑似心電図):三角波1 は心拍数が60になるように調節してある。入力ソースが DEMOのときのみ有効。
  5. 感度切り替え(×1/4, ×1/2, ×1, ×2, ×4)
  6. 波形掃引の開始/停止ボタン:4msインターバルと10msインターバルのスレッドを起動/終了している。
  7. 心拍数の表示:心電図のとがった部分(QRS波形)が1分間に何回計測したかをカウントして表示している。※マークはQRS波形検出時に200ms表示している。
  8. 電源ノイズフィルターON/OFF:心電図に重畳した電源ノイズ50Hz/60Hzを除去するためのデジタルフィルターのON/OFFスイッチ。ソースの中身は空となっている。(自分でフィルタを設計して実装する)
  9. ドリフトフリーフィルタON/OFF:心電図は体動などの影響で基線動揺が起こる。基線が動揺すると画面からはみ出したりして見にくいので、ドリフトフリーフィルタ(ローカットフィルタ)を入れる。ソースの中身は空となっている。(自分でフィルタを設計して実装する)
  10. ハイカットフィルターON/OFF:A/D変換前にアナログのハイカットフィルターを入れるが、それよりも低い周波数帯域のハイカットデジタルフィルター。ソースの中身は空となっている。(自分でフィルタを設計して実装する)
  11. フィルタの遅れ時間:デジタルフィルタをかけると遅れ時間が生じる。特にドリフトフリーフィルタは遅れ時間が大きい。入力波形に対して表示している波形がどれくらい遅れているのか表示する。
  12. 入力波形:フィルタ後の入力波形。
  13. 心拍検出のためのスレッショルドレベルの変化の様子:心拍数を計測するために14のフィルタ出力値のピークを計測して、そのピークを階段状に下げていき、スレッショルドレベルを設定している。(簡易的なものを実装している。このスレッショルドレベルを超えた時が新たなQRSの起点となる。)
  14. 心電図波形の微分値:心拍数を計測するために、入力波形のうち心電図のとがった部分(QRS波形)だけを際立たせるようなフィルタ(現在はサンプルで入力波形を微分して、絶対値を取っている。暫定的なもの。)をかけている。
心拍検出部分とデジタルフィルタをスケルトン(メソッドと入出力のみ実装し、中身を空にしてある)にしているのは、そこに業務ドメイン上のノウハウがあると考えているからであり、その他の部分のソースコードを公開したのは、そのアーキテクチャ自体はドメインには特化していない技術であり、学ぼうと思えばデザインパターンの本など教材は入手する方法がいろいろあるからである。

繰り返すが、もの作りをするソフトウェアエンジニアにとって、「実際に動いている」実績のあるソースコードは貴重で、「動いている」ことを確認した上で、自分自身で一から同じものを作り上げてみて、同じように「動いている」状態にまで仕上げることと、アーキテクチャを含めより理解が深まるし、自信につながる。

こちらはPC上で波形表示を行ったデモ画像


こちらは 5インチのLCD上で波形表示を行ったデモ画像

アーキテクチャの目標

今回のソフトウェアのアーキテクチャの目標は次の通り。
  1. 一発勝負ではなく、商品や商品群として10年間は骨格を変えずに使えるアーキテクチャにしたい。
  2. 変わりやすい部分(画面表示を含むUI)と変わりにくい部分(デジタルフィルタや心拍数検出の論理など)を分離したい。
  3. 心拍数計測等のアルゴリズムの改善作業を見越して、実際の生体信号の入力(A/D変換)ではなく、サンプルデータやSIN波を入力ソースとできるようにしたい。
  4. デジタルフィルタや心拍数計測アルゴリズムが意図通りに働いているかどうかをデバッグ画面で確認できるようにしたい。
  5. 普段はPC上で検討を重ね、シミュレーションで上手くいくことが検証をかさね、その後、実機でバリデーションするようにしたい。(開発効率を高めるため)
  6. タイマー制御、スレッド制御といったリアルタイム制御に関する部分は Qt で行い、作成するアプリケーションソフトウェアからはできるだけ隠蔽したい。
1回動くものができればいいという発想ではなく、商品群として長く保守や性能向上を継続し、開発効率が良く、競争力が高く、研究開発もでき、保守しやすいアーキテクチャを目指している。

ただただ早く動くものを作りたいという考え方でソフトウェアの開発を続けていくと、開発効率は上がらず、過去の作ったソフトウェアの保守に追われるという悪循環から抜け出すことができない。

そのような負のスパイラルから脱却するためには、長持ちして、競争力が高く、保守もしやすいアーキテクチャを目指す必要がある。

実現の可能性(Feasibility Study)の結果

上記の目標を実現するのに重要な「実現可能性」のポイントは、Qt で正確なタイマ割り込みを発生させ、かつ、優先度の高いスレッドと低いスレッドを走らせることができるかどうかだった。

Raspberry Pi3B+ のCPU は ARM Cortex-A53 1.4GHz でクワッドコア(CPU4つ)なので、4ms の定期割り込みの発生など、CPUスペックから考えれば楽勝と思えるのだが、そう簡単にはいかない。

なにしろ、Linux はリアルタイムOSではないし、OSの制限もあり、msオーダーの定期割り込みを発生させるのは簡単ではない。

さらに、このシステムを実現するには、マルチスレッドが必須であり、かつスレッド間のデータの受け渡しが必要で、これが意外に難しい。

でも、Qt の SIGNAL/SLOT のしくみを使うことによって、マルチスレッドとイベントの伝達、データの受け渡しができることが分かった。

ただ、できれば、CPUコアが4つあるので、4msインターバルの割り込み処理を行うスレッドは4つのコアのうち一つに割り当てたかったのだが、Qt 上でその設定ができなかった。

その代わり、定期インターバルの起動間隔に大きなバラツキが発生したいないことは、常に確認できるようにした。

10ms 間隔で起動されるスレッドで、システムタイマーの値を読み込んで、前回のタイマーの値との差分を printf で表示するようにしてある。下記は Windows で走らせたときの様子だ。これを Raspberry Pi3B+ で実行したときも、間隔に大きなずれがなければ、定期割り込みは成功していることになる。ちなみに、下記の例ではインターバル間隔に 0.5% 程度の誤差(ジッター)がある。この誤差がシステムとして許容できないようならば、Raspberry Pi3B+ の外側で発生させるか、Raspberry Pi3B+ にリアルタイムOS を搭載する。今回は、できる限り、ハードウェアの追加なしで実装したかったので、0.5%のジッターは許容したが、常にブレが大きくなっていないかどうかをWatchしておくことは重要だ。

time: 1475803.318404771 CLOCK_REALTIME 09987 DIFF:uSec
time: 1475803.328394707 CLOCK_REALTIME 09989 DIFF:uSec
time: 1475803.338389771 CLOCK_REALTIME 09995 DIFF:uSec
time: 1475803.348481033 CLOCK_REALTIME 10091 DIFF:uSec
time: 1475803.358499317 CLOCK_REALTIME 10018 DIFF:uSec
time: 1475803.368381897 CLOCK_REALTIME 09882 DIFF:uSec
time: 1475803.378349216 CLOCK_REALTIME 09967 DIFF:uSec
time: 1475803.388392228 CLOCK_REALTIME 10043 DIFF:uSec
time: 1475803.398597482 CLOCK_REALTIME 10205 DIFF:uSec
time: 1475803.408382657 CLOCK_REALTIME 09785 DIFF:uSec
time: 1475803.418337612 CLOCK_REALTIME 09954 DIFF:uSec
time: 1475803.428369466 CLOCK_REALTIME 10031 DIFF:uSec
time: 1475803.438366942 CLOCK_REALTIME 09997 DIFF:uSec
time: 1475803.448362910 CLOCK_REALTIME 09995 DIFF:uSec
time: 1475803.458338070 CLOCK_REALTIME 09975 DIFF:uSec
time: 1475803.468366909 CLOCK_REALTIME 10028 DIFF:uSec
time: 1475803.478349608 CLOCK_REALTIME 09982 DIFF:uSec
time: 1475803.488375431 CLOCK_REALTIME 10025 DIFF:uSec
time: 1475803.498362352 CLOCK_REALTIME 09986 DIFF:uSec
time: 1475803.508351083 CLOCK_REALTIME 09988 DIFF:uSec
time: 1475803.518362431 CLOCK_REALTIME 10011 DIFF:uSec
time: 1475803.528348146 CLOCK_REALTIME 09985 DIFF:uSec
time: 1475803.538355272 CLOCK_REALTIME 10007 DIFF:uSec
time: 1475803.548384713 CLOCK_REALTIME 10029 DIFF:uSec
time: 1475803.558325194 CLOCK_REALTIME 09940 DIFF:uSec
time: 1475803.568313623 CLOCK_REALTIME 09988 DIFF:uSec
time: 1475803.578347889 CLOCK_REALTIME 10034 DIFF:uSec
time: 1475803.588317923 CLOCK_REALTIME 09970 DIFF:uSec
time: 1475803.598452309 CLOCK_REALTIME 10134 DIFF:uSec
time: 1475803.608321620 CLOCK_REALTIME 09869 DIFF:uSec
time: 1475803.618302208 CLOCK_REALTIME 09980 DIFF:uSec

ちなみに、この部分を実験したのが『Raspberry Pi3を使ったリアル波形描画(6) Qtでマルチスレッド処理をやってみる』の記事になる。 この実験によって、およそQt上で4ms と 10ms の間隔のタイマー割り込みを発生させ、マルチスレッドで優先度の高いスレッドにタイマー割り込み処理を任せることができた。(目標6) その確認なくして、Qtを使ったリアル波形描画 の構想はありえないし、意味がない。

 また、Qt そのものの特長により、マルチプラットフォームでの開発と、UI部分の開発効率を高めることができることも分かった。(目標2,5) 以前の記事でアーキテクトは「実現の可能性(Feasibility Study)」について「いける!」という感覚をつかめるかどうかが重要だと言うことを書いた。 「実現の可能性(Feasibility Study)」について「いける」のか「いけないのか」を判断するには、その商品に対して求められている要求と要求の優先度が頭の中に入っており、その要求を実現するための実現方法の引き出しをたくさん持っている必要がある。

要求はさまざまあり、全部実現しようとするとコストが上がったり、開発の時間が足りなくなったりするので、競争力の高い商品にするためには、どの部分に独自の研究開発のリソースをつぎ込んで、どの部分はすでにある技術や市販のソフトウェアで補うのか、一番いいバラインスポイントを探す必要がある。

それには深いドメイン知識と、広いソフトウェアエンジニアリングの技術、豊富なソリューションの引き出しを持っていることが大事だ。

ちなみに、オブジェクト思考設計のコンサルタントは「広いソフトウェアエンジニアリングの技術」、「豊富なソリューションの引き出し」を持っているが、「深いドメイン知識」はない。一般的に組込みソフトウェア製品の企業のエンジニアは「深いドメイン知識」を持っているが、「広いソフトウェアエンジニアリングの技術」と「豊富なソリューションの引き出し」を持っている者は少ない。

よって、長持ちして、競争力が高く、保守もしやすいアーキテクチャを目指すためには、両者が協力するか、企業のエンジニアががんばって知識やスキルを身につける必要がある。

なお、ドメイン知識として、ユーザーまたは業務ドメインにおける市場要求の分析は、製品企業側で分析しておく必要がある。お客さんの声を吸い上げることができるのは、製品を設計販売している企業であり、そこに実現すべき機能や性能のもととなる要求がある。

要求の整理についてはQFD(品質機能展開。左図が一例)などを使うとよい。または、マインドマップなどで整理してもよい。 それらの要求が実現可能かどうかは、さまざまな条件(コスト、リソース、エンジニアが使いこなせるかどうか等々)があるので、単純ではない。なにしろ幅広い知識(引き出し)と、最新の情報にアンテナを張っていることが必要になる。 今の世の中情報はあふれかえっているので、やりたいこと、実現したいことは常に頭の中に記憶しておいて、やりたいことの実現に関係ある情報を見つけたら、その情報をクリップしておいて掘り下げるという習慣を付けておく必要がある。 働き方改革で、オンとオフをキッチリ分けて休むときは休んでエネルギーを充電するのはいいと思うが、アンテナはオフのときも立てておいて、実現可能性の種がピピッときたらメモっておくぐらいのとをやっていないと、なかなか競争には勝てない。

静的構造の説明(3層構造)



さて、「Raspberry Pi3を使ったリアル波形描画」のアーキテクチャ(静的な構造、クラス図)について説明していこうと思う。 なお、このアーキテクチャについては今回記事の冒頭でソースコードを入手できるようにしてあるので、ソースコードを見ながらクラス図を眺めて欲しい。

デジタルフィルタと心拍数検出のアルゴリズムについては、それぞれソースコードはインタフェースだけ用意してあり、中身は空または簡易的なものにしてある。(ここが最も重要なノウハウ=コア資産と考えているため)

この部分の性能を高めることができれば、他社に対する競合力となり、その資産は組織の財産となる。また、その資産が真似しにくい若しくは特許で保護されていれば、商品群のコア資産として商品の価値を生み出す宝となりうる。

 このアーキテクチャは プレゼンテーションレイヤー、ドメインレイヤー、データソースレイヤーの3層構造となっている。 プレゼンテーションレイヤーは、ユーザーとのユーザインタフェースを担う部分であり、表現方法は商品群の中でも変わりやすい(ハイエンド製品は大画面、ローエンド機器は小画面など)。逆に言えば、プレゼンテーションレイヤー部分を変えることで、ラインナップを揃えたり、マイナーチェンジをして商品を新しく見せることができる。

仕向け地や特別なユーザー用にカスタマイズすることもあるかもしれない。 プレゼンテーションレイヤーは変わりやすく、変化に柔軟に対応できるようにしておく必要がある。

プレゼンテーションレイヤーの部分の変化に対する対応は Qt Creator を使うことで、簡便にいろいろな画面を実現することが可能だ。

また、ドメインレイヤーは、その商品あるいはその商品群に特有の資産を配置している。ドメインに特化しているため、その商品の本質的な機能や性能を実現していることが多い。別な言い方をすれば「本業の強み」を実現する部分だ。場合によってはソフトウェアだけでなくハードウェアも含めてコア資産となる場合もある。 基本、このドメインは「変わりにくい部分」であり、ノウハウとなり得る。

なお、今回のケースでは、黄色背景のタイマー処理とスレッドのクラスもドメインレイヤーに配置した。これらはドメインに特化したクラスではないものの、プレゼンテーションレイヤーやデータソースレイヤーとも異なり、リアルタイムシステム実現のための要素でり、システム実現の根幹と考えたからである。

データソースレイヤーはその名の通り、システムが使用する入力データの元となるレイヤーであり、ここは、 EcgAcquirer (心電図の受入れ者)から派生させた、3つのクラスを実装している。 その意図は、入力クラスのインタフェースを共通にしておき、入力元の実態を A/D変換器と、サンプルデータと sin波 の3種類で切り替えることができるようにしている。 信号処理アルゴリズムの設計、性能分析、性能向上を検討するためには、リファレンスとなる波形データや、問題が起こったときの 波形データをファイルから読み込んでシミュレーションでさまざまな確認を行うことが重要となる。

アルゴリズムを変更したときなど、今まで動いていた機能や性能が、意図通りに動いているかどうかを確認することはとても重要であり、その確認・検証には、検証に使うシミュレーション用のデータがあるとよい。そのための入力切り替えでもある。

また、デジタルフィルタの効き具合を確かめるのに、シミュレーションでsin波を入力できると都合がよい。 入力の切り替えのしくみは、C言語でも条件コンパイルを駆使したりして実現は可能だが、オブジェクト指向言語であるC++を使えば、スマートに実現できるし、クラス図でその設計思想を表現するのもやりやすい。

実際、このクラス図も、Qtで設計して上手くいくことを確認したC++のソースファイルを UMLツールである Enterprise Architect に読み込ませて、整えることでクラス図にした。 そうすれば、実際のソースコードの設計思想を正確にクラス図で表現できるからだ。 入力の切り替えは、紫背景のQt Creator で作成したボタンの押下をトリガーにして、緑背景の EcgMonitor で入力のオブジェクトインスタンスを入れ替えることで、簡単に切り替えることができる。 これができると非常に気持ちがいい。また、入力のインタフェースが変わらなければ、さらに別の入力例えば、既存の心電図データベースを追加することもできる。
る。

Qt Creator の画面

左記が QtCreator で RealWaveDisplay01 のプロジェクトファイルを開いたときの画面である。ざっと、ソースファイルの説明をしておく。

まず、頭に ap_ がついているファイルは、リアル波形描画特有のアプリケーションを意味する。

qt_ がついているファイルは、Qt に特化した部分、今回は周期起動するスレッド処理。

qcustomplot.h と qcustomplot.cpp はオシロスコープのように右から左に波形を流すために使っているソースで、『Raspberry Pi3を使ったリアル波形描画(3) Qt + QCustomPlot で静止グラフを描いてみる』の記事の一番下に使い方の解説動画がある。

main.cpp は Qt のお決まりの入り口関数で、mainwindow.h と mainwindow.cpp は Qt のメインウインドウの画面の初期化やら、画面で使っている変数や、ボタンが押されたときの処理のメソッドなどが入っている。

アプリケーションのソフトウェアのファイルの説明は次の通り。
  • ap_EcgAcquirorFromSampleData.cpp :DEMO波形のデータ
  • ap_EcgAcquirorFromADConvertor.cpp :A/Dコンバータからの入力(現在スケルトン)
  • ap_EcgAcLineNoiseFilter.cpp :電源ノイズフィルター(現在スケルトン)
  • ap_EcgHighCutFilter.cpp :ハイカットフィルター(現在スケルトン)
  • ap_EcgDriftFreeFilter.cpp :ドリフトフリーフィルタ(現在スケルトン)
  • ap_EcgMonitor.cpp:波形計測の中心となるクラス(各種オブジェクトの生成も行う)
  • ap_EcgAcquirorFromSinWave.cpp:SIN波発生
  • ap_EcgDetectQrsTypeTest1.cpp:QRS検出(簡易的なアルゴリズムにしている)

mainwindow.cpp の説明

mainwindow.cpp 内の MainWindow クラスは QMainWindow クラスから派生したクラスでヘッダは次のようになっている。

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    // コンストラクタ
    explicit MainWindow(QWidget *parent = nullptr);
    // デストラクタ
    ~MainWindow();

    // グラフ初期化
    void intializeGraph();

private slots:
    // Run/Stop ボタンを押された時の処理
    void on_pbt_run_clicked();
    // 波形タイプボタンを押された時の処理
    void on_pbt_wavetype_clicked();
    // 電源ノイズフィルターボタンが押された時の処理
    void on_pbt_ac_line_filter_clicked();
    // ドリフトフリーフィルタボタンが押された時の処理
    void on_pbt_drift_free_filter_clicked();
    // ハイカットフィルタボタンが押されたときの処理
    void on_pbt_hicut_filter_clicked();
    // 表示感度ボタンが押された時の処理
    void on_pbt_displaygain_clicked();
    // 入力ソースボタンが押されたときの処理
    void on_pbt_input_type_clicked();
    // 周波数スピンボックスの値が変化したときの処理
    void on_doubleSpinBox_frequency_valueChanged(double arg1);

public slots:
    // 4ms周期処理
    void work4msPeriodicOnMainWindow(void);
    // 10ms周期処理
    void work10msPeriodicOnMainWindow(void);
private:
    Ui::MainWindow *ui;

    CyclicThread *mpCyclicThread4ms     = new CyclicThread;      // 周期起動のスレッドオブジェクト(プライベートメンバ変数)
    CyclicThread *mpCyclicThread10ms    = new CyclicThread;      // 周期起動のスレッドオブジェクト(プライベートメンバ変数)

    float mTimekey_wave = 0;                        // 波形表示間隔 (s)
    float mWave_data = 0;                           // 波形データ
    float mAdd_data = 0.1F;

    float mTimekey_debugwave=0;                     // デバッグ用波形表示間隔
    float mQrsFilterWave_data=0;                    // QRSフィルt出力用波形データ
    float mThresholdLevel_data=0;                   // スレッショルドレベルデータ

    unsigned short mDisplayCounter=0;               // ディスプレイ用のカウンタ(10msの倍数で表示する)

    bool mRunStopFlag = false;                      // Start/Stop フラグ

    unsigned short mEcgDemoCounter=0;               // 心電図デモカウンター
    unsigned short mDemoWaveType=0;                 // デモ波形タイプ0:三角波 HR60 1:三角波 HR300 2:疑似心電図,);
    float mMaxWave=0;                               // 最大波形
    float mMinWave=0;                               // 最小波形
    float mPreviousPlotData=0;                      // 前回のプロットデータ

    bool mRealWaveFlag=false;                       // リアル波形<->デモ波形スイッチ

    // 心電図モニタクラスを生成
    EcgMonitor  *mpEcgMoniter = new EcgMonitor;     // 心電図モニタクラス

    bool mAcLineFilterFlag;                         // 電源ノイズファルタフラグ
    bool mDriftFreeFilterFlag;                      // ドリフトフリーフィルタフラグ
    bool mHicutFilterFlag;                          // ハイカットフィルターフラグ
    unsigned short mHRDisplayCounter;               // ハートレート表示用カウンタ(1秒)
    unsigned short mQrsMarkDislpayCounter;          // QRSマーク表示用カウンタ
    bool mQrsMarkDislpayFlag;                       // QRSマーク表示用フラグ

    unsigned short mDisplayGain;                    // 表示用の感度変数
    char mGainText[5][6]={("x1/4"), ("x1/2"),("x1  "),("x2  "),("x4  ")};
    unsigned short mInputType;                      // 入力種別
    char mInputTypeText[3][18]={("Demo             "),
                                ("A/D Convertor    "),
                                ("Sin Wave         ")};
    double mFrequency;                              // sin波の周波数

};

頭が on_pbt_・・・ となっているメソッドは、画面上でボタンが押されたときに、呼ばれるメソッドとなる。

各種変数の定義と初期化、4msと10msの周期起動のスレッドの生成、心電図モニタメインクラスの生成などを行っている。(青字部分)

入力の切り替え


入力の切り替え部分を説明する。登場人物の説明
  • EcgAcquirorクラス:心電図入力の基底クラスで実態を持たない。
  • EcgAcquirorFromADConvertor:EcgAcquirorクラスから派生したクラスで、A/D変換器からの入力を意図している。(公開ソースでは、スケルトンにしてある)
  • EcgAcquirorFromSampleData:EcgAcquirorクラスから派生したクラスで、データで定義したサンプルデータ(三角波二種類と疑似心電図データ)を供給することを意図している。
  • EcgAcquirorFromSinWave:EcgAcquirorクラスから派生したクラスで、SIN波のデータをEcgMonitorクラスに供給することを意図している。
EcgAcquiror は 心電図入力の基底クラスで getEcgWaveData() を純粋仮想関数にして、心電図波形データの入力ソースを切り替えられるようにしている。

EcgAcquiror 入力のインタフェースを固定しておき、EcgAcquiror クラスから 派生させた入力ソースのクラスから、さまざまなデータを EcgMonitor クラスに共有する。

EcgAcquirorFromADConvertorクラス以外は、実機(製品)では使わないのだが、デジタルフィルタや心拍計測アルゴリズムの検証したり、デモンストレーションしたりするときに、EcgAcquirorFromSampleDataクラスや、EcgAcquirorFromSinWaveクラスはあると便利だ。

また、例えば、実際にA/D変換して取り込んだ心電図データや、いろいろな心電図のデータライブラリをファイルから読み込むような時には、EcgAcquirorクラスから派生させたクラスを新たに作成して、EcgMonitor クラスに持たせれば 簡単かつ、安全に入力ソースを追加することができる。

この「簡単かつ安全に追加できる」ということがオブジェクト指向言語である C++ を使う目的であり、組込みソフトウェアでも C言語にこだわっている時代ではないと思う理由だ。C言語でも同じことはできると思うが、「簡単かつ安全に追加する」ためにはC++を使った方がよいと思う。

具体的には EcgMonitor クラス のメソッドに 心電図入力オブジェクトのポインタ変数を作っておき、派生させた3つのクラスを new で作成してそれらのポインタをポインタ変数に格納している。

protected:
    // 心電図入力オブジェクトのポインタ(メンバ)をする
    EcgAcquiror *mpEcgAcquiror;
    EcgAcquirorFromSampleData   *mpEcgAcquirorDemo  = new EcgAcquirorFromSampleData;
    EcgAcquirorFromADConvertor  *mpEcgAcquirorADC   = new EcgAcquirorFromADConvertor;

    EcgAcquirorFromSinWave      *mpEcgAcquirorSin   = new EcgAcquirorFromSinWave;


void EcgMonitor::activateEcgDemoMode(void)
{
    // 心電図入力オブジェクトにデモ波形オブジェクトを設定して、デモタイプを三角波にする
    mpEcgAcquiror       = mpEcgAcquirorDemo;
    mpEcgAcquirorDemo->setEcgDemoType(0);

}


void EcgMonitor::activateEcgAdcMode(void)
{
    // 心電図入力オブジェクトにADCオブジェクトを設定する
    mpEcgAcquiror       = mpEcgAcquirorADC;

}


void EcgMonitor::setEcgDemoType(unsigned short type)
{
    if ( type > 2) type = 0;        // 3以上だったら強制的に 0:ECGにする

    // 波形種別を設定する。
    mpEcgAcquirorDemo->setEcgDemoType(type);

}


そして、入力モードの切り替えが指示されたら、mpEcgAcquiror に各、入力ソースのポインタをセットしてあげればよい。

それだけで、その後の処理は何も変える必要がない。

   //----------------------------------------------
    //     内容:疑似心電図三角波
    // サンプリング: 4ms
    //   データ数: 250
    //     振幅:1000
    //----------------------------------------------
    // HR = 60のデータ
        {
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.000 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.010 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.020 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.030 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.040 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.050 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.060 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.070 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.080 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.090 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.100 
               0, 100, 200, 300, 400, 500, 600, 700, 800, 900, // No.110 
            1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, // No.120 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.130 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.140 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.150 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.160 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.170 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.180 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.190 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.200 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.210 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.220 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // No.230 
               0,   0,   0,   0,   0,   0,   0,   0,   0,   0 // No.240 

        },

サンプルデータはこんな感じでデータが定義されている。

そして、このような切り替え可能な構造になっていいることは、作成したソースコードを UML ツールに読み込んで、整形して クラス図にすることで、モデルで確認することができる。

それが、上記のクラス図だ。クラス図を描いてからソースコードを作ったり、ソースコードをリバースエンジニアリングしてクラス図を作ったりを繰り返していると、だんだん派生のしくみや、クラス図どおりに実装するにはどうすればよいかが分かってくる。

次回以降は、入力部以外のアーキテクチャを解説していく。