マークダウンメッセージボックス

メッセージボックスをマークダウン記法で記載できると、表示方法の幅が広がり役立つのでは無いかと思い、社内ライブラリに追加してみました。
スタイルシートも設定できるようにしました。

使い方は以下のようになります。


var msg = new MarkdownMsgBox("#ヘッダA\n* 項目A\n* 項目B\n\n##ヘッダB\nメッセージA\nメッセージB\n\n-----\n1. 項目C\n1. 項目D", "テスト", MsgBoxButtons.OKCancel);
msg.StyleSheet = "h1 {font-size=30pt;  border-bottom: solid 4px Gray; color: Blue;}\nh2 { font-size = 20pt; border-bottom: solid 2px Gray;color: DarkBlue; }";
msg.Show();

結果のメッセージボックス表示は以下のようになります。

KeyValue属性

C#のお話です。

KeyとValueのペアを定義できる属性があると汎用に使えて便利ではなかろうかと思い、KeyValueAttribute属性クラスと、列挙型の場合に簡単にKeyValue属性の値にアクセスするためのEnumInfoクラスを作成してみました。以下に使用例を記載します。


//KeyValue属性を使用した列挙型の定義
public enum Idol {
    [KeyValue("Age", 16), KeyValue("Height", 162), KeyValue("BloodType", "A")]
    Chihaya,
    [KeyValue("Age", 14), KeyValue("Height", 145), KeyValue("BloodType", "O")]
    Yayoi,
    [KeyValue("Age", 13), KeyValue("Height", 158), KeyValue("BloodType", "B")]
    Ami,
    [KeyValue("Age", 13), KeyValue("Height", 158), KeyValue("BloodType", "B")]
    Mami,
}

//列挙型の拡張メソッドクラス
public static class IdolExtension {
    private static EnumInfo<Idol> EnumInfo { get; } = new EnumInfo<Idol>();
    public static int Age(this Idol idol) => EnumInfo[idol].GetValue<int>("Age");
    public static int Height(this Idol idol) => EnumInfo[idol].GetValue<int>("Height");
    public static string BloodType(this Idol idol) => EnumInfo[idol].GetValue<string>("BloodType");
}

//拡張メソッドの使用例
public void Sample() {
    int age = Idol.Chihaya.Age();
    int height = Idol.Chihaya.Height();
    string bloodType = Idol.Chihaya.BloodType();
}

上記のように、簡単に列挙型の拡張メソッド(Age,Height,BloodType)が出来上がりました。
静的なデータの場合はこの方法で拡張メソッドが定義できます。

しかし、動的なデータの場合は、今まで通り拡張メソッドを自力でゴリゴリ書かないといけないですね。属性でラムダ式が使えれば改善できると思うのですが。残念です。

波形発生器クラス

制御系ソフトでは外部からのAD入力値をロギングして演算や制御を行うことがよくあります。

社内での開発時はAD入力機器が無い場合が多いので、AD入力のデバッグ用に波形発生器クラスを社内ライブラリに実装しています。

このクラスを使用すると、指定した条件の経時変化波形を作成できます。今のところDC ・矩形波・サイン波・CSVファイルによる任意波形の4種類に対応しています。

あとは必要になったらパルス波・三角波・ノコギリ波・複数の波形発生器の合成波くらいを実装すればだいたいのことは試せるはず。

パソコンや設備の更新時期

先日、お客様から連絡があり、古いPC-98のパソコンが壊れて動かなくなったとのこと。

症状を聞きながら色々と試して貰うと、HDDは大丈夫なようで、別の使っていないPC-98にHDDだけ入れ替えてソフトが起動できるようになりました。

OSがWindowsではなくMS-DOSで、もう20年くらい前のソフトを今も使っているようです。

今回は使っていないパソコンが余ってたので良かったですが、またいつ壊れるか分からない危険な状態です。

特注の検査装置のソフトなので、簡単にパソコンを更新する訳にもいかないのは分かるのですが、問題が発生したときのリスクを考えると、数年単位に更新の検討をするべきだと思いました。

開発側も更新時期をお客様に提案し、危険を回避できるように動いた方が良さそうです。

WPF

今仕事でWPFを使用しています。

いつも仕事ではWindowsFormを使用しており、馴れているためWPFに移行出来ていませんでした。

実際使用してみると一長一短ではあるものの、今後はWPFでも良いかと思っています。理由は以下。

1.予想よりも移行が簡単である。もっとWindowsFormとのギャップがあるかと考えていたが、それほどでも無い。事前にXAMLやVMMVを理解していたからかもしれないが。

2. 予想よりもWindowsFormと共存ができる。いままで社内ライブラリで使っていた共通のWindowsFormがWPFのプロジェクトで使えたり、WindowsFormsHostコントロールを使用してWindowsFormコントロ-ルが使えたり。

3.自由度が高い。さすがWPFと思えるところ。今まで簡単にできなかったことができるようになる。逆に今まで簡単にできたことが多少面倒になっている部分もある。

4.UWPの布石として。 正直、WPFがWindowsFormに置き換わることは無く、その前にデスクトップアプリが無くなりUWPに移行すると思われる。UWPのUIもXAMLなのでWPFに馴れておいた方が良い。 

他社ソフト会社のソースコードについて

今までに他社ソフト会社が作成したソフトの改造依頼などで色々なソースコードを見てきましたが、まともなソースコードはほとんど見たことがありません。

制御系ソフトという業界は、どの会社もソフト開発スキルが低いです。電気やメカについても学習しないといけないため、相対的にソフト開発スキルが下がるのだと考えています。

手前味噌ではありますが、インフォテックは制御系のソフト会社では 他社と比べスキルが高く、業界ではかなり珍しいと思います。

そんな他社制御系ソフトについて、最近とても出来の良いソースコードと出会いました。事前に設計をしているというのが分かるクラス構造や、デザインパターンへの理解がある実装など。他社ソフトでこんなに出来の良いソースコードは初めてです。しかも、ソフト専門の会社ではなく、お客様社内のソフト開発部が作成されたソフトでした。

来週の現調で開発された方にお会いするのが楽しみです。

FileMappedList

インフォテックで作成するソフトでは、1日分などの長期間の時系列測定データを処理する場合などが良くあります。

さすがに全てのデータをRAMメモリ上に確保するのは現実的ではないので、 バイナリファイルにデータを保存し、必要なデータだけを読み込んで使用できるようにするのが良いのですか、データを使用する側の実装が複雑になるのは避けたいところ。

ですので、バイナリファイル内のデータをListのように扱えるFileMappedListクラスを作成しました。このクラスを使用すると、インデクサを使用して指定したインデックスの値だけをバイナリファイルから取得したり、指定したインデックス範囲について間引きを行って値を取得するなどが行えます。データの型はジェネリックで指定可能です。

200行程度の小さいクラスですが、使ってみた結果はとても良いです。

null条件演算子 (?.演算子)

VS2015で追加されたnull条件演算子(?.演算子)について、今日初めて使ったので覚え書き。

この演算子を使用すると、 Maybeモナドのようにオブジェクトがnullかどうかを気にせずに実装を行えるようになります。

例1:
var control = form?.ActiveControl;
フォームオブジェクトからアクティブなコントロールを取得します。
フォームオブジェクトがnullの場合、controlはnullになります。

例2:
var color = form?.ActiveControl?.BackColor;
フォームオブジェクトからアクティブなコントロールの背景色を取得します。
フォームオブジェクトやアクティブコントロールがnullの場合、colorはnullになります。
なお、null条件演算子を使用しているため、colorの型はNullable<Color>になります。

例3:
var color = form?.ActiveControl?.BackColor ?? Color.White;
フォームオブジェクトからアクティブなコントロールの背景色を取得します。
フォームオブジェクトやアクティブコントロールがnullの場合、colorはWhiteになります。
colorの型はColorになります。

例4:
form?.ActiveControl?.Refresh();
フォームオブジェクトのアクティブなコントロールのRefreshメソッドを実行します。
フォームオブジェクトやアクティブコントロールがnullの場合、Refresh()メソッドは実行されません。

残念なのはプロパティやインデクサへのSetには使用できないこと。(自分が使い方を分かっていないだけかも)以下のようなコードは書けません。
form?.ActiveControl?.BackColor = Color.Black;
(frm?.ActiveControl?.BackColor).Value = Color.Black;