2015年5月20日水曜日

WPFのRichTextBoxで文字数を制限する

※ 疑最適解の謎ポストですお許しを WPFでアプリケーションを作っていて、トレースログをUI上に表示する機能が必要なことが結構あります。ほとんどの人はそんなことないと思いますが、私にはあります。
そしてトレースログなのでERRORとかWARNINGとかINFOとか表示するので、漢の私は赤とかオレンジとかボールドとか使って味気ないログを華やかに彩りたいと思うのです。
するとUIにはTextBoxコントロールではなくRichTextBoxコントロールを使わなければいけないのですが、ここで問題が。
そんなに過去のトレースログをご丁寧に画面上のコントロールに持っておくのも精神衛生上よろしくないので文字数に制限をかけたいと考えるのですが、WPFのRichTextBoxにはないんですね、MaxLengthプロパティが。
ですが DeleteTextInRun というメソッドがあるようなので、これを使えば打ち止めの文字数を超えたN文字を先頭から削除するのはちょちょいのちょいだよね・・・

MyRichTextBox.Document.ContentStart.DeleteTextInRun(N);

働きませんな。
何が間違っているのでしょうか、私はダメな人間でしょうか。
そこでGoogle先生に DeleteTextInRun doesn't work と質問したところ海外のQuiitaことStackoverflowやMSDNでも似たようなことでお困りの方が何人かおられましたが、私の求める最適解にはたどり着けませんでした。
しかたないので参考になる文献をベースに修正してTextChangedイベントに実装した結果

private void MyRichTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    // MaxTextLengthは制限をかけたい文字数です
    TextRange range = new TextRange(MyRichTextBox.Document.ContentStart, MyRichTextBox.Document.ContentEnd);     if (range.Text.Length > MaxTextLength)     {         TextSelection selection = MyRichTextBox.Selection;         TextPointer contentStart = MyRichTextBox.Document.ContentStart;         TextPointer selectionStart = contentStart.GetPositionAtOffset(0);         TextPointer selectionEnd = contentStart.GetPositionAtOffset(range.Text.Length - MaxTextLength);         selection.Select(selectionStart, selectionEnd);         selection.Text = "";     } }

期待する動きは実現しましたがかなり冗長な気がするんです。
いい方法をご存知の方、居たら教えてください。

完

2015年4月26日日曜日

一日を1.5倍に使う

現在の自身の起床時刻は午前4時です。
これは、自宅から職場まで遠いためこのような時刻に起床するというサイクルになっているわけですが、それとは別に理由がもう2つあります。
1つ目は時差通勤です。本格的なラッシュアワーより少し早めの時間帯に通勤時間を設定するのはストレスを軽減する効果が思ったより大きいですし、何よりゆとりが生まれるので自分自身を殺伐とした雰囲気から遠ざけることが可能です。
2つ目は朝の時間帯に余裕を持たせることにより、ゆっくりとニュースに目を通したり朝食をしっかり食べて、2杯目のコーヒーのゆとりさえ生み出します。

以上のことを踏まえて、皆さんにもぜひとも早起きの効果を体現して欲しいと思っています。あるいは、もしかしたら職場や学校まで徒歩圏内なのだから、そんな面倒なことはわざわざしたくないと考える方も多いと思いますが、そういう方々にこそ生活サイクルの見直しを考えてみては?といたるところでお薦めしています。
たとえば、恒常的に午前5時に目覚めるリズムを作り出しておくと、転勤や引っ越しなどで環境が大きく変わったときなどに有利ですし、身体のリズムの大きく損なうことなく環境の変化に溶け込んでいくのは意外と難しいものなので、大きくマージンをとったリズムを普段から自分自身に律しておくとストレスフリーのベクトルを生み出すことが可能です。何より一日を長く使えることの可能性と有益さに気付くことが出来ます。
その代わりに夜は早めに就寝しましょう。

2015年4月20日月曜日

Keysight EasyEXPERTのCommand Executionで使用する外部アプリケーションをC#で作る

今回も計測アプリケーションエンジニア向け、しかも半導体のアナライザと、とってもニッチなHow Toモノです。99.97%の人は恐らくスルーでしょうね。
では行きましょう。

Keysight EasyEXPERTで独自の測定シーケンスを作りたいとき、スクリプトをBuilt-in Functionだけで構成できればいいですが、機能がそれほど多くないので、Widows Command Executionという機能を駆使して独自シーケンスを作成することになります。

このWidows Command Executionという機能、外部アプリケーション(以降「外部アプリ」とします)に機能を委託することができるということなのですが、EasyEXPERTのヘルプやサンプルを探すこと数時間、この機能 は3rdパーティーのプラグインかな?ってくらいに説明がありません。サンプルも。

スタックしたのはテスト定義画面のパラメータと、アプリケーションのINPUT/OUTPUTの関係がどうなっているのかさっぱりわからなかった点です。
以降、数日の格闘の記録を冒険の書としてここに記します。

テストの定義


まずはEasyEXPERTをスタートして、起動したら新規テストを定義するためのテスト定義画面(本当はなんと言うのでしょう)を表示します。
続いてテスト定義画面のTest Contentsタブ、続いてMiscellaneousタブを開きます。

テスト定義画面

テスト定義画面の左側、スクリプト・ビュー(赤い線の矩形の部分です。本当はなんと言うのでしょうか)のBLOCK~END BLOCK間にCommand文を挿入するため、Command Executionアイコンをポイントした状態でInsertボタンをクリックします。
ここまでの操作でテスト定義画面が上記の画面表示になっていればOKです。

変数の宣言


次のステップとして、今回のサンプルで必要な変数4つをテスト定義画面で宣言します。
スクリプトビューのLocal Variables Definitionをクリックして変数の宣言ペインを表示し、上部のLocal Variables Definition statementの下にあるAdd xxxx Variableボタンで変数の宣言を追加します。
宣言する変数は次の通りです。

return_code (Numeric)

外部アプリが返す実行結果を格納します。

num_var (Numeric)

外部アプリとの間で数値を送受する際に使用します。

str_var (String)

外部アプリとの間で文字列を送受する際に使用します。

num_array (Vector)

外部アプリとの間で配列データを送受する際に使用します。
※ 0から9の数列を初期値として設定しています

変数の宣言(定義?)画面

Command関数のパラメータ


ここではCommand関数のパラメータについて説明します。
まずは上段のWindows Command Executionにあるパラメータ4つです。

Command Filename

実行する外部アプリのファイル名、今回はあとでビルドするサンプル・アプリケーションのパスを設定します。

Argument

コマンドライン引数、外部アプリのMain関数の引数に、文字列配列で入ってくるアレです。EasyEXPERT側から変数の値を送る場合は、Bult-in Functionを使って
string(<VARNAME>)
とする必要があります。また文字列と変数値の組み合わせを送ることも可能です。
例として、--hold_time=<変数num_varの値> を外部アプリに渡したいばあい
string("--hold_time="+string(num_var))
となり、なかなかにトリッキーです。

Write Type

外部アプリに送るパラメータの型を指定します。
Stringを指定したばあい、テキストボックスに入力した文字列はそのまま文字列として外部アプリに渡ります。
変数の値を外部アプリへ送るには、string(<VARNAME>)  とテキストボックスに記述します。
文字列と変数の値を組み合わせて外部アプリへ渡すことも可能です(Argumentsの例参照)。
StringまたはListで設定したパラメータは変数の型にかかわらず、常にパラメータ値をhひとつの文字列として結合し、外部アプリの標準入力の入力データになります。
スクリプトが複数の文字列あるいはListを単一の文字列に結合する際、変数もしくは要素の区切りにCRLFを設定します。 このため外部アプリでは、受け取った文字列をCRLFで分割することで、元のデータに分離することが可能です。

Read Type

外部アプリから実行結果を受け取るパラメータの型を指定します。Read TypeにStringもしくはValueを指定したばあい、外部アプリから受けた文字列は、ひとつのデータとして解釈します。
Read TypeにListを指定したばあい、外部アプリから受け取った文字列はCRLFまたはカンマで要素に分割します。
 続いて中段のWrite ・・・の部分と下段のRead ・・・の部分ですが、その前に、ほとんど間違い探しの2つの画像をご覧ください。

上がWrite Type/Read TypeともにString型を指定した時の画面、下がWrite Type/Read TypeともにList型を指定した時の画面です。

Write Type/Read TypeともにString型を指定

Write String

外部アプリに送りたい文字列を指定します。
パラメータを画像のとおりに設定したばあい、外部アプリは標準入力で、
aaaaa<CRLF>bbbb<CRLF>--number=<num_barの値><EOF>
という文字列を受け取ることになります。
仮に外部アプリに20個以上のパラメータを設定するばあいは、前述のArgumentを使用すれば対応できます。
ただしこのとき高確率でカオスになります。

Read String

ResultとString、2つのテキストボックスがあります。
Resultは、外部アプリ終了時の終了ステータス(外部アプリのMain関数の戻り値)を受け取ります。
Stringは、外部アプリの標準出力で出力したデータを受け取ります。
Write Type/Read TypeともにList型を指定

Write List

外部アプリに送りたいVector型の変数を指定します。
num_arrayには、0から9までの数値が入っていることを前提に、パラメータを画像のとおり設定したばあい、外部アプリは標準入力で、
0<CRLF>1<CRLF>2<CRLF>3<CRLF>4<CRLF>5<CRLF>6<CRLF>7<CRLF>8<CRLF>9<CRLF><EOF>
という文字列を受け取ることになります。

Read List

Read Listにも、Read Stringのときとおなじく2つのテキストボックスがあります。
ResultはRead Stringのときと同様、外部アプリ終了時の終了ステータスを受け取ります。
Valuesは、Command関数が実行した外部アプリが標準出力で出力した文字列をカンマ、TABもしくはCRLFで分割し、配列にした結果を保持します。

外部アプリの作成


では実際にスクリプトのWindows Command Executionで実行する外部アプリ(コンソールアプリケーション)を作成して行きます。
開発環境には、 Visual Studio Express 2013 for Windows Desktop を使用します。
Visual Studioをスタートし、起動したらメニューの ファイル > 新しいプロジェクト でダイアログを開き、Visual C# > コンソールアプリケーション を選択します。
名前テキストボックスに任意のアプリケーション名を付け(ここでは「WinComExec」としています)、OKボタンをクリックしてプロジェクトを生成します。





エラー発生時とデバッグのときに変数値をMessageBoxで確認するため、アセンブリSystem.Windows.Formsをプロジェクトの参照設定で追加しています。

今回ビルドするアプリケーションのProgram.csは以下のとおりです。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;     // MessageBoxを使用します。参照設定にも追加してください。

namespace WinComExec
{
    class Program
    {
        /// <summary>
        /// コマンドラインパラメータが "--help(-h)" を含んでいるばあいに表示するヘルプメッセージを定義します
        /// </summary>
        const string help = "";
        /// <summary>
        /// コマンドラインパラメータが "--debug(-d)" を含むばあい値をtrueに設定します
        /// </summary>
        static bool _debug = false;
        /// <summary>
        /// 機能を実装します
        /// </summary>
        /// <param name="args">--help(-h)または--debug(―d)が有効です</param>
        /// <returns>Read TypeにNone以外を指定したばあい、Resultで設定したパラメータに値を設定します</returns>
        static int Main(string[] args)
        {
            int stat = 0;
            try
            {
                // コマンドラインパラメータを解析します
                foreach (string arg in args)
                {
                    string a = arg.Trim().ToLower();
                    switch (a)
                    {
                        case "--help":
                        case "-h":
                            Console.Out.Write(help);
                            return 0;
                        case "--debug":
                        case "-d":
                            _debug = true;
                            break;
                        default:
                            throw new ArgumentException("無効なコマンドラインパラメータです\n" + arg);
                    }
                }
                // --helpオプションの指定がなく、このあとのReadToEndで何も入力がないと、アプリケーションは入力
                // 待ちのまま何もしません。 別スレッドを起動し、一定時間入力が無いばあいにあプログラムを強制終了
                // するようにします。
                System.Threading.Thread timeoutProc = new System.Threading.Thread(ReadToEndTimeoutProc);
                timeoutProc.Start();
                // 標準入力からのデータ入力完了を待ちます
                string src = Console.In.ReadToEnd();
                // 標準入力からのデータ入力が完了したのでスレッドを終了します
                timeoutProc.Abort();
                // (SAMPLE) srcをセパレータで分割、文字列配列化して、要素を逆順に並び替えたうえでCSVにします。
                string[] inputs = src.Split(new string[] { ",", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                string result = string.Join(",", inputs.Reverse());
                // 実行結果を標準出力に出力します
                Console.Out.Write(result);
                // コマンドラインパラメータで--debug(―d)の指定があるばあい、インプットとアウトプットをファイルに
                // 出力したうでメッセージボックスに表示します
                if (_debug)
                {
                    string logMessage = string.Format("IN:\n{0}\n\nOUT:\n{1}", src, result);
                    System.IO.File.WriteAllText("C:\\source.txt", logMessage);
                    MessageBox.Show(logMessage);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, typeof(Program).Namespace, MessageBoxButtons.OK, MessageBoxIcon.Error);
                stat = -1;
            }
            return stat;
        }
        /// <summary>
        /// ReadToEndの入力タイムアウト処理を実装します
        /// </summary>
        /// <param name="obj"></param>
        private static void ReadToEndTimeoutProc(object obj)
        {
            // 5秒間で入力が完了しなければメッセージボックスでエラーを表示します。
            System.Threading.Thread.Sleep(5000);
            MessageBox.Show("文字列の読み込みがタイムアウトしました。\nヘルプを表示するにはコマンドラインオプションで --help または -h を指定してください。",
                typeof(Program).Namespace, MessageBoxButtons.OK, MessageBoxIcon.Error);
            Environment.Exit(258);  // =WAIT_TIMEOUT;
        }
    }
}

主要なロジックについて解説していきます。カッコ内の数字は行番号です。

Main関数の戻り値 (26)

新規プロジェクトの追加ウィザードで生成したプロジェクトのMain関数の戻り値は、デフォルトではvoidですが、スクリプトでは実行結果を取得することが出来、かつ実行結果に応じて処理を変えることが可能になるので、intにします。 

コマンドラインパラメータの解析 (31-47)

コマンドラインパラメータには、デバッグモードでの起動スイッチとなる --debug およびヘルプを表示するための --help を指定できるようにしています。 

Console.In.ReadToEndの入力待ちタイムアウト処理・スレッドの起動 (51-52)

単純なスレッドを起動します。82行目以降がスレッドの本体です。 

標準入力 (54)

アプリケーションは、テスト定義のWrite StringまたはWrite Listで設定したパラメータをここで読み込みます。
テスト定義のWrite TypeでListを選択するか、Stringで複数のデータを送るばあい、スクリプトはデータとデータのセパレータとしてCRLFを挿入した一つの文字列としてアプリケーションの標準入力に書き込みます。 

入力待ちタイムアウト処理/スレッドの中止 (56)

標準入力に入力があったばあい、タイムアウト処理スレッドをAbortします。 

標準出力 (61)

実行結果を標準出力で出力します。
テスト定義でRead TypeをStringにしたばあい、外部アプリが出力した結果データは、スクリプトが文字列としてそのまま変数に代入します。
同じくRead TypeをValueにしたばあいは、スクリプトが数値に変換して変数に代入します。
またRead TypeをListにしたばあい、スクリプトがセパレータ(カンマ、TAB、LF、CRLF)で分割して、Vectorの各要素に値を設定します。

デバッグのコツ


デバッグはコマンドラインパラメータに --debug を設定した時だけ実施するロジックを埋め込んでおき、インプットとアウトプットが期待する動きになっているかをモニタするのがベストプラクティスです。
アプリケーションの規模が大きくなるばあいは、ログファイルの併用も有効です。

その他個人的な感想としては、テスト定義画面のDebugにあるInspect機能はやや癖があるので、必要な時だけに使用を限定したほうが無難でしょう。
さらにはEasyEXPERTのビルトイン関数を組み合わせてなんとか実装できるような複雑な処理であれば、新たに外部アプリを作ってしまったほうが結果的に早く実装ができると思います。
これは先ほどのInspect機能の使い難さからくるデバッグ工数の増加と、ビルトイン関数の機能を確認するためサンプルを作って動作確認してみたものの、期待していた結果にならないことがあるためです。
C#やVB.net、C++などでのプログラミングに抵抗がないなら、迷わず外部アプリでの実装を検討しましょう。


ということで、今回も役に立つのか立たないのか微妙なTipsでした。

2015年4月19日日曜日

ルールの普遍性

新しいプロジェクトが発足する度にコーディングスタンダード、実装方針、例外のハンドリング方針、アーキテクチャレベルでのガイドライン、メッセージやロギングのポリシーなどを策定します。場合によっては国際化/ローカリゼーションをにらんだカルチャレベルでの表現の規約なども決める場合もあるでしょう。
このときにいつも考えるのは、ルールと自由の境界における戦いです。この境界線をチーム編成、規模、あるいは合理性とうまくバランスさせるポイントを探す作業が課題の中心になるわけですが、これはひどく困難な作業でもあります。
そして、毎回思うのは人間の習性。
人は一般に自由を求めており、本質的に束縛を求めます。このことは本来は矛盾を生み出します。
この相反する事象の正体はいったい何なのでしょう?
一言で説明を付けるのは難しいですが、関連する概念を探すのは出来るかもしれません。それらは協調性、市民社会、志、才能というキーワードで表現が出来そうにも思えます。先に上げた言葉の順列は一般に後者に進むにしたがって自由度が高まり、前者であればあるほど束縛やルールの普遍性に近づきつつあります。つまり、これらはひとつのモデルで地続きに存在するのではないか、という推測が出来そうです。

では、ルールの本質とはいったい何なのでしょうか?
もしかしたら、これは必要悪という表現を当てはめることが可能なのかもしれません。

2015年4月12日日曜日

品質と作業品質

と書いたはいいですが、あまりにも広大過ぎます。
では。もう一段分解してみようと思います。

・作業品質
・コミュニケーションの品質

他にもいろいろあるはずですが、ここではコミュニケーションの品質にフォーカスして考えたいと思います。
言葉や表現手段は時代によってどんどん変化します。
表現手段というのは、つまり言葉の選択の仕方や、あるいは、言い回しの枠組みの方法や背景、加えて、コミュニティの文化に強く依存する形式。前者はおそらくセンスが強く作用し、反面、後者はもっと記号的な表し方につながると思います。これらはまったく別のベクトルを持った定義であり、もしかしたら表現という範疇に収まるものではないのかもしれません。実際、そのように捉えるのが自然だと思えます。
これはひとつの妥協です。
2つのベクトルを妥協して捉え、私たちの仕事としての妥協すべきではないポイントをあぶりだそうと思うのが、今回の狙いです。

私たちの仕事には正確性が必要です。それは当たり前のことですし、あるいは、どんな仕事にも共通していえるのが、言い回しの正確さです。

たとえば、りんご がここにあります。
これを正しく表現しましょう。と言えば、おそらく誰しも 「りんごがあります。」と表現すると思います。では、ここにりんごが2個あるとします。どのように答えるでしょうか? 「りんごが2つあります。」
少し冗長であるように思えます。しかし、一応正しく、かつ、認識の祖語が生まれにくい表現に思えます。
では、もし、りんごが6個あった場合はどのように表しますか?
「りんごが6個あります。」
方法としては、さきほどの例と比べて考え方に変化はなく、冗長表現であることに変わりはありません、しかし正確さは保っています。
気を利かした人が「りんごが半ダースあります。」と表した場合はどうでしょうか?
正しい表現でしょうか?認識の祖語が生まれにくい構造と工夫の跡を感じられるでしょうか?
重要なポイントとしては、単位という概念が持ち込まれています。つまり、集合と要素という観点に気付いた点は評価できますが、単位という背景は果たして、発信者と受信者の間で一致した表現の見解として共有されているのでしょうか?
これは、日本語の弱点のように思えます。私たちの言語は合理性よりも情緒の方角に優れているため、まれに、と言うより、頻繁にこの危険性に出くわします。
したがって、事象や物体の表し方には気を使うべきだと思うのです。

今度は、私たちの世界にわかりやすく表してみましょう。
複数のりんごがあります。
List<りんご>
集合と要素の関係を表しています。
では、りんごに個性を与えたときは、どのように変化するでしょうか?

案外、日本語は工夫のし甲斐がある興味深い言語だと思います。

2015年4月11日土曜日

三単現の”s”は必要か

プログラムを書いていると必ずコメントの記述が必要になります。
このコメントの入力、今でも日本語で書くことは少なくないのですが、意識高い系PGアピールのため英語で書く頻度も上がってきています。

プログラマが短いコメントを書くとき、動詞から記述して主語をお察しください的に省略することがままあるではないかと思います。オブジェクト指向言語全般の、メソッドが動詞から始まる習慣とも一致するのでなおさらです。
中学英語で教わった主語を文の最初に置くなどどうでも良く動詞ファーストです。

動詞ファーストなので暗黙の主語は盲目的に三人称単数現在形です(私のばあいですが)。
そうなると動詞には、中学英語で教わった三単現の 's' が必要ということになります。
これにならって普段動詞でコメントを書き始めるとき、私は三単現のsをつけるようにしています。
ですが先日、コメント文中に動詞が複数回登場することがあって、いずれの主語も省略して三単現のsを連発してみたところ、見た目にしつこく感じることがありました。クリス松村氏を初めてみたときの感覚に似ています。
英語圏のプログラマが書いたコードのコメントを見ても、三単現のsが付いていたり付いていなかったり、そもそもメソッド名にsetだgetだcreateだと三単現のsなんて目にしたことも付けたこともないわけで、

「三単現のsって盲腸みたいなものかい?」

ティーチャーGに聞いてみました。
「三単現 s 」と入力をしたところ「廃止」のサジェスチョンがありこれはと思いクリックしました。
以下の検索結果が目に飛び込んできたため、米国でも三単現のsの存在が疑問視されているのかと記事を読みはじめました。

三単現の“s”20年めどに廃止 米・教育局


新聞社の記者の取材能力(想像力かもしれません)に驚きました。

最後に、今後コメントで動詞から書く際に三単現のsをつけるかどうかですが、気分で付けたりつけなかったりすることにします。

完




2015年4月8日水曜日

維新の党とソフト会社の共通点

私はその日の朝の情報を、SmartNewsと文化放送の福井謙二グッモニから仕入れています(以前使っていたiPhone4がradiko専用端末として余生を送っています)。


今朝(4月7日朝のことですが)のコメンテーターはジャーナリストの藤吉雅春氏でしたが、維新の党で起きた上西小百合議員の不祥事について話をしていました。
維新の党は規模の拡大を急ごうとしたあまり、党としてふさわしくない人材を採用してしまい、結果的に綻びが出てきてしまっているようだ。
人選ということでは政党も一般企業も、我々ソフト会社も皆同じですよね。
規模の拡大を急ぎ目先の人数に囚われれば、目標や目的を共有していないメンバーとともに仕事をすることになり、ちいさな会社を営む側にしてみれば、たったひとりの人選のミスが廃業につながることにもなりかねません。

急いては事を仕損じる

私たちはどんどん考えを表に出して、共有してくれる同志(お客様、従業員、社外取締役、単なる協力者等々)を急がず、焦らず、地道に募っていこうと思います。