烈烈布神社に参拝して集団心理の毀損と進化に至る話

新年あけましておめでとう御座います。ぐらいは書いておこうと思いつつ、現代思想2025年1月版のロスト・セオリー(絶滅した理論)に興味をそそられたので記録しておきましょう。最近、ブログ書きに戻ろうとしているのはツイッターのX化もあるし、散漫に残しているだけではどこかに散ってしまいそうだからという理由もある。もちろん、以前と同じく「moonmile 〇〇」という形で Google で検索すれば自分のブログが引っかかるので、全文検索替わりになるというのもある。Google検索のAIはあいかわず当てにはならないのだけど、自分のブログの特殊なものも拾ってくれるのもいいのではないだろうか?

烈烈布神社に参拝する

年末年始に実家に帰ると近場の烈烈布神社に参拝することにしている。以前は、車で北海道神宮神宮まで行っていたのだが、父が亡くなってからは私自身が免許を持っていないし、まあ近場でよいだろうという感じになっている。

初詣の神社参拝は鉄道会社の宣伝という話もあるけど、それは遠地の観光地化された神社の話であって、近場の神社に参る場合にはそれ以前からある。どれ以前かは知らなけど、少なくとも明治以前も神社があったはずだし、初詣の習慣は江戸時代にもあっただろう。

初詣に行かないとダメというわけではない。別にいかなくてもいいのだが、ちょっとした切っ掛けがあるときには面倒でなければ行っておいたほうがよい。つまりは、

「今年は初詣に行かなかったから、この仕事がうまくいかなかったかも」

という憂いを避けるためである。「仕事」の部分は、学業でも受験でも就活でも構わない。これは、実際に子の大学生に言った話であるが、「それならば」という形で重い腰を上げさせることに成功している。罰があたるわけでもなく、ご利益があるわけでもなく、個人的な「憂い」を払拭するために初詣に行くのだ。

烈々布神社の歴史

烈々布神社の歴史であるが、当然のことながら明治以降である。函館の五稜郭があったり札幌の屯田兵があったりして、徐々に札幌の町を広げた結果が、今の烈々布神社ともいえる。

https://hokkaidojinjacho.jp/%E7%83%88%E3%80%85%E5%B8%83%E7%A5%9E%E7%A4%BE

天照の御霊がどれほど広く届くかわからないが(少なくとも日本全土には即なのかもしれない、よくわからんw)、菅原道真公も入っていてなんだかよくわからない感じになっている。ただし、氏子(たぶん、開拓民の子孫と思われる、かつ地元の企業)もそこそこいるので、神社としての体裁以上ものは備わっている。

ただし、見ての通り明治以降の建立なので、歴史的な価値はまるでない。神話的に意味もあるかどうかも微妙なところである。門前に建立されている石碑も昭和のものなので、比較的新しい。

しかし、なぜここに「初詣」することができるのか?というのを心理的に読み解いてみようというのが、今回のブログ記事の主旨である。そう、おわかりのように暇なのだ。

神社へのいたずら書きを考察する

神社の架空性を問うだけでは詰まらないので、去年あった靖国神社へのいたずら書きをあわせて考えてみよう。

靖国神社 落書き事件 14歳の中国人少年に逮捕状 落書き見つかった当日に中国帰国 | NHK | 事件 https://www3.nhk.or.jp/news/html/20241121/k10014645201000.html

「礼拝所不敬罪」とかにあたるらしいのだが、いわゆる公共物損壊の罪だろう。礼拝所不敬罪という意味だと、いわゆる尊属殺人を想起させるわけで、公共物としては神社仏閣であろうと通常の公共物(区役所とか公園とか)と同じ列に据えてもよいはずだ。しかし、なぜか、不敬罪という形でワンランク上の罪をかぶせようとしたがるし、一般的には不敬罪であろうという思い強い。私も直感的には不敬罪にあたるだろうと感じてしまう。

しかし、ちょっと考えなおしてみよう。不敬罪という言い方をするが、何に対して不敬なのだろうか?靖国神社自身に対してなのか、靖国神社で奉られている英霊に対してなのか、それとも靖国神社をたたえている人たちに対してなのか?ここを分解してみようという試みである。

靖国神社を烈々布神社に置き換えてみよう。烈々布神社にいたずら書きがされたときにどうなるだろう。一般的な神社仏閣に対していたずら書きをさえたときには、たしかに礼拝所という特別な場所(何に対して特別であるかは、後述する)への公共物損壊には違いないが、靖国神社ほど怒るとは思えないし、それほどニュースとして広まらないだろう。場合によっては、天照や道真公を毀損されたとも言えなくもないが、そこまで考えて怒る人は、烈々布神社の氏子ぐらいなものだろう。

靖国神社には氏子相当の人が多い(いわゆる、戦没者が安置されている、とされるので。戦没者でも安置されない場合もあるけど)と勘がられるので、氏子という意味では烈々布神社よりも多くてニュース性が高いかもしれない。しかし、靖国神社へのいたずら書きは単に氏子が多いという問題以外のところにある。つまり、別のものを毀損されたとほとんどの日本人が感じるからだ。もちろん、毀損されたと感じない日本人もいるわけで、今回はそれは例外としてとらえておく。まあ、ニュースになったという事実からして、それなりに「事件性」があるとということだ。

偶像崇拝と組織進化論

靖国神社が一般的な概念として偶像崇拝であることは間違いない。仏教も仏陀の教えそのものだけでなく仏像をあがめることも多いので偶像崇拝である。イスラム教ではないが、偶像にはちょっとした価値がでてきてしまう。偶像そのものに価値がでてきてしまう。

偶像としては芸術的な価値もあるのだが、ここでは靖国神社自身の価値を考えてみよう。狭い範囲でいえば、いたずら書きされた石碑そのものと言ってよい。

現代芸術もそうだが、芸術対象や宗教的な対象そのものには価値がない。話がずれるが、「壺」だった、壺自身に価値があるわけではない(価値があるとして売られているが)。とある宗教的な意味(芸術的でもいいし、かつての持ち主でもいいし、芸術家自身がつけた解釈自身でもよい)。ときどき、Xに流れてくる「バナナをキャンバスに貼り付けた」だけでも構わない。金銭的な価値というものはそういうものだし、それぐらいの価値しかない。

じゃあ、石碑自身には価値がないが、これにいたずら書きをされたときに、何が毀損されるかというと、石碑をあがめている人達になる。いわゆる、氏子が最初にあがるわけだが、靖国神社の場合は氏子だけに限らない。いわゆる、右翼的な積極的な靖国崇拝者もいれば、なんちゃってネット右翼のコスプレ団体かもしれないし、戦時を抜けてきた老人かもしれないし、漠然と日本の神社のくくりとして靖国神社を毀損されたという漠然とした厭らしさかもしれない。

ここでは、より消極的な「漠然とした厭らしさ」に焦点をあてていこう。

普段は積極的に靖国神社に行こうとは思わないが、靖国神社の石碑にいたずら書きをされるとちょっと嫌な感じがする、という程度のことである。でも、この「ちょっと嫌な感じがする」はどこからくるのだろうか?

同じように、たいていの日本人が知らない烈烈布神社の何かの石碑に同じようにいたずら書きをされたと仮定しよう。そうすると、感じるだろうか?別になんとも感じない場合もあるけど、ちょっと嫌な感じがする人が多いと思う(ここはアンケートなどでフィールド調査しないといけないところだが、思考実験で済ますw)

つまり、日常の中で特に意識はしていないが、神社というものが毀損されるとちょっと嫌な感じがするわけである。とくに「神社」を崇拝しているわけではない(崇拝する場合もあるけど)が、漠然とした偶像崇拝を神社という形で存在させることができるぐらいには許容しているわけである。同じものに、神社の鳥居がある。これはよく言われるように、小便をさせない場所に鳥居の記号を書いて避けさせるぐらいいは、呪術的な効果がある。そう、この呪術的な部分と「ちょっと嫌な感じがする」というのは同根であるのだ。

神社あるいは仏閣をそこかしこに配置させるだけの心の余裕を日本人は持ち続け現在にいたる。もちろん、明治の初期の廃仏毀釈があるので一筋縄ではいかないが、単純化して現在では神社仏閣を毀損しない程度の集団に日本はなっている、ということが靖国神社のいたずら書きの事件から読み取ることができる。

そこで、日本人の魂とか言ってしまうと速攻陰謀論になってしまうので、組織進化学の話につなげていこう。組織進化という視点では、いっていの集団には一定のゆるいルールが存在し、そのルールを尊重する人たちが集まって組織を担っているのである。「ゆるいルール」というのが重要で、これが厳しい罰則付きのルールであると村八分や外乱の取り込みができなくなって組織を継続して存続させることができなくなってしまう。「ゆるいルール」というのは、組織外にあるルールさえも少し取り込むことができるという緩さ必要になる。

つまりは、日本という組織(国ではなく、自分は日本人であるという意図的な従属の関係?あるいは承認意識?)をまとめているものひとつとして、「神社に対してなにかいたずらをしてはいけない」という共通認識があり、それが緩いルールになっていて、不敬罪というのを認める形になってるということである。「不敬罪」というのが、昔の尊属殺人罪ほど強い印象を与えないのはそのためだろう。尊属殺人は儒教の教えもあるだろうが、西洋でのエディプス・コンプレックス論にもでてくるので、東洋に限った話ではない。

おまけ、男女という枠組みを外す

ここでは「女性への痴漢」という言葉を出したが、言い直せば「人への痴漢」である。女性への痴漢があれば、男性への痴漢もある、さらにその中間もあるのだから、性別や年齢に限らず「人への痴漢」行為はあかんだろう、というのが共通認識になればよいのだ。

だから、Xでよく出てくる「男が~」「女が~」という言葉に敏感に反応してしまいがちなのは、そこが男性あるいは女性というカテゴリー/セグメントに無意識に分けてしまっているのが問題なのであって、これをヒトというひとつのカテゴリにしてしまうと問題が少なくなる。このあたりは類型論と特性論(精神分析と臨床心理学)に属するので、日を改めて書くことにしよう。

カテゴリー: 開発 | コメントする

年末なので五行思想でコンピュータサイエンスを解説してみよう

もともとの発端はここから。五芒星か六芒星が一部で流行っているらしいので、どうせならば本格的にやりましょう、という発想。

実は、個人的に陰陽道とか五行思想には詳しい(趣味レベルですが)ので、けっこうイケる感じではあります。

ざっと、次のところまでつなげておきましょう。

参考文献

いきなり本格的なものを読むとつらいので、次の2冊からスタートするといいです。

シム・フースイは風水をテーマにしたミステリー小説です。荒俣宏が書いているので、安全に風水ワールドを観察できます。

禁止目録は五行機関がでてくるところまででいいです。アニメの第1シーズンだけでもいいけど、基本的な用語がでてきるので、それでも十分だったりします。

五行思想

いきなり、陰陽道につながっても怪しいだけなので、鍼灸のところからスタートしましょう。安倍晴明とか陰陽道からスタートしてしまうと、単なる幻魔大戦の雰囲気になってしまうので(ラノベ的にはそれでもよいけど)、もっと現実的に五行が使われているところからいきます。

要するに、『「木・火・土・金・水」という「五行」によって』のところがキーポイントで、「木火土金水」という5つの要素で東洋医学のツボや健康などを見ていきます。「木火土金水」は、色でいれば「緑赤黄白黒」か「青赤黄白黒」です。古来より緑/青が混在されるので、どちらでも構いません。

いわゆる、三原色に白と黒を混ぜて5つにするわけですね。光の三原色、ものの色の三原色とありますが、とにかく5つになることが重要です。

相合・相克とありますが、これもどうということはありません。理論的な根拠はなくて、あてはめて後付けしているだけです。あるいは、相合・相克っぽいものを、五行にあてはめてなにかとこじつけをします。

これをコンピュータにあてはめると、このようになります。

よく、コンピューターサイエンスでは、

  • 演算装置(CPU)
  • メモリ
  • 入力装置(キーボード、マウス)
  • 出力装置(モニタ、プリンタ)
  • 補助記憶装置(HDD、SSD、データベース)

が使われます。最近はこれにネットワークを付け加えて六芒星…じゃなくて、6個の分野に分けることが多いのですが、ここは五行思想に従って5つのまま使いましょう。

なんとなく、相合・相克ができたら完成です!

五行大儀

この適当に5つに分けるというのは、私のオリジナルではありません。実は「五行大儀」(あるいは五行大義)という本に書いてあります。

なにかと、物事を5つに分けて分類すると便利ですというパタン・ランゲージみたいな本です。

先の「木火土金水」だけでなくて、色とか味とか様々なものを5つに分類します。5という家数に特に意味があるわけではありません。慣習的に5つに分けるという話です。

なので、方角であっても「東西南北」の4つに「中央」を加えて5つにするとか、味を5味にするとか(甘味、酸味、塩味、苦味、うま味)のように分けるとかします。味の場合は、先に五味があって、味蕾を探すという逆転現象が起こっています。5つ分ける元ネタはだいたいこれが原点です。戦隊もののゴレンジャーとかもそれですね。

陰陽道と易経

陰陽道は「陰」と「陽」を用いて2つに分類するところからスタートします。太極図が示すように陰陽は二つで一体であるし、どちらが上とか下とかいうわけではありません。

陰陽思想自体は世界各国にどこにでもあります。男女であったり性器の形であったり、白と黒であったり、真偽(True/False)であったり、二値のあらゆるものは陰陽思想が発祥です。

この事象を陰陽/正負/真偽にわけて二分岐させたものが、陰陽道であったり易経であったりします。いわゆる占いですね。何かの事象に対して、判断をYesとNoに分けて考えるわけです。何故なに思考は5行思想にあわせて、5回やることにしていますが。易経の六十四卦は2の6条で6回分岐します。これは、卦を3本と3本に分けて混ぜるので計6本になるわけですね。

そんなわけで、易経では未来を64分割して予想します。この考えた方はリスク管理の基本でもあります。リスクを判断するときに発生する割合をYes/Noなどで分けていって、最終的に5,6回ほど判断していけばいいわけで、最終的に大きなリスクやきわめて高い確率で起こる事象ということが分かってきます。同じパターンでプロジェクトの予想もできます。ぜひ、易経をプロジェクト管理に利用してみてください。

さらに、陰陽道では方角の示すことができるので、先の五行コンピュータサイエンスと合わせると、どの部位にトラブルが発生しそうか、あるいは設計に力をいれないといけないかを確認できます。これにより、禁書目録などであるように、玄武などの色を配置してトラブルをさけたりモジュールの強化をすることができますね。これも陰陽道の盤を買って試してみてください。

老子

老子の道教も外せません。かつて、物理学者が道教にはまってしまったように、コンピュータサイエンスを道教に嵌めるのも容易です。

量子力学のスピンやカラー、チャームなどの用語は道教が由来ですし、道教が先の発見者…ということにしておきましょう。ファンタジー味が増すので。

攻殻機動隊の第1巻の最後に出てくるスピンの話とか、仙術超攻殻ORION」に出てくるアマテラスなどの登場人物も無縁ではありません。ぜひ、古事記を読み通してください。日本書紀のほうはまあいいです。ファンタジー味が少ないので。

そんなわけで、道教のタオイズムは物理業界では一定のブームでした。いまはどうかわからないのですが、50歳オーバーの人には非常になじみが強いし、それ以前の物理学者は実際にインドに行って修行をしてしまうぐらいのでオカルトな世界に足を突っ込んでいるので、いまの陰謀論やらなんたらを笑えません。

老子を読むと、色即是空とか、陰陽の玄とか出てくるのでぜひ活用してみましょう。

あと、儒教由来の孔子に対抗するために、老子の道教を持っておくと便利です。先祖崇拝の儒教はなかなか呪縛が多いので、対抗で道教を持ち出すといいです。

具体的にコンピュータサイエンスに活用する例

基本章情報処理的に五行思想や易経を使ってみましょう。先に例を出した演算装置のあてはめやプロジェクト管理だけでなく、いろいろなところに活用…といいますか、こじつけができますw

  • プロジェクト計画を立てるとき、要件定義/設計/実装/試験/運用の5つに分ける
  • コードを書くときに、ベースとなる「土」の部分の共通基盤/ライブラリから作り始める
  • 「土」を肥沃にするためのは、人を教育する「木」が必要となる
  • 五行の「金」は必要ですね。まさしく予算!
  • イテレーション開発のPDCAに対して、「何か」を加えて5つにしいて、サイクルにする。
  • 陰陽の遺伝子を螺旋にしてイテレーション開発する。イテレーションの形は、つまりは螺旋でドリルで、グレンラガンである!

いかがでしょう?いろいろ幅が広がりますよ。

余談ですが、サーバーマシンには落ちないように交通安全のお札を張っておくとよいですよ。

カテゴリー: 開発 | コメントする

ソフトウェア開発で「生産性」を使うときに注意すること

このあたりの生産性の問題は、リモートワークと社内出社の双方に意見の食い違いがあり、佐藤氏のいう「誰かが顧客要件をキチンとまとめ~」に掛かってきているのかな、というのがある。

著作を出しているので、こっちも踏まえて議論を残しておこう。

主に議論になりそうなのは、第3章からの具体的な開発プロジェクトの話になる。ここの中でリモートとか設計とかコミュニケーションの話がでてくるので、それでよいだろう。

第三部では「現場で役に立つ周辺知識」としてマネジメントの話がでてくる。

  • 第9章 チームビルディング
  • 第10章 設計
  • 第11章 Gitによるリポジトリ管理

ということで前半の技術的な要素(ReactやDjango)とは違い、それらのWebアプリを具体的にどのように会社で開発しているのか?という紹介に近いものになる。

書籍の中には、アジャイル開発、イテレーション開発のような一般的なソフトウェア開発の用語はでてきるのだが「スクラム」や「PMBOK」のような用語はでてこない。これはあえて外したのか、既存のスクラムとはやり方が異なるために「スクラム」という用語を外したのかどうかはわからない。が、第9章や第10章を読む限り、

  • 開発自体はアジャイル開発でやっている模様
  • 顧客とのやり取りやプロジェクトメンバとの情報共有のため文書を積極的に作っている
  • 実際のタスク管理や進捗管理はチケット方式を使っている模様
  • プロジェクトメンバは5名程度で、単独ではない。あるいは大人数(100名とか)ではない。

ということがわかる。

一般的にアジャイル開発でのスクラムやXPなどがうまく回る人数として5~10名程度というものがある。それ以上になると、スクラムチームを分割して別途プロダクトオーナー(社内であればプロダクト・マネージャとか)が担うということなる。これは社内開発=製品開発なのか、受託開発を含めた請負であるのかにもよる。また、本格的な顧客参加型のスクラムチームであるのかにも依るだろう。

なので、「実装に学ぶフルスタック~」にあるソフトウェア開発が適当であるかどうかは、時と場合による。逆に言えば、株式会社オープントーンでは、この方式でうまくいっているという事例となる。

「誰かが顧客要件をキチンとまとめ~」の部分の生産性を考える

先のツイートから伺い知るに、既にチケット化されているものに対して自宅などを使ったリモート環境ではうまく生産性を上げることはできるが、それ依然のチケット化する工程において(設計や要件定義など?)ではリモートワークでは難しいのではないか?という話だろう。第9,10章を読むと、「他メンバーの課題・問題を感知しにくくなる」や「リモートでは仕様書の文書化を意識的に取り組む必要ある」という書き方があるので、プロジェクト内での情報共有や相互のサポートに難点が感じられたのであろう。また、「タスクマネジメントしづらい」という点を挙げていて、手が空いている人にタスクをうまく割り振りにくいという書き方もされている。つまりは、暇な人と忙しい人の見わけがリモートワークでは見つけづらいという難点をあげている。

ここの点からいえば、チケット駆動であれば本日こなすチケットを自らが取りに行くという方式がとられるし、スクラムであればスクラムミーティングというものが存在する。リーダーあるいはマネージャがタスクを割り振るのではなく、メンバー自らがタスクを取りに行くのだ。このあたり、スクラムの手法に沿っていないゆえの懸念点のような気もするのが、うまくいっているのであれば別に大丈夫なのだ。

論点は、それ以前のチケットを作るときの問題となるだろう。

顧客要件をまとめる、おそらく要件定義をまとめたり設計をまとめたりした段階で、WBSに落としていると思われる。この割り振りを誰がするのか?という話になると思うのだが、一般的なアジャイル開発(特にスクラム)の場合は、

  • 顧客の達成目標がプロダクトオーナー=顧客自身が立てる
  • スクラムマスターが、達成目標を適宜ピックアップして、スプリントする
  • スプリントの中で、チームのメンバーがタスクに落とす

という手順になる。なので、チケット(あるいはWBS)に落とすのはメンバー自身だし、つまりは顧客要件=達成目標をブレークダウンするのは、スクラムマスターとメンバーの共同作業となる。

が、先のようにマネージャが顧客の達成目標(つまりは要件定義など)をブレークダウンしようとすると、そのWBSを誰がやるのか?=マネージャがやる、WBSをどうやって計画通りに割り振るのか=マネージャがやる、というスタイルにならざるを得ず、そのために「誰かが顧客要件をキチンとまとめ~」という誰か=マネージャという主張となってしまう。

どうやら、このツイートを受け取った開発者(マネージャも含む?)の意見が分かれるのは、この部分で、

  • チケットはマネージャが作成し、メンバーに割り振る
  • 達成目標はメンバーがブレークダウンして、メンバー自身がチケットを受け取る

という開発スタイルの違いに他ならない。

ゆえに、そこの「生産性」も違ってしまうのだ。

では、プログラマ自身の生産性はどうなのか?

プログラマはプログラムを書くことだけが仕事ではないが、主な仕事はプログラムを書くことだ。昨今では、いろいろな仕事を平行で動かすことも必要なのだが、まあ、最終的にコードを書かないとソフトウェアというものは動かないのは確かだ。

まあ、例外的にノーコードっていう方法もあるけど、それはまた別の機会に。

で、私のツイートした「正直言うと~」の部分を解説しておこう。要するに、ソフトウェア開発のスタイルで、一番高速にモノができあがるのは「ウォーターフォール開発/計画駆動」であることを忘れてはいけない。これには異論があるだろうが、つまりは

  • 事前に完全な計画ができあがること
  • 計画通りのタスクを分割できること
  • タスクは計画通りに進み、手戻りや遅延がないこと

が、最高速にプロダクトができあがっていく計画駆動の条件となる。つまりは、自動車工場におけるオートメーションをソフトウェア開発に応用すればよい。ソフトウェア開発の大量生産工場のできあがりである!

のだが、そんなことは不可能だ。ということは皆わかっている。今では皆わかっているが、かつてはそうしていたのだ。丁寧な設計書や丁寧なフローチャートや綿密なレビューを繰り返していたのだ。

で、実際のところ、長期では難しいのが1週間程度あるいは2,3日程度の短期間であれば、この計画駆動は実に高速に動く。

これが「正直に言うと~」の前半の部分である。

後半の部分は、実際このようなキリキリに詰めた状態だと、うまくいくのいくのだが、非常に疲れるのである。疲労困憊だし、何度も続けてできるわけではない。最終的な納品直前に馬鹿力を発揮できるぐらいである。

なので、トップスピードを保つことはほとんどやらない。特にアクセルを踏むが、たいていの場合は、ブレーキのほうを踏んだままだ…と前に進まないので、オートマの自動走行位のスピードの気持ちでやることが多い。

それって、生産性が悪いんじゃないですかね?という意見もあるだろうが、長期的に見ればこのほうが生産性がよいのだ。体の故障は少ないし、他の人の気配り(手伝いやアイデアだしなど)の余裕ができる。つまり「ゆとりの法則」ができるわけだ。きちきちの作業をやるだけでは、ピースは全く動かなくなっていまう。ある程度の余裕がないとチームはうまく動かない。

わたしの場合は、フリーで仕事を受けることになるので、多数の仕事が平行で走る。シングルタスクの最速スピードよりもマルチタスクのスレッド操作のほうが遅くなるのは IT 屋では周知の事実だと思う。思うが、実際のところは

  • お客からの回答待ちなどで、こっちに待ち時間が発生する
  • 開発が先にできあがっても、お客の都合でリリースなどで待たされることが多い
  • 協力しているメンバーの進捗スピードなどで、待たされることも多い

ということで、結構な確率で「待ち」が挟まってくる。この場合、会社に居れば、待ちの状態でぼんやりと椅子に座っているのも可能(だとまずいんだけど)なのだが、リモートワークをやっていると、じゃあ気分を変えて別のに手をつけておくか、という形にできるわけだ。

ある程度、開発力があればシングルタスクで仕事をやるよりも、ある程度のマルチタスクのほうが作業効率がよいことがわかるだろう。新人レベルならばひとつの仕事べったりのほうがいいだろうが、ある程度年季が入ったプログラマならば、マルチでやらないとちょっと無駄が多いわけですよ。

アジャイル開発スクラムやチケット駆動、PMBOKの基本的なところは下記の本に書いたので、それを入口にして本格的なスクラムやPMBOKの書籍にあたってみてください。もちろん、いきなり「アジャイル開発スクラム」にあたるのもよいです。

カテゴリー: 開発 | コメントする

GoFとMVCパターンと、ソフトウェア工学のデザインパターンのその後を私見で

せっかくなので、ソフトウェア工学における私がみつけたパターンをいくつか書いておきます。

原著の「Design Patterns」

いわゆる「GoF本」です。「デザパタ」と略されることもあるのですが、巷のデザインパターンの本と区別が付きづらいので「GoF本」で通しています。

日本語訳でもいいのですが、手元には原著のKindle版があります。Kindleってことは、だいぶん後に買ったので、当時(1994年)に読んだわけではありません。日本でのデザインパターンブームが2000年頃(だったっけ?)なので、原著(あるいは訳本)を読まないままデザインパターンを知った或いは覚えた人も多いと思います。私の入り口もそれですね。

ただ、それだけだとコード寄りになってしまうので、いったん原著にあたっておくといいです。が、中身がC++なのでC++が読めないと辛いです。確か、Javaへの翻訳本もあるはずなのですが、GoF本の発端自体が、C++あるいはMVCパターンによるアプリケーション開発の現場からの抽出なので、当時のC++の状況がわからないと読み込めません。いまのJavaやC#だと、GoFの各種パターンが言語仕様に組み込まれた状態になってしまっているので、そのまま読んでも「何でこんなややこしいことになっているの?」としか見えません。

2016年の自分のブログ記事ですが ふと GoF のデザインパターンを再考しておく で GoF パターンを C# で検証しなおしたものがあります。

同時期の Smalltalk best practice patterns

先の GoF 本と同時期に出たケント・ベック氏の書いた本です。買ったのは随分後なので、当時がどういう状況だったのかはよくわかりません。結構高いので、参考までに持っておくというので良いです。

GoF 本と同じく「パターン」と書かれていますが、GoF とは異なり、アレクサンダー氏の「パタン・ランゲージ」を元にしている訳ではありません。UMLのクラス図もありません。コードがSmalltalkで書かれているので、これに慣れていないと読み取れません。

という難点がありますが、ケント・ベックが書いたということと、オブジェクト指向(クラス構造であれメッセージングであれ)を語る上で Smalltalk は外せないので持っています。持っていますが、私としては Smalltalk でオブジェクト指向を語る気はありません。どちらかというと、クラス構造としてのオブジェクト指向(C++やJavaなど)寄りのところで過ごして来たので、別の流れもあるということですね。

ちなみに、メッセージングのほうのオブジェクト指向は Web APIの呼び出しにてかなり実現されていると考えられます。HTTPプロトコルのステータス無し、Cookie等によるステータスの付与、物理的時間的に遠地にあるファンクションのコールなど、Smalltalk の語るオブジェクト指向が別系統でも実現されつつあるかなぁと。つーか、それで十分ではないか、その先に行きましょうよ、って感じですね。

ちなみにその先のパターンに関しては後述します。

追記しておくと、冒頭にある MVCパターンの論文はここにあります。

https://ics.uci.edu/~dfredmil/ics227-SQ04/papers/KrasnerPope88.pdf

大本のデザインパターンとしての「パタン・ランゲージ」

デザインパターンブームの元ネタの「パタン・ランゲージ」です。ご存じ、アレグザンダー氏の著書で高いので、ひと苦労です。しかも、コードじゃなくて建築の話です。

これも2000年のデザインパターンブームのときには買わなかったのですが、のちに購入しました。やっぱり原点をあたってみないとわからないということで。

この中で書かれていることから読み取ると、

パターンの原点であるアレクサンダー著「パタン・ランゲージ」では、現在ある構造物(都市とか商店街とか)を観察すると、部分的な構造物が組み合わさって現在の良い状態があることわかる。逆に、部品の良い組み合わせ、つまりパターンを作っておけば、良い結果を得られやすいのではないか?という発想です。
なので、当時のMVC パターンで作られた数々の完成物、更に成功している製品を眺めた時に、内部構造としてGoFで書かれたようなパターンを見出す事ができた、故にGoFパターンを使うと成功しやすい、という帰納的解法と思われます。

というのが私の結論です。

なので、建築のパターンやソフトウェア工学のパターンを、構造物(あるいはクラス)作成の効率化や、アプリオリ的に構造物とその組み合わせ(≒パターン)をしておけば良い結果が得られる、という思いこみは間違いです。

歴史的に良い結果(建造物の寿命や商店街の活性化、ソフトウェアの寿命など)を得られてきたものを分解してみると、ところどころに良いパターンが潜んでいるということです。この良いパターンや組み合わせを使うことによって、最終的に良い結果が得られる可能性が高いということですね。ここは帰納法的な思考になります。「帰納法」を出して来たのは、ちょうど統計学再入門を読んでいるところなので、当時は「経験上、よくわからないがうまくいくパターンがあるので、温故知新を用いて使ってみよう」という具合です。

私が、よく先人の知恵とか温故知新とか書くのはそれです。

GoF 本あるいはデザインパターンのその先へ

ここから本題です。いまさら GoF のパターンを学んでも仕方がないです。というか鵜呑みにしても、先に書いた通りプログラム言語仕様の中に組み込まれているものが多くて、原著を読んで C++ で読み込んでもたいして知識が得られるわけではありません。

個人的には、組み込みシステムや別途スマホアプリに応用が効くので、ひと通り知っておいて設計に活かすのがベターなのですが、C++ や Smalltalk を学び直して読み込む必要はないかなぁと思っています。

デザインパターンというと「アナリシスパターン」や「ドメイン駆動設計」あたりに走ってしまいがちになるのですが、実はもっとその先があります。

というかですね。アレクサンダーの著書である「パタン・ランゲージ」の派生を追っていくと、他の分野にも応用できると主張されている節がある。

実際、アレグサンダー自身が当時のソフトウェア業界(1994年)に講演をしているので、そこの別業界としての接点があります。

じゃあ、GoF の各種のコードあるいは周辺のデザインパターンのコードではなくて、もっと広い意味で都市計画とか商店街とか人の導線とかいう意味で、ソフトウェア工学に「パターン」は何を与えていたかというと、

「Garbage Collection」では、ガベージコレクションの世代交代が実装されている。今だとGC では当たり前なんだけど、単純な GC だと頻繁に配置側が発生していまうので、利用している世代を分ける。いわゆる、利用する頻度によって分類するという分け方です。これは、キャッシュとか予約発券システムとかに使われているパターン。 単純にキャッシュするのではなく、頻度に対して分布を作るところにパターンがある。

画像の深層学習で一掃されてしまったが「Toward Category-Lravel Object Recognition」というアイデアがあった。特徴量を導き出して、相互の距離を計測して画像認識に使う技で Google とかもやっていました。結果的に、隠れ層を使った7層程度の深層学習(Deep Learning)のほうが効率が良いことになったので、このあたりの実装はなくなってしまったのだが、特徴量的な考え方は統計学とか主要因分析にも応用されているし(おそらく、この当時の画像認識の研究が統計学の応用だろうし)、その後の報酬関数(フィードバックシステム)のパターンがこの当時からあります。生成AIもこのあたりの延長と言える。

こちらは工業デザインのパターンということで、ノーマン著「誰のためのデザイン」。現在は認知科学として捉えるべきなのか?単なるエモーショナルな流行デザインに過ぎないのか不明なんところではあるけど(いわゆるグローバルデザインの話になる)、それでも一種のパターンに従えば、利用者の落とし穴を防ぐことができる。これこそパターン・ランゲージの応用例と言える。
ソフトウェア工学として、一時期UIデザインにも取り入られた筈なのだが、iOS7からのフラットデザインから以降、それが本質的に誰のためのデザインなのかわからなくなっているところがある。個人的には、高齢者カスタムのかんたんスマホのUIとかはいいと思うんですけどね。設定がごちゃごちゃなのはいただけないが、ガラケーっぽいUIに揃えてあるのは、ガラケー主流だった高齢者にはよいデザインだと思う。誰が?という意味では、「高齢者」が使うわけですから。

「コンピュータの中の人口社会」に書かれているエージェントシステムは、ソフトウェア工学のなかで MVC パターンと同じぐらい重要なデザインパターンと思われる。全体を統括的に扱うのではなく、個々のオブジェクトを独立してプロセスやスレッドで動かすというパターン。まさしく、オブジェクト指向による「オブジェクト」の実現なわけだが、それぞれを独立して動かすことにより、相互の影響を環境値として与えることができるのがミソ。いわゆる、品質工学などでいう外乱を内部特性を区別できるシステムである。

エージェントシステムの発想は遺伝的プログラミングとか並列処理とかいろいろ発想が効くので知っておくとよいパターンです。

まとめとしてのパターン

そんな訳で、ソフトウェアにおけるパターンはなにもコーディングに限らないので、システム全体の構築やユーザーの利用動向、UIの作成、PMBOKによるプロジェクト管理、DevOpsやテスト駆動による価値の制御(スループットや計測)などいろいろな場面でパターンがあります。細かい単位のプラクティスではなくて、大局としてのパターン、つまりは軍事ドクトリンに近いのです。みなさま、応用しては如何でしょうか?

といういうブログパターンで〆てみるテスト。

カテゴリー: 開発 | コメントする

GPT4o に技術マニュアルから実装コードを模索する話(C2340R5でデバイス名を変更編)

生成AIを使ってコード生成をし始めて半年ぐらい経つわけですが、「いろいろなコード生成が自動的にできるよ!」という話よりも、実際に目の前のコードをどうやって生成していくのか?うまく動くようにAIが生成したコードをどうやって人(=自分)が修正していくのか?がプログラマには当面の課題になります。

まあ、どうやってChatGPTあるいはCopilotを使って、コード書きを促進させるか?という実例ですね。WEB アプリケーションのように巷に情報が溢れているならばまだしも、組み込みのような情報が閉鎖的(でもないのだけど)で少ない場合にはどうするのか?という例でもあります。実際のところ、ブロック崩しとか顔認識AIのような既にコードがある場合にはそれをコピペすれば言い訳で、ほどよく WEB サイトに散らばっているならば大丈夫なんですが、完全に未知なコード(少なくとも自分にとっては「未知」の状態)の場合には、ちょっと困るわけです。

C2340R5でBLEのデバイス名を変更したい

未知の人には完全に未知だと思います。まずは、「C2340R5」ってのが何なのかを調べないといけません。BLEを知っていればいいのだけど、デバイス名は何なのか、ぐらいは知っておいて欲しいものですが、まあ、そこからスタートしましょう。

ちなみに、CC2340R5 ってのは Bluetooth が入っている組み込み用のボードですね。ESP32 が入っている M5Stack 系で Bluetooth/BLE を扱えば結構情報が多いのですが、Texas Insturuments の CC23xx 系のボードだと情報が少ないのです。まあ、フォーラムがあるだけマシなのと、マニュアルが揃っているのでなんとかなりそうではあります。

https://www.ti.com/product/CC2340R5

実は SimpleLink というライブラリが TI から提供されていて、これに結構書いてあります。書いてはあるのですが、じゃあ「BLEのデバイス名を変更する」方法は書いていないのが難点ではあります(ほんとうのところ、書いていないのではなくて、デバイス名を変更する方法がない、ということなのですが)。

SWCU193 User guide | TI.com https://www.ti.com/document-viewer/lit/html/swcu193

初手は素直に聞いてみる

AIに質問をするときに「目的」と「手段」をうまく切り分けないと変な回答が出てくることが多いのですが、ひとまず初手としては素直に聞いてみるのがいちばんです。ぴったりとした答えがでてくればそれでよいし、まあ違ったとしたらそれを踏み台にして探索を続ければいいのです。

実はですね、この模索を再現しようと思って、初手に「C2340R5でBLEのデバイス名を変更したい」を再び入力したら、正解が出てくるんですよ。実は、SimpleLink のライブラリには動的にデバイス名を変更する API がなくて、BLE のアドバタイズ部分を再起動するしかありません。しかも配信するデバイス名は上記のように直書きになっているので、自前でバイト単位で書き出さなければ(memcpy使うけど)いけないのです。

まあ、このコードを見て「デバイス名が変更できる」と分かるには、その模索がってこそなのですが。

以下は、以前出した間違ったChatGTPの回答を上げておきます。

ChatGPT にマニュアルの PDF を入れる

ChatGPT にはファイルアップロードして、その中を優先的に探索してくれる機能があるので、それを使います。

初期値は英語になっているようですが、次のプロンプトで「日本語で。」というと日本語が主になります。

いろいろと探索する

前回の場合は「simplelink でデバイス名を変更するには?」で質問しています。この手の質問は、初手は漠然とした「目的」を示したほうがよいです。最終的には実装するための「手段」を探すことになるのですが、いきなり手段を示してしまうと、それ以外の手段を探すのが難しくなりがちです(人間の頭的に)。なので、最初は何もわからない振りをして、ChatGPTに尋ね、少し手間がかかりますが掘り込み方式で進めます。

  • TGAP_DEVICE_NAME という定義はどこにもない

  • GGS_DEVICE_NAME_ATT はあるが、デバイス名は変更されない。

  • GAPRole_SetParameter は存在しない。

  • BLEAppUtil_setAdvData は存在していない。

そんな訳で、なかなか正解に辿り着けません。実は、最初の質問ででてきたscanRespDataの書き換えが正解で、なんとか API 経由で書き換えようとしていたのですが、そんな API は存在しなくて、直接データを書き換えて BLEAppUtil_advStart で BLE のアドバタイズを再起動しないといけないのです。

仕方がないので、初期設定されているアドバタイズデータを書き換える

実は TI のプログラムは Code Composer Studio の *.syscfg というファイルを使っています。これが設定ファイルになっていて、各種設定をコード出力しているのです。Code Composer Studio でデバイス名(ble.deviceName)を書き換えると、うまく書き換わるわけで、そのあたりから更にコードを見ていきます。

最終的には BLEAppUtil_AdvInit_t 構造体があって、ここで初期設定されているのが肝です。コメントを見ると Sysconfig から変換されていることがわかるし、const struct になっているので、実にそれっぽいです。

//! Advertise param, needed for each advertise set, Generate by Sysconfig
const BLEAppUtil_AdvInit_t advSetInitParamsSet_1 =
{
    /* Advertise data and length */
    .advDataLen        = sizeof(advData1),
    .advData           = advData1,

    /* Scan respond data and length */
    .scanRespDataLen   = sizeof(scanResData1),
    .scanRespData      = scanResData1,

    .advParam          = &advParams1
};

まずは、const のままでは書き換えられないのでコードを修正します。

//! Advertise param, needed for each advertise set, Generate by Sysconfig
BLEAppUtil_AdvInit_t advSetInitParamsSet_1 =
{
    /* Advertise data and length */
    .advDataLen        = sizeof(advData1),
    .advData           = advData1,

    /* Scan respond data and length */
    .scanRespDataLen   = sizeof(scanResData1),
    .scanRespData      = scanResData1,

    .advParam          = &advParams1
};

その後に、appMain 関数の先頭で advDataLen と advData を書き換えます。

    // デバイス名の変更
    static uint8_t deviceName[] = "BLE-TEST";

    uint8_t deviceNameLen = strlen((const char*)deviceName);
    static uint8_t scanResData[32];
    uint8_t scanResDataLen = 0;
    scanResData[0] = strlen((const char*)deviceName) + 1;
    scanResData[1] = GAP_ADTYPE_LOCAL_NAME_COMPLETE;
    memcpy(&scanResData[2], deviceName, deviceNameLen);
    scanResData[2+deviceNameLen] = 0x02;
    scanResData[3+deviceNameLen] = GAP_ADTYPE_POWER_LEVEL;
    scanResData[4+deviceNameLen] = 0x00;
    scanResDataLen = 5 + deviceNameLen;

    memcpy( attDeviceName, deviceName, deviceNameLen+1 );
    // memcpy( scanResData1 + 2, deviceName, deviceNameLen );
    extern BLEAppUtil_AdvInit_t advSetInitParamsSet_1;
    advSetInitParamsSet_1.scanRespData = scanResData;
    advSetInitParamsSet_1.scanRespDataLen = scanResDataLen;

動的に変更したい場合は、BLEAppUtil_advStop でいったん止めてから BLEAppUtil_advStart すれば OK です。

ChatGPT の回答では、起動時の BLEAppUtil_advStart しか出て来ないのでなかなか正解に辿り着けないのですが、実は「動的に BLE のデバイス名を変えるにはどうすればいいですか?」と質問すると、かなりイイ線までいきます。

void updateDeviceName(const char* newDeviceName)
{
    // 新しいデバイス名を格納(長さチェックを含む)
    size_t nameLen = strlen(newDeviceName);
    if (nameLen > MAX_DEVICE_NAME_LEN)
    {
        nameLen = MAX_DEVICE_NAME_LEN;
    }
    strncpy(deviceName, newDeviceName, nameLen);

    // スキャンレスポンスデータを更新
    uint8_t scanRespData[] =
    {
        nameLen + 1,  // データ長
        GAP_ADTYPE_LOCAL_NAME_COMPLETE,  // デバイス名のタイプ
        // デバイス名データ
    };

    // デバイス名をコピー(新しい名前で上書き)
    memcpy(&scanRespData[2], deviceName, nameLen);

    // BLEアドバタイズ設定を更新
    BLEAppUtil_AdvInit_t advParams = {
        .advParamLegacy = { ... },  // 他のパラメータを設定
        .scanResponseData = scanRespData,  // 更新されたスキャンレスポンスデータをセット
    };

    // 既存のアドバタイズを停止
    BLEAppUtil_advStop();

    // 新しいアドバタイズを開始
    BLEAppUtil_advStart();
}

BLEAppUtil_AdvInit_t advParams が何処で使われているのか?(実際には BLEAppUtil_advStart の中身なんですが)、というのが不明なのでこのコードではうまくいかないのですが、先に説明した const struct BLEAppUtil_AdvInit_t までたどり着いています。

AI でコード出力をする場合、マニュアルにないコードは探せません。逆に言えば、マニュアルにあるコードならば結構な確率で探し出してくれます。数々の回答のコード(間違ってはいるけれども)もかつてのコードかもしれないし、うまく類推しているコードでしょう。

AI でコード出力と言うと「5分でなんとか」方式が多いので、あっという間に作れる雰囲気がありますが、実際のところはかなり違います。まあ、それでも何も分からないところがから、最初の下調べをしてくれてマニュアルからなんとなく導き出してくれるところは便利ですね。当然のことながら、コンパイルも含めて動作確認は人の手でやらないと無理なので、そのあたりで動作確認は必須なところです。

カテゴリー: 開発 | GPT4o に技術マニュアルから実装コードを模索する話(C2340R5でデバイス名を変更編) はコメントを受け付けていません

M5Stack で iBeacon を飛ばす(PlatformIO環境)

iBeacon をスマホで受信するテストしているときに、iBeacon 自体がないので困ることはありませんか?いや、困ってなくても作ってみるのがお勧めです。

たまに作ろうと思うと、ググってもうまく引っ掛からない(iBeaconを受信するツールはあるのですが、発信するほうはあまりないので)、自分のブログに残しておきます。

platformio.ini

初代の m5stack の platformio,ini は次の通り。m5stick cplus とかを使う場合は適宜かえておきます。

[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino
monitor_speed = 115200

lib_deps = 
    m5stack/M5Stack@^0.3.9

RTOS を使わない場合

RTOS を使わずに framework の android で setup/loop 関数を使って作ります。iBeacon を発信するツールとして使いたいときはこれで十分です。

#include <M5Stack.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <M5Stack.h>

static BLEServer *pServer ;
static BLEAdvertising *advertising ;

#define BEACON_UUID "12345678-1111-2222-3333-56789abcdef0"
#define BEACON_MAJOR 0x0001
#define BEACON_MINOR 0x0001
#define BEACON_POWER 0xC5  // Measured power (at 1 meter distance)

void init_beacon() {
  BLEAdvertisementData ibeaconData;
  // 12345678-1111-2222-3333-56789abcdef0
  char UUID[] = { 
    0x12, 0x34, 0x56, 0x78, 
    0x11, 0x11, 
    0x22, 0x22, 
    0x33, 0x33, 
    0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 
    0x00};
    int major = BEACON_MAJOR ;
    int minor = BEACON_MINOR ;
    int txPower = BEACON_POWER;

    ibeaconData.setFlags(0x1A);
    std::string strServiceData = "";
    strServiceData += (char)0x4c;  // apple 
    strServiceData += (char)0x00;  // apple 
    strServiceData += (char)0x02;  // apple 
    strServiceData += (char)0x15;  // apple
    strServiceData += UUID ;
    strServiceData += (char)(major >> 8);    // major
    strServiceData += (char)(major & 0xFF);  // major
    strServiceData += (char)(minor >> 8);    // minor
    strServiceData += (char)(minor & 0xFF);  // minor
    strServiceData += (char)txPower;  // 
    ibeaconData.setManufacturerData(strServiceData);

    // アドバタイズの設定
    advertising = BLEDevice::getAdvertising();
    advertising->setAdvertisementData(ibeaconData);
    advertising->setScanResponse(false);

}

// Arduinoのsetup関数
void setup() {
    // M5Stack の初期化
    M5.begin();
    Serial.begin(115200);

    BLEDevice::init("M5Stack");
    BLEServer *pServer = BLEDevice::createServer();
    init_beacon();

    // taskBeacon 内で start している
    // 実は start 1回だけではうまくいかないことが多く、
    // start/stop を繰り返す必要がある
    // advertising->start();
}

// Arduinoのloop関数
void loop() {
    // メインタスクのループ
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(10, 10);
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(2);
    M5.Lcd.print("Hello, M5Stack with FreeRTOS! ");
    M5.Lcd.print(BEACON_UUID);
    Serial.println("Hello, M5Stack with FreeRTOS! ");
    // vTaskDelay(pdMS_TO_TICKS(3000));  // 3秒待機
    advertising->start();
    delay(3000);
    advertising->stop();
    delay(3000);
}

iBeacon で送信するサービスUUIDは、Apple の 0x004c となっていて、内部データとして iBeacon として送信したい UUID(12345678-1111-2222-3333-56789abcdef0)を設定しています。本来ならば、BEACON_UUID から 16 bytes のデータを作るところでが、面倒なので UUID 配列に詰め込んでいます。ちなみに 0x1502 は特性ID(キャラクタリスティック)のようなもので固定値です。

このコードでは、init_beacon 関数内でビーコンの発信設定をしているので、advertising->start(); を 1回だけ呼び出せばビーコンが送信されるような気もするのですが、うまくいきません。

これが BLEAdvertising クラスの仕様なのか、M5Stack の制限なのか(あるいは手元の M5Stack が不調なのか)わかりませんが、loop 関数にあるように、ある程度の間隔で start/stop を繰り返してやらないと iBeacon のデータが飛びません。

iBeacon が無事飛んでいると、次のように別のツールで動作確認ができます。

これは dotnet で作ったツールで、こんなコードで動かしています。

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Storage.Streams;

// BLEのスキャナ
BluetoothLEAdvertisementWatcher watcher;
// MACアドレスの保持(ランダムなので意味はない)
List<ulong> maclist = new List<ulong>();

Main(args);

void Main(string[] args)
{
    Console.WriteLine("Folkbears iBeaconCheck");

    watcher = new BluetoothLEAdvertisementWatcher()
    {
        ScanningMode = BluetoothLEScanningMode.Passive
    };
    // スキャンしたときのコールバックを設定
    watcher.Received += Watcher_Received;
    // スキャン開始
    watcher.Start();
    // キーが押されるまで待つ
    Console.WriteLine("Press any key to continue");
    Console.ReadLine();
}

void Watcher_Received(
    BluetoothLEAdvertisementWatcher sender,
    BluetoothLEAdvertisementReceivedEventArgs args)
{

    var uuids = args.Advertisement.ServiceUuids;
    var mac = string.Join(":",
                BitConverter.GetBytes(args.BluetoothAddress).Reverse()
                .Select(b => b.ToString("X2"))).Substring(6);
    var name = args.Advertisement.LocalName;
    var rssi = args.RawSignalStrengthInDBm;
    var time = args.Timestamp.ToString("yyyy/MM/dd HH:mm:ss.fff");
    

    if ( args.Advertisement.ManufacturerData.Count > 0)
    {
        var data = args.Advertisement.ManufacturerData[0];
        if ( data.CompanyId == 0x004c && data.Data.Length >= 23)
        {
            byte[] ibeacon = new byte[data.Data.Length];
            DataReader.FromBuffer(data.Data).ReadBytes(ibeacon);
            if (ibeacon[0] == 0x02 && ibeacon[1] == 0x15)
            {
                byte[] uuid = ibeacon[2..18];
                byte[] major = ibeacon[18..20];
                byte[] minor = ibeacon[20..22];
                byte txpower = ibeacon[22];

                int majorvalue = major[0] * 256 + major[1];
                int minorvalue = minor[0] * 256 + minor[1];
                Console.WriteLine($"{time} [{tohex(uuid)}] {rssi} dBm {mac} "
                    + string.Format("{0:x04}", majorvalue) + " "
                    + string.Format("{0:x04}", minorvalue) + " "
                    );
            }
        }
    }
    string tohex( byte[] data )
    {
        return BitConverter.ToString(data).Replace("-", "").ToLower();
    }
}

*.csproj ファイルはこんな感じです。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

RTOS を使う場合

先のコードでは iBeacon の発信と停止を loop 内で制御しましたが、RTOS を使えばタスク内で記述ができます。

#include <M5Stack.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <M5Stack.h>

static BLEServer *pServer ;
static BLEAdvertising *advertising ;

// タスクの定義
void task1(void *pvParameters) {
    while (1) {
        Serial.println("Hello from Task 1");
        vTaskDelay(pdMS_TO_TICKS(1000));  // 1秒待機
    }
}

void task2(void *pvParameters) {
    while (1) {
        Serial.println("Hello from Task 2");
        vTaskDelay(pdMS_TO_TICKS(2000));  // 2秒待機
    }
}

void taskBeacon(void *pvParameters) {
    while (1) {
        Serial.println("task beacon start");
        advertising->start();
        vTaskDelay(pdMS_TO_TICKS(3000));  // 3秒待機
        Serial.println("task beacon stop");
        advertising->stop();
        vTaskDelay(pdMS_TO_TICKS(10000));  // 10秒待機
    }
}

#define BEACON_UUID "12345678-1111-2222-3333-56789abcdef0"
#define BEACON_MAJOR 0x0001
#define BEACON_MINOR 0x0001
#define BEACON_POWER 0xC5  // Measured power (at 1 meter distance)

void init_beacon() {
  BLEAdvertisementData ibeaconData;
  // 12345678-1111-2222-3333-56789abcdef0
  char UUID[] = { 
    0x12, 0x34, 0x56, 0x78, 
    0x11, 0x11, 
    0x22, 0x22, 
    0x33, 0x33, 
    0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 
    0x00};
    int major = BEACON_MAJOR ;
    int minor = BEACON_MINOR ;
    int txPower = BEACON_POWER;

    ibeaconData.setFlags(0x1A);
    std::string strServiceData = "";
    strServiceData += (char)0x4c;  // apple 
    strServiceData += (char)0x00;  // apple 
    strServiceData += (char)0x02;  // apple 
    strServiceData += (char)0x15;  // apple
    strServiceData += UUID ;
    strServiceData += (char)(major >> 8);    // major
    strServiceData += (char)(major & 0xFF);  // major
    strServiceData += (char)(minor >> 8);    // minor
    strServiceData += (char)(minor & 0xFF);  // minor
    strServiceData += (char)txPower;  // 
    ibeaconData.setManufacturerData(strServiceData);

    // アドバタイズの設定
    advertising = BLEDevice::getAdvertising();
    advertising->setAdvertisementData(ibeaconData);
    advertising->setScanResponse(false);
}

// Arduinoのsetup関数
void setup() {
    // M5Stack の初期化
    M5.begin();
    Serial.begin(115200);

    BLEDevice::init("M5Stack");
    BLEServer *pServer = BLEDevice::createServer();
    init_beacon();

    // taskBeacon 内で start している
    // 実は start 1回だけではうまくいかないことが多く、
    // start/stop を繰り返す必要がある
    // advertising->start();

    // タスクの作成
    xTaskCreate(&task1, "task1", 2048, NULL, 5, NULL);
    xTaskCreate(&task2, "task2", 2048, NULL, 5, NULL);
    xTaskCreate(&taskBeacon, "taskBeacon", 2048, NULL, 5, NULL);
}

// Arduinoのloop関数
void loop() {
    // メインタスクのループ
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(10, 10);
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(2);
    M5.Lcd.print("Hello, M5Stack with FreeRTOS! ");
    M5.Lcd.print(BEACON_UUID);
    Serial.println("Hello, M5Stack with FreeRTOS! ");
    vTaskDelay(pdMS_TO_TICKS(3000));  // 3秒待機
}

ここでは、task1, task2, taskBeacon という3つのタスクが同時に動いています。優先度が同じなので、並列で動いているのですが、優先度を変えれば iBeacon 発信のタスクだけ優先して動かすことも可能でしょう(実験までしていませんが)。

先の iBeacon の発信/停止の切り替えは loop 関数から taskBeacon 内に移動しています。

m5stackの画面はこんな感じです。

カテゴリー: 開発 | M5Stack で iBeacon を飛ばす(PlatformIO環境) はコメントを受け付けていません

M5Stack で RTOS を試すときに main.cpp で動作させること

M5Stackシリーズを使うと、内部で ESP32のチップが使わているのでWi-FiやBluetooth周りのテストをするのに非常に便利です。手元の BLE 関係の通信も、いったん M5Stack で試し実装をしてみてスマホで動作確認をしてから、専用ボードに書き込むと動作がわかりやすいですよね。

手元でTIのボードを使ってBLE通信をしているのですが、そこでRTOSを使うことになっています。なぜRTOSを使うことになったのかはさておき、最近では AWS から FreeRTOSが配布されているので、これの準じたサンプルコードがあちこちで配布されています。当然ながらTIでも配布されているので、これを参考に作ります。

で、各ボードでのRTOS対応のコードは結構な職人プログラマが整備しているので、うまいことRTOSの説明や動きを解説することができあません。せっかくRTOSという統一されたOSのAPIを使っているわけですが、それぞれのボードに対応した専用関数で置き換えられているので(これはこれで仕事上は便利)コーディングがしやすいのですが、まあ、内部でどう動いているのか不可解なところがあるので、できることならば生のRTOSのAPIを叩いて試しておきたい。

ちなみに、私の場合 μiTronで仕事をしたことがあるのでリアルタイムOSまわりの動きはわかるのですが、RTOSのほうは初見ですね。

VSCode で PlatformIO を使う。

以前はESP32の開発環境を整えるのが一苦労だったのですが、最近は VSCode に「PlatformIO」という拡張をいれるだけで十分です。コンパイラ諸々をインストールしてくれます。

VSCode-PlatformIO IDEを使って、ESP32の開発環境を構築およびLチカ https://zenn.dev/kotaproj/articles/esp32_vscode_pio

デバッガ機能等便利なものが多いのですが、私の場合は Arduino IDE の上位互換機能があれば十分なので、build と upload だけあれば十分なところです。

Arduino IDE の場合はコード補完機能(インテリセンス機能)が効かないのでコーディングに苦労することが多いのですが、VSCode だと補完が効くし、GitHub Copilot を入れた現状だとかなりの部分をコード補完してくるので便利ですね。

RTOS対応のプロジェクトを作る

RTOSを使ってプログラミングをするのに、何かライブラリを入れないといけないのでは?と思いますが、実は ESP32の「espidf」というフレームワークには既に、RTOSのAPIが含まれています。他のボードだと結構苦労しそうな感じなのですが、少なくともM5StackシリーズでRTOSをやるときは特に追加のライブラリは必要ありません。

手元にあるのが、初期のm5stackなのでBoardには「M5Stack Core ESP32」を選択します。

ここでは、Framework に「Espidf」を選択していますが、実は「Android」でも構いません。どういうライブラリの構造かわかりませんが、ここで指定する「Android」のほうにもRTOSのライブラリが入っています。

最初、m5stackでRTOSプログラミングをするときのサンプルコードが、「Espidf」が多かったので、これにしたのが以下の躓きの始まりです。結論から言えば、(おそらくライブラリの容量の関係)「Android」で問題がないと思われます。

platformio.ini を比較する

プロジェクトを作成したら platformio.ini を確認しておきます。ここでフレームワークの指定やライブラリの追加ができます。

Framework が 「Espidf」の場合

[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = espidf

Framework が Android の場合

[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino

platformio.ini の中身は framework のところ以外は変わりません。

ただし、このままでは M5.Lcd のような、モニタを使ったコードが書けないので、lib_deps を追加しておきます。これで M5.* 系のライブラリが自動でインストールされます。

[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino

lib_deps = 
    m5stack/M5Stack@^0.3.9

ChatGPT に RTOS を使ったサンプルコードを書いて貰う。

実は GPT4o を使うと、ほどよく M5Satck を使って RTOS のサンプルコードを書いてくれます。

以下は、提案してもらったコードなのですが、実はビルドができません。

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_system.h>
#include <esp_log.h>
#include <M5Stack.h>

// ログタグの定義
static const char *TAG = "main";

// タスクの定義
void task1(void *pvParameters) {
    while (1) {
        ESP_LOGI(TAG, "Hello from Task 1");
        vTaskDelay(pdMS_TO_TICKS(1000));  // 1秒待機
    }
}

void task2(void *pvParameters) {
    while (1) {
        ESP_LOGI(TAG, "Hello from Task 2");
        vTaskDelay(pdMS_TO_TICKS(2000));  // 2秒待機
    }
}

// メイン関数
void app_main(void) {
    // M5Stack の初期化
    M5.begin();

    // タスクの作成
    xTaskCreate(&task1, "task1", 2048, NULL, 5, NULL);
    xTaskCreate(&task2, "task2", 2048, NULL, 5, NULL);

    // メインタスクのループ
    while (1) {
        M5.Lcd.fillScreen(BLACK);
        M5.Lcd.setCursor(10, 10);
        M5.Lcd.setTextColor(WHITE);
        M5.Lcd.setTextSize(2);
        M5.Lcd.print("Hello, M5Stack with FreeRTOS!");
        vTaskDelay(pdMS_TO_TICKS(3000));  // 3秒待機
    }
}

error “This library only supports boards with ESP32 processor.”

なエラーが M5Stack.h 内で出ています。

よくわからないので、framework が「Android」のほうに、main.c をコピーすると以下のようなエラーがでます。

“class” がないというエラーなのですが、実は main.c をそのままコピーしたので C言語で扱われているからです。なので、main.cpp にして C++ でコンパイルされるようにします。

実は SD.h がないとか諸々エラーになることもあるのですが、結論としては main.c のままなのが原因で、main.cpp にすれば ok です。

コンパイルは順調に進むのですが、最後のリンクで失敗します。

どうやら、

  • framework が espidf のときはC言語で、app_main がエントリー関数(俗にいうmain関数)
  • framework が android のときはC++で、setup と loop が呼び出される

という違いがあるようですね。TI のサンプルコードが C言語のほうの *.c に限られていたので、Android のほうが C++ のほうの *.cpp に統一されていたのを忘れていました、というオチです。

で、最終的には以下のコードで動くようになります。

#include <M5Stack.h>

// タスクの定義
void task1(void *pvParameters) {
    while (1) {
        Serial.println("Hello from Task 1");
        vTaskDelay(pdMS_TO_TICKS(1000));  // 1秒待機
    }
}

void task2(void *pvParameters) {
    while (1) {
        Serial.println("Hello from Task 2");
        vTaskDelay(pdMS_TO_TICKS(2000));  // 2秒待機
    }
}

// Arduinoのsetup関数
void setup() {
    // M5Stack の初期化
    M5.begin();
    Serial.begin(115200);

    // タスクの作成
    xTaskCreate(&task1, "task1", 2048, NULL, 5, NULL);
    xTaskCreate(&task2, "task2", 2048, NULL, 5, NULL);
}

// Arduinoのloop関数
void loop() {
    // メインタスクのループ
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(10, 10);
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(2);
    M5.Lcd.print("Hello, M5Stack with FreeRTOS!");
    vTaskDelay(pdMS_TO_TICKS(3000));  // 3秒待機
}


先頭部分の「freertos/FreeRTOS.h」あたりは、M5Stack.h から自動的にインクルードされているらしく必要ありません。RTOS 特有の関数としては、タスク生成の xTaskCreate の部分で、これが呼び出せていれば RTOS でビルドできています。

This page describes the RTOS xTaskCreate() FreeRTOS API function which is part of the RTOS task control API. FreeRTOS is a professional grade, small footprint, open source RTOS for microcontrollers. https://www.freertos.org/a00125.html

アップロードして動作させる

アップロードでして動作させると m5stack のモニタに「Hello, M5Stack with FreeRTOS!」の表示がでて、シリアルポートには、以下のような2つのタスクが交互っぽい形で動いていることがわかります。

ここまでで半日費やしてしまったので力尽きて、何をやろうとしていたのか忘れてしまいました。まあ、いいんですが。

後日、BLE通信のコードをちょっと入れ込んで試しおくつもりです。

余談

ちなみに、これらのサンプルコードは ChatGPT の GPT4o を使って尋ねているのですが、最終的に main.c と main.cpp に気付いた部分はこれになります。

いやいやいや、そもそも ESP-IDF フレームワークと Android フレームワークを混在させてきたのは君なんですが!をぐっとこらえてw

まあ、それでも GPT4o を使って、何度も「このコードだと動かないのですが、どうしたらいいですか?」を繰り返している訳で。なかなか一発で解決にはなりませんね。

それでも最初のスタートダッシュは生成AIを使うと便利です。

カテゴリー: 開発 | M5Stack で RTOS を試すときに main.cpp で動作させること はコメントを受け付けていません

CrawdStrike 関係で Windows のカーネルドライバーの動きを少し解説

NHK の解説

【最新】システム障害 マイクロソフトは「サービスは回復」と発表 影響は一部で継続も収束にむかう | NHK | 航空 https://www3.nhk.or.jp/news/html/20240720/k10014517151000.html

Microsoft からの復旧手順

KB5042421: CrowdStrike issue impacting Windows endpoints causing an 0x50 or 0x7E error message on a blue screen – Microsoft Support https://support.microsoft.com/en-us/topic/kb5042421-crowdstrike-issue-impacting-windows-endpoints-causing-an-0x50-or-0x7e-error-message-on-a-blue-screen-b1c700e0-7317-4e95-aeee-5d67dd35b92f

Recovery options for Azure Virtual Machines (VM) affected by CrowdStrike Falcon agent – Microsoft Community Hub https://techcommunity.microsoft.com/t5/azure-compute-blog/recovery-options-for-azure-virtual-machines-vm-affected-by/ba-p/4196798

どのようにNULLポインタアクセスなのか?

画面キャプチャだけ抜き出し。

このツイートでは、00000000 0000009c ゆえに NULL ポインタアクセスという指摘になっているが、(確か)実際は Windows 起動時はリアルモードで実行されるので DS レジスタが有効になっていて DS:009c がアクセスされる。つまり 002b:009c から DWORD の 4バイトの読み込みでアクセスエラーとなっているので、NULL ポインタアクセスとは限らない(まあ、この DS が間違っている可能性もあるので)。ここは私も見逃したところのなので、特に言及はしない(私が間違っている可能性あるし)以下はひとつの考察である。

アセンブリコードを見ると、

mov r9d,dword [r8] ds:002b:00000000`0000009c=????????

r8 レジスタが示すメモリつまり [002b:009c] なんだけど、きちんとデータセグメントにあるっぽいのに、read access error を起こしているのはかなり変な状況になっている感じがする。

が!!!、再考すると、

  • カーネルドライバは「プロテクトモード」で動いている
  • セグメントレジスタの値(特にcs=0010, ss=0018, ds=002bなど)は、プロテクトモードのGDT内のエントリを指している可能性が高い。

とのことなので、これは「プロテクトモード」っぽい。

となると、先のツイートにあるように、なんらかの変数A(配列の先頭と思われる)に対して「オフセットで、0x9cの場所を参照しようとしている。つまり

DWORD x = A[ 0x9c ] ;

みたいなコードで、DWORD(4バイト)読み込みをしようとしているのだが、最初の変数 A が NULL ポインタなので(おそらく初期化漏れ)、アクセスエラーを発生している。大抵のメモリでは、先頭 0x1000 位が NULL ポインタチェック用に用意されていることが多く、それに引っ掛かったのだろう。

DWORD *A = 0x00 ;
// ここで変数 A の初期化漏れ
DWORD x = A[ 0x9c ] ;

この部分、変数 A の初期化漏れを Rust で回避できるかどうかなんだけど、Rust の場合は初期化していない変数を利用しようとするとコンパイルエラーになるので、これは回避できるかも。

「インサイド Windows」下巻の第12章より

ここの「ドライバー実行環境(DXE)」のときに、既にプロテクトモードになっているそうだ。なので、リアルモードのセグメント化でつかう DS レジスタは、既に GDT を示しているのだけなので、00000000 0000009c がフラットなアドレスと指定される。メモリを示すときはこんな小さなアドレスを示すことはないので、配列の先頭を示すアドレスの初期化漏れ(しかもわざわざ 0x00 が入っていた)と考えるのが正しい。

参考先

Windows カーネルドライバーって Visual Studio のプロジェクトテンプレートにあるらしく、結構便利になっている。

Write a Hello World Windows Driver (KMDF) – Windows drivers | Microsoft Learn https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/writing-a-very-small-kmdf–driver

追記 2024/07/25

Technical Details: Falcon Update for Windows Hosts | CrowdStrike https://www.crowdstrike.com/blog/falcon-update-for-windows-hosts-technical-details/

名前付きパイプの読み取りで失敗して、C-00000291-*.sys の読み込みあたりでエラーが発声しているので、Although Channel Files end with the SYS extension, they are not kernel drivers. カーネルドライバーではないよ、という理由らしい。C-00000291-*.sys ファイルを消すと正常動作するので、読み取り先のデータ領域っぽい(多分、ファイルをオープンして、メモリマップドしていると思う)ので間違いではないが、NULLポインタエラーの否定にはなっていないし、おそらく

  • C-00000291-*.sys ファイルが新規に追加される
  • C-00000291-*.sys ファイルをオープンする。
  • 名前付きパイプでアクセスする(大抵はメモリマップドである)
  • このときに NULL チェックを怠ったか、C-00000291-*.sys の先が NULL 書き込みされていたかでアクセスエラー発生

という手順だろう。

ほぼ100%再現性があるっぽいので(何万台というレベルで発生いるため)、これ最初に動作確認したのか?が危ぶまれるのだが CrawdStrike社としてはどうなのだろうか?

単体テストというか、リリース前の運用テストをスキップしてしまった感じがする。

カテゴリー: 開発 | CrawdStrike 関係で Windows のカーネルドライバーの動きを少し解説 はコメントを受け付けていません

詳細設計書を「効果的に」活用するためのパターン

発端はこのツイート

スナップショットはこちら(ロードするたびに割合がかわってしまうので)

前提条件

いろいろ議論が発散してしまうので、前提条件を確認しておきます。

「詳細設計」というのは、PMBOKの「Detail Design」にあたる、とします。

・要件定義
・概要設計、外部設計、基本設計、画面設計
・詳細設計、内部設計
・実装
・各種のテスト

PMBOKで云うと概要設計と基本設計が同じになります。最近は「基本設計」の方が言われることが多かった(多分、新聞などで使われることになったからだと思う)のですが、いわゆる顧客からの要件定義を受けて、システムを構築するための設計のことです。

実は、大手の会社でもここの分類が分かれるところで「自社だと違う」というパターンが多いでしょう。実際に画面設計(ユーザーインターフェース)やネットワーク設計などの仕様書が必要になることが多く、PMBOKが規定する分類にあてはまるとは限りません。

この分類は、漠然としたものではなく「誰が誰のために作るのか?」という視点で分けるとわかりやすいです。

「要件定義」は、顧客の要望を文書化したものです。顧客自身が書いてもいいし、請け負った会社が書いても構いません。

概要設計(基本設計や画面設計やネットワーク設計など)は、要望をどのように実現するのか?を記述していきます。これは、要件定義と対になるもので、いわゆるプロジェクト予算を作るときに利用します。ただし、実際のITプロジェクトの契約では、要件定義段階で契約することが多く、概要設計を作らずに(あるいは概要の概要みたいなものは作る)契約してしまうことも多いのですが、これは別の問題なのでここでは議論しません。

詳細設計は、概要に従ったものを具体的にクラスやフレームワーク、関数や、シーケンスなどを考慮して記述していきます。近年「詳細設計」を飛ばしてしまうことが多いのは、概要設計から実装(WEBの各種フレームワークやプロトタイプ、ノンコーディングできる実装環境など)が繋ぎやすいところにあります。いわゆる、スクラッチビルドが試せる環境が整っていたことがあります。逆に言えば、プロトタイプビルディングができない環境である場合は、詳細設計が必須です。

実装は、実際にコーディングをして動かす部分です。これに対しても以前はモジュール設計、クラス設計などを記述して分離させていた時期もあるのですが、最近では詳細設計に入れてしまう場合が多いです。これは会社のプロジェクトによって異なるところです。

UMLは、かつては概要設計と詳細設計の両方で使われる可能性があったのですが、現在はあまりUMLで書いてある仕様書を見かけません。これは有償のケースツールの衰退もあるのと、UML自体が細分化されてしまって、忌諱されてしまったためでしょう。

UMLを記述する場合は、最低限

・クラス図
・シーケンス図
・オブジェクト図(これはなくてもよい)
・状態遷移図
・ユースケース記述

だけ覚えればOKでしょう。これは時間と具象/抽象の4象限でわけたものです。

ただし、これ自体はここでは議論しません。

詳細設計は誰のためにつくるのか?

さて、詳細設計は誰のために作るのか?をパターン分けしておきましょう。先のアンケートを分類すると、

  1. 詳細設計書と実装が別の人
  2. 詳細設計書と実装が同じ人
  3. 詳細設計を書かず、実装のみ

のパターンに分けられています。それぞれ、意味があるので何がよいという訳ではありません。ITプロジェクトの状態に沿った形で、詳細設計を書くかあるいは省略するかすればよいのです。

詳細設計と実装が別の会社の場合

契約上、詳細設計と実装が異なる会社の場合があります。かつての中国オフショアなどがそれで、日本で詳細設計書を書いて中国のプログラマに発注していた時期があります。オフショアに限らず、子会社は外注に出すパターンはこの組み合わせが多いです。ただし、WEBサイト開発のような場合は、概要設計や画面設計から実装へ進む場合も多いので、これは情報システムや銀行系の場合に多いですね。

それぞれの会社が別なので「瑕疵」という契約が発生します。どのような場合に「瑕疵」となるのか、不具合をどのように直すのか?が問われるところなので、契約のために「詳細設計書」が必要になります。

別の会社であっても、派遣社員や準派遣の場合は、発注会社が責任を持つことが多いので、詳細設計書を省略しても契約上問題ありません。

詳細設計と実装が同じ会社(人)の場合

同じ会社であり同じ人であるならば詳細設計を作らずに、画面設計からいきなり実装に移ることも多いでしょう。保守運用的に詳細設計を残す或いは残さないの判断もあるのですが、実装する人の経験が浅かったり、ロジックが複雑怪奇であったりする場合には、詳細設計書を書くのがベターです。

詳細設計といっても、コードと逐一同じものを書くのではなく、UMLなどを使いながら簡略化していきます。

私の新人教育では、

・箇条書きで詳細設計を行う
・シーケンス図を記述する

ことにしています。シーケンス図はプログラムコードが入り組んでいた時、タイミングが微妙なときを考察するのに便利です。基本的に詳細設計は使い捨てです。そのままコードのドキュメントとして残すか(文芸的プログラミングのように)、単なるメモ書きとして記録しておくだけでよいでしょう。

コードレベルの詳細設計は書かない

先のアンケートの結果を見ればわかる通り、私は「コードレベルの詳細設計」は書きません。しかし、場合によってはシーケンス図のような詳細設計を書きます。

このアンケートで答えた人がどの位の「詳細設計書」を考えているかわかりませんが、分布としては形式的な詳細設計書はなくなっていく傾向にあります。

ここで注意しておきたいのは、

・アジャイル開発だからといって、詳細設計が存在しない訳ではない
・ウォーターフォール開発だからといって、詳細設計が必須な理由はない

ということです。ITプロジェクトのスタイル(アジャイル開発、計画駆動、イテレーション開発などなど)に問われるものではありません。アジャイル開発においても「設計書」は情報共有のために必要となるので、そこに囚われる必要はないでしょう。

設計書からコードを出力するツールはあるのか?

あると言えばあるんですよ。

記事を見ると解る通り、みずほ銀行の件は失敗しています。失敗していますが、詳細設計書からコードを出力するツールはあるんです。あるんですが、失敗しています、ということを覚えておくことが重要です。

個人的には MDA の復活になると思うのですが、それがいつ頃になるかわかりません。Scratch とか Node-RED のツールとかがそれに近いです。各種のノンコードツールもそれに近いものがあります。ノンコードツールの延長線上にあるとは思えないのですが、一種ではあると思われます。

参考先

図解即戦力 PMBOK第6版の知識と手法がこれ1冊でしっかりわかる教科書

図解即戦力 アジャイル開発の基礎知識と導入方法がこれ1冊でしっかりわかる教科書 Kindle版

図解入門 よくわかる最新 システム開発者のための仕様書の基本と仕組み

カテゴリー: 開発 | 詳細設計書を「効果的に」活用するためのパターン はコメントを受け付けていません

MS-MVP の再受賞(14回目)と学習効果の話

今年も無事、Microsoft MVP を受賞することができました。ぱちぱちぱち。

ひとまず、Microsoft系の書籍を書いている限りは、申請を出すつもりなので、引き続きよろしくお願い致します。

「Azure OpenAI Service 入門」のほうも合わせて。

さて学習効果の話

ツイートを失念してしまったのですが、学習効果が複利的に逓増するか?というのが再び流行りました。もともとの図自体はかなり前に流行ったものなので、目新しいことではないのですが、賛同だけではなく、疑問符な方たちが結構いたようなので、学習効果についてちょっと記述しておきます。

誰が云ったか忘れてしまったのですが、「1.01の法則と0.99の法則」というものです。

1.01を365日続けると37倍になり、0.99で努力が減ると0.03になるという話で、最初でてきた当時も「1年間続けたとしても、さすがに37倍にはならんやろ」という結論がでています。まあ、1.01の努力ってのもたとえ話なので、実際に37倍になるかどうかは不明です。

さて、これを数式で表すと、次のように複利的にΠで計算するか、加算ということでΣで表すかという違いになります。

果たして、微々たる努力(1.00のところを1%だけ上げるので、勤務時間8時間であれば、8x60x0.01 = 4.8分/日ということになる)で、じゃあ、1年後に37倍になるか?という話になり、まあそうなんらんやろという結論で、Bの加算程度じゃないだろうか?というツイートが今回散見しました。努力が加算されるという意味ですね。

なにかの作業的なものであれば、たかだか1日のうち5分間というのは大した効果ではありません。単純に自給換算にで言えば、8時間勤務が8時間5分勤務になったというだけなのです。

が、Aのような複利効果を考えるとき、実はベースとなる実力そのものが増大するため、その効果はその時々において確かに複利的になる(常に元金が増えるような状態なので)ものです。たとえ話のように、356日連続で向上するとなるとなにやらうさん臭くなってしまいますが、これが「1か月単位で、ある程度実力が向上する」となると少し話が違ってきます。

たとえば、プログラムのコードを書く作業を生産性とみなし(詳細設計や単体試験まどを含めた、バグのないコードをどのくらいのペースで書けるか?ということを考えてみましょう)たときに、1か月のうちで、1.00の時間ところを0.99の時間で書けるようになったとしましょう。たかだか1か月のうちで1%しかスピードアップはしませんが、これが12か月になると、0.99^12 = 0.886 となり、おおよそ 90%程度になります。つまり、1割位の短縮ができるわけです。

プログラムを書くスピードはそのプロログラム言語の習得率や設計のうまさ、テスト環境の整え方など各人の経験で向上できるところの多い分野です。この「経験」は、Σのように加算ではなく、Πのように累積されることは、いわゆるプログラマ力の個人差が大きいところを見ると明らかでしょう。スタート時点での経験の差(あるいは習得率の差)は、その差のまま続くのではなく、1年間のうちに「経験」として向上するものです。さらに、1か月ごとの累積で考えるならば、1か月単位のスタートで「経験」に対して累積していくと言えるでしょう。スタートとなる土台は、次のスタートの時には土台が上がっていると考えられます。

このあたりは町工場などの職人も同じですね。学習前の土台を常々上げることによって、次のステップが楽になります(段差が小さくなる状態にしておきます)。

ここでは仮に1か月につき1%の向上にしましたが、5%と仮定したときは、当初1.0だった見積もりが0.54程度に住むことになります(0.95^12 = 0.54)。つまり1年たてば、最初の見積もり期間よりも半分の工数でコードができあがる、という予定になります。

実際、プログラミング言語の習得だけではこの向上は見込めませんが、

  • フレームワークの使い方の習得
  • サンプルコードやテストコードを作ることで、落とし穴を回避する
  • あるいは、あらかじめ落とし穴に落ちておく(踏み抜いておく)
  • テストコードや不具合対処の勘がさえてくる(経験上)
  • プログラミング言語自体の習熟度が上がっている

などの要因もあり、単純な加算ではなく複利的に実力が向上することが見込まれます。

実は、似たところはプロジェクトのスケジューリングにも言えるのです。プロジェクトの当初では未知の部分も多く、プロジェクトで利用するライブラリや環境にも不慣れな状態からスタートします。しかし、プロジェクトが終盤になれば、プロジェクトのメンバーはライブラリや環境にも習熟して、数々の不具合を素早く対処できるようになってきています。

終盤で下手に炎上しているプロジェクトではなければ(実は炎上しているプロジェクトであったとしても、プロジェクトメンバー自身は成長しているのですが)、プロジェクトの最初の力量よりもプロジェクト終了時の力量のほうが上であると考えられます。

つまり、この習熟度の高い状態を疑似的に作り出せば、プロジェクトはより安全に安定期(予測可能という意味で)に入ることができます。

  • プロジェクトで使うフレームワークを事前にサンプル等を作って試しておく
  • 運用直前でトラブルになりそうなリスク要件をあらかじめ確認しておく
  • もともと、習熟度の高いメンバーを入れる(可能であれば)
  • プロジェクト途中で「学習」を重視する

という様々な方法が考えられます。

そんな訳で、私的には学習による複利的効果(基礎力をアップするという意味で)を期待して、基礎力のアップに努めたほうがプロジェクトも安定しやすいだろう、ということです。

加筆

これは正しいwww

カテゴリー: 開発 | MS-MVP の再受賞(14回目)と学習効果の話 はコメントを受け付けていません