技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

メインフレームのプリンターで使われる制御コードの概要

メインフレームのシステムでは、プリンターを用いて帳票やハガキを印刷することが多いです。
この際、プリンターに読み込ませるためのバイナリの制御コードを用いて、文字の形式や改行・改ページ等をコントロールします。
プリンターに読み込ませるファイルには制御コードが埋め込まれており、制御コードの意味がわかっていないとバイナリエディタやダンプ結果を通してファイルを見る際に困ることがあります。
(出力される帳票やハガキを見ることができれば良いのですが、印刷には費用や時間を要するので、手軽に見ることはできません)
 
制御コードで代表的なものは、「シフトアウト・シフトイン」と、「改行指示」です。
 
「シフトアウト・シフトイン」は、1バイト文字の箇所と2バイト文字の箇所を明示するための制御コードであり、2バイト文字が始まる直前にシフトアウト文字を入れ、2バイト文字が終わった直後にシフトイン文字を入れます。
(2バイト文字の前後にシフトアウト・シフトインを入れないと、その箇所が1バイト文字として扱われ、文字化けします)
以下は、文字コードにEBCDIK/KEISを使用する場合の例です。

f:id:akira2kun:20201212165445j:plain
 
「改行指示」は、その名の通り改行を示す制御コードです。
プリンターに読み込ませるファイルは基本的にバイナリなのでテキストエディタで開いても改行されませんが、改行指示の制御コードを探して、そのコードを通常用いられる改行コード(CRLF等)に置換することで、テキストエディタで開いた時になんとなく内容を読み取れるようになります。
 
その他、「改ページ指示」「文字の大きさ指定」「バーコード印字」等の制御コードが存在します。
どのような制御コードが存在するのか、制御コードとして具体的にどのようなコードを指定すれば良いのか、はプリンターごとに決まりがあり、その資料も基本的には社外に持ち出せないので、具体的なことは記事中に書くことができません。
しかし、そのようなコードが存在するということを知っているだけでもテストが容易になりますし、もし知る必要があれば現場の有識者に質問して教えてもらうと良いでしょう。

Vue.jsインストール手順とHelloWorld(Windows、2020年12月)

Vue.jsとはJavaScriptGUIフレームワークです。
MVVM(Model-View-ViewModel)を採用しており、画面(View)と内部状態(Model)を分離して画面の制約にとらわれないようにすることで、画面で入力された内容をリアルタイムに内部状態に反映させたり、内部状態の変化をリアルタイムに画面に出力したりすることができます。
 
今回は、WindowsOSのPCにインストールし、実行確認をする手順を掲載します。
実行確認の「Hello World」に関しては色々書き方がありますが、今回はMVVMの良さを実感できる書き方で「Hello World」を試します。
 
■前提
・OS
 Windows8.1
 
・実施日
 2020年12月30日
 
・Vue.jsのバージョン
 2.6.12
 
■インストール手順
1.Vue.jsの公式サイトにアクセスする。
 https://jp.vuejs.org/index.html
 
2.「はじめる」を左クリックする。

f:id:akira2kun:20201230202159j:plain


3.「インストール」を左クリックする。

f:id:akira2kun:20201230202223j:plain


4.「開発バージョン」を右クリックし「名前を付けてリンク先を保存」をクリック。
 ※FireFoxの場合。
  リンク先はjsファイルなので、ブラウザの機能でファイルとして保存する。

f:id:akira2kun:20201230202243j:plain


5.任意のフォルダにvue.jsを保存。
 ※今回は"C:\tmp"に保存。

f:id:akira2kun:20201230202307j:plain

 
■実行確認(Hello World)手順
1."vue.js"を保存したフォルダに、"hello.html"という名前のファイルを作成する。
 
2.「hello.html」に下記を入力して保存。

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - Hello world!</title>
</head>
<body>

<div id="app">
 <input type="checkbox" id="checkbox1" v-model="checked1">
 <label for="checkbox1">
  {{ message }}
 </label>
</div>

<script src="./vue.js"></script>
<script>
 var app = new Vue({
  // elで指定した値はdivタグのidと対応
  el: '#app',
  // 変数の初期値定義
  data: {
   message: '',
   checked1: false
  },
  // 変数の値を監視するイベントを定義
  watch: {
   checked1: function(newVal, oldVal) {
    this.message = (newVal) ? 'Hello World!' : '';
   }
  }
 })
</script>
</body>
</html>

 
3.「hello.html」をブラウザから開く。
 ※今回はChromeで開きます。
 
初期状態では以下のように何も表示されませんが

f:id:akira2kun:20201230202338j:plain

 

チェックボックスをチェックすると「Hello World!」と表示されます。

f:id:akira2kun:20201230202421j:plain


ボタンをクリックしてイベントを飛ばしたり、リロードしたりすることなく、画面で変更された内部状態をリアルタイムに画面上に反映することができています。

 

java:例外発生後に例外のメッセージを書き変える

例外オブジェクトにはメッセージが格納されており、getMessage()メソッドでそのメッセージを取得することができます。
しかし、このメッセージはコンストラクタでのみ設定可能であり、メッセージを後で変更するメソッドは用意されていないため、例外クラスに用意されている手段では例外発生後にメッセージを書き変えることはできません。
 
しかし、リフレクションを使用することで、メッセージを後で書き変えることが可能です。
メッセージはThrowableクラスのprivateのクラス変数"detailMessage"に保持されるため、これをリフレクションで書き変えます。
 
サンプルコードは以下の通りです。
 
【サンプルコード】
・ExceptionTest.java
import java.io.IOException;
import java.lang.reflect.Field;

public class ExceptionTest {

  public static void main(String[ ] args) {

    try {
      method();
    } catch (IOException e) {
      System.out.println(e.getMessage());
    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  static void method() throws Exception {

    try {

      // メッセージ付きで例外を発生させる
      throw new IOException("hoge");

    } catch (Exception e) {

      // detailMessageフィールドの定義を取得
      Field fieldDefinition =
        Throwable.class.getDeclaredField("detailMessage");

      // フィールドをアクセス可能に設定
      fieldDefinition.setAccessible(true);

      // 例外オブジェクトのメッセージを変更する
      fieldDefinition.set(e, e.getMessage() + "fuga");

      // 例外を再スロー
      throw e;

    }

  }

}
 
【実行結果】
・コンソール(標準出力)
hogefuga
 
・コンソール(標準エラー出力
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by ExceptionTest (file:/C:/pleiades/workspace/Hello/build/classes/) to field java.lang.Throwable.detailMessage
WARNING: Please consider reporting this to the maintainers of ExceptionTest
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
 
----
 
ちなみに、リフレクション時に発生してしまう標準エラー出力ですが、筆者の環境(Java10)では抑止する良い手段が見つかりませんでした。
以下の2つの手段を試しています。
 
1.JVM引数で抑止
"--illegal-access=deny"を指定したら逆に異常終了するようになってしまいました。
下記ページによると、Java11も不可、Java8は可(そもそもリフレクション時の標準エラー出力が出ない)だそうです。
 
Java - Javaの開発環境を作る過程でのエラー文について|teratail
https://teratail.com/questions/153595
 
2.標準エラー出力の出力ストリームを変更
通常、"System.setErr(自作PrintStreamオブジェクト);"を記述することで標準エラー出力の出力先を変更し、コンソールに出力されないようにすることができるのですが、今回のケースではそれができませんでした。
"System.err.close();"を記述した場合はコンソールへの出力を抑止することができたので、リフレクション時のWARNINGメッセージでは"System.err"を直接使用しているのではないかと思います。
なお、"System.err"はクローズしたらリオープンすることができないので、"System.err.close();"を実行してしまうと、以降は予期せぬ例外等が発生しても標準エラー出力として出力できなくなってしまいます。
("System.setErr(System.out);"で標準出力として出力することならできますが…)

ゴンペルツ曲線(信頼度成長曲線)とは

ゴンペルツ曲線(信頼度成長曲線)とは、テストで発見されるバグ数をグラフにしたものです。
横軸に時間、縦軸に累積バグ数をとる場合、下記のようなグラフになります。

f:id:akira2kun:20201219180016j:plain

 
テストを開始した直後は、テスト手順が確立していないため、バグはなかなか見つかりません。
テスト手順が確立した後は、時間が経つにつれて順調にバグが見つかります。
テストが終盤に近づくと、残っているテストケースはテスト困難なレアケースになるので、再びバグを見つけにくくなります。
 
具体的にいくつのバグが出るかは、プロジェクトの特性によって変化します。
現場毎に、プロジェクトの規模と発見されるバグ数の統計を取り、その統計を元にいくつのバグが出るのかを具体的な数値に落とし込みます。
 
もし、バグが大量に見つかる(aのケース)ようであれば、テスト開始時点の品質が悪い疑いがあります。
この場合、品質管理者は、バグの原因を確認し、前工程で見つけるべきバグが大量に見つかっていないかどうかを見る必要があります。
前工程で見つけるべきバグが大量に発見された場合は、バグの対応でテストの進捗が悪くなることを防ぐために、もう一度前工程に戻って品質強化を行う必要があります。
 
また、発見されるバグが少ない(bのケース)ようであれば、テストケースの作り込みが甘い疑いがあります。
この場合、品質管理者は、テストに使われているテストケースを確認し、テストの目的に照らし合わせてテストケースの網羅性が十分かを見る必要があります。
テストケースの網羅性が不十分な場合は、現在のテスト工程で見つけるべきバグが見つからないことにより次工程やリリース後に悪影響を及ぼすのを防ぐため、テストケースの修正を指示する必要があります。
 
品質管理者には成果物の一つ一つを細かく見る程の時間はないため、上記のような数値管理を行い、プロジェクトの状況を把握しようとします。
(人間の体に例えると、健康診断で数値を出し、疑わしい数値が出た場合に医師が検査を行う、という例えになります)
そのため、作業者としては、正直に数値を出すことで、品質管理者が適切に管理を行えるようにする必要があります。

java:引数で渡した参照型変数をメソッド内で変更する書き方について

参照型変数を引数としてメソッドに渡し、呼び出し先のメソッドの中でその参照型変数に変更を入れた場合、呼び出し元でもその変更内容を参照することができます。
ソースコードで言うと、以下のような形で呼び出し元に影響を与えることができます。
(isDisplay[0]の変更は呼び出し元にも反映されます。実行すると、"Hello World!"が出力され、"Error!"は出力されません。)
 
public class HelloWorld {
  public static void main(String[ ] args){

    boolean isDisplay = {false,false};

    getIsDisplay(isDisplay);

    if (isDisplay[0]) {
      System.out.println("Hello World!");
    }
    if (isDisplay[1]) {
      System.out.println("Error!");
    }

  }

  public static void getIsDisplay(boolean isDisplay) {
    isDisplay[0] = true;
  }
}
 
このような書き方は、C言語のレガシーなコードではよく見かけますが、javaではあまり見かけることがないと思います。
レガシーな現場に入ったことが無い方は、見慣れていない、違和感がある、と感じる方が多いと思います。
 
javaでは、以下のように戻り値により呼び出し元に影響を与える書き方の方が主流です。
 
public class HelloWorld {
  public static void main(String[ ] args){

    boolean[] isDisplay = {false,false};

    isDisplay[0] = getIsDisplay();

    if (isDisplay[0]) {
      System.out.println("Hello World!");
    }
    if (isDisplay[1]) {
      System.out.println("Error!");
    }

  }

  public static boolean getIsDisplay() {
    return true;
  }
}
 
ビジネスロジックを知り尽くしている場合は、前者のようにメソッドに参照型変数を渡してメソッドの中で好きに変更させる形にした方が楽にコーディングできます。
しかし、ビジネスロジックをよく知らない担当者がソースコードを調査したり改修したりする場合、後者のように戻り値を通して呼び出し元に影響を与える形にしないと、読む必要があるソースコードの範囲が広がり、調査・改修が困難になります。
今回の例で言うと、"isDisplay[1]"に変更が入っていないことを知るために、前者のコードではメソッドの中まで見る必要がありますが、後者のコードではメソッドの中まで見る必要はありません。
 
現在の開発現場では、複数の担当者が入れ替わり立ち替わりでソースコードを改修し続けるのが主流ですので、後者の書き方の方が望まれます。
現行のソースコードが既に前者の形で書かれているのであれば、それに合わせた方が良い場合もありますが、そうではないなら後者の書き方で書くべきでしょう。

Excel:ピボットテーブルの使い道と作り方(テストデータ付き)

ピボットテーブルとは、集計作業を行う機能のことです。
自力で集計用の表を作成して関数を書くことでも集計作業は可能ですが、ピボットテーブルを用いればマウス操作だけで簡単に集計作業を行うことができます。
 
今回は、試しにピボットテーブルで架空の体力測定結果を集計してみます。
握力について、男女で差があるのか、測定月で差があるのかを、平均値を出すことで確認します。
 
なお、使用するデータは以下からダウンロードできます。
手を動かしながら覚えたい方はぜひ。
https://1drv.ms/x/s!AivF3bzWXOzukHtXk5hscKYqkLkM
 
----
 
1.集計作業を行う表をドラッグで選択し、挿入>ピボットテーブルを選択する。

f:id:akira2kun:20201204213710j:plain

2.ドラッグした範囲が自動的に選択されているので、OKボタンを押下する。

f:id:akira2kun:20201204213734j:plain

 

3.ピボットテーブルのシートが生成されるので、列に測定月、行に性別、値に握力(kg)をドラッグして選択する。

f:id:akira2kun:20201204213749j:plain

 

4.「合計 / 握力(kg)」の右にある矢印を選択し、値フィールドの設定を選択する。

f:id:akira2kun:20201204213805j:plain

 

5.デフォルトでは合計が選択されているので、平均を選択し、OKボタンを押下する。

f:id:akira2kun:20201204213821j:plain

 

6.性別ごと、また測定月ごとで、握力(kg)の平均値が算出される。

f:id:akira2kun:20201204213842j:plain

 

 これで、男女では大きな差があるが、測定月では大きな差がないことが一目でわかるようになりました。

障害の発生原因の切り分けのポイント

テストや本番運用で障害が発生した場合、既知の障害等で原因が明らかな場合を除き、対応のために原因を調査する必要があります。
原因を調査する上ではどこに原因があるのかの切り分けが必要になります。
以下では、切り分け作業を行う上でのポイントを順を追って説明します。
 
1.障害が発生した状況を保全する
障害が発生したデータやその状況を記録したログは後の調査で使いますので、誤って更新したり削除したりしないように保全する必要があります。
テスト中であればそのデータを用いたテストは中断する必要がありますし、場合によってはコピーして別の場所に補完する必要があります。
 
2.期待される値との比較を行う
データやログを見て、設計上期待される値と異なる点を探していきます。
コーディングの単純な誤りであれば、大抵の場合は、データが期待値と異なるようになった箇所やログの出力内容から原因を特定できます。
場合によっては、その期待値自体が合っているのかどうか、設計や要件の確認・再検討が必要になる場合もあります。
 
3.障害発生手順を確立させる
ログの出力内容が不足している場合や原因が込み入っている場合は、データやログを見ただけでは原因がわからない場合があります。
その場合は、障害が発生した時と同じ手順で障害の再現を確認します。
障害が再現すれば、障害を発生させる手順が確立されたということになります。
厄介なのは障害が再現しなかった場合で、一意キー(顧客番号や受付番号の類)が障害発生時と異なることに注目して一意キーと結びつくデータを洗い出す、ランダムに処理が変わる箇所がないか(乱数を使っている箇所や振り分け先がランダムなロードバランサー等に注目する)、という観点で再現しなかった理由を調査し、障害発生手順を確立させます。
どうしても確立できない場合は、ハード障害である可能性を視野に、障害発生時の状況をまとめてハードウェアを提供するベンダーに確認する、というアクションを起こす必要がある場合もあります。
(私の経験上、「太陽フレアでビットが入れ替わってしまった」という冗談のような原因を告げられたこともあります)
 
4.再現手順を元に開発環境で原因調査を行う
再現手順が確立したら、開発環境で再現手順を試し、徐々に原因を絞り込んでいきます。
もし環境を変えたことで障害が再現しなくなった場合、環境問題である可能性が出てきます。
(例えば、マスタデータの不備、機器の構成の違いに起因する問題、等)
障害が再現する場合、開発環境ではデバッグを入れて変数の内容を表示することができますので、それをログの代わりにして原因調査を進めることができます。
また、開発環境ではデータも自由に書き変えられますので、データを少しずつ書き変えて挙動を確かめることでも、原因調査を進められます。