C#

C#で医療用検査機器とシリアル通信 – パート3

長々とすいません。これで終わります。

前回で試験オーダーの要求で、検体ID(バーコード)を受け取りましたので、今回はその検体IDからASTMプロトコルのデータを生成して医療機器に返してあげます。

必要となる検査データは被験者さんのカルテ情報などから引っ張ってくることになりますが、取得方法は環境によって変わるはずですのでここでは端折ります。

まずは検査内容を医療機器に返す、大まかな処理です。

private static void ReplyOrderData()
{
    List<string> sampleids = SampleIDList;

    // 要求された検体識別コードをリスト化する
    sampleids = sampleids.Distinct().ToList(); // 重複を削除

    // 返信メッセージ(試験オーダ情報)を取得する
    List<string> order_msgs = CreateOrderMsg(sampleids);

    // メッセージ送信処理
    SendMsg(order_msgs);

    // 保持したサンプルIDなどを初期化する
    SampleIDList = new List<string>();
    ResultReportState = null;

    // スレッド実行フラグ
    ThreadRun = false;

    return;
}

続いて、試験オーダーの内容を生成する処理です。
検査内容は通常でしたらデータベースから検体数分取ってきて、ループでデータを作り出すことになりますが、個々では

// 医療機器に返す試験オーダメッセージを生成する
public List<string> CreateOrderMsg(List<string> sampleids)
{
    List<string> rec = new List<string>();
    
    // ヘッダーレコードを追加
    rec.Add(this.HeaderRec());

    int p_no = 1;
    foreach (var sample_id in sampleids){
        // 患者レコードを追加
        rec.Add(this.PatientRec(sample_id, p_no)); 
        // 試験オーダレコードの追加(依頼する検査があるなら)
        rec.AddRange(this.OrderRec(sample_id));
        p_no++;
    }

    // 終了レコードを追加
    rec.Add(this.FooterRec());

    return rec;
}

private static string DlmtFld = "|";
private static string DlmtRpt = "\";
private static string DlmtCmp = "^";
private static string DlmtEsc = "&";
// ヘッダーレコードを返す
// H|\^&|||LISHost|||||Device_name||P|1
private string HeaderRec()
{
    string dt = DateTime.Now.ToString("yyyyMMddHHmmss");

    string[] h = new string[14];

    h[0] = "H";
    h[1] = DlmtRpt + DlmtCmp + DlmtEsc;
    h[2] = "";
    h[3] = "";
    h[4] = "LISHost";
    h[5] = "";
    h[6] = "";
    h[7] = "";
    h[8] = "";
    h[9] = "Device_name";
    h[10] = "";
    h[11] = "P";
    h[12] = "1";
    h[13] = dt;

    return string.Join(DlmtFld, h);
}

// 患者レコードを返す
// P|1|PatID01|||Meria^Anna||19741001|F|||||MARTINEZ
private string PatientRec(string sample_id, int no)
{
    string code = "sample_id" から被験者コードを入れて下さい。
    string name = "sample_id" から被験者の名前を入れて下さい。
    // 患者情報レコード
    string[] p = new string[14];

    p[0] = "P";
    p[1] = no;
    p[2] = code;
    p[3] = "";
    p[4] = "";
    p[5] = name;
    p[6] = "";
    p[7] = "";
    p[8] = "";
    p[9] = "F";
    p[10] = "";
    p[11] = "";
    p[12] = "";
    p[13] = "Hospital";
    
    return string.Join(DlmtFld, p);

}

// 試験オーダレコードを返す
/// O|1|SAMPLE01||^^^CT/GC|R|19980506090909|||||N||||Serum||||||||||O
/// O|2|SAMPLE01||^^^HPV|R|19980506090909|||||N||||Serum||||||||||O
private List<string> OrderRec(string sample_id)
{

    string dt = 検体の採取日などの"日付";

    List<string> rec = new List<string>();

    // 試験オーダレコード
    // 検査対象となるアッセイ名毎に配列を作り、その配列毎にレコードを作る
    int o_no = 1;
    Dictionary<string, string> assay_names = new Dictionary<string, string>();
    assay_names = "sample_id" から、その検体で行う検査数分の検査情報の配列を作って入れて下さい;
    foreach (KeyValuePair<string, string> name in assay_names)
    {
        string assay_name = name.Key;
        string analyte_name = name.Value;

        string[] o = new string[27];
        string o_4 = _DlmtCmp + _DlmtCmp + _DlmtCmp + assay_name;

        if (name.Value != null && name.Value.Length != 0)
        {
            o_4 += _DlmtCmp + analyte_name + _DlmtCmp + _DlmtCmp;
        }
        else
        {
            o_4 += _DlmtCmp + _DlmtCmp + _DlmtCmp;
        }
        o[0] = "O";
        o[1] = o_no.ToString();
        o[2] = item_code;
        o[3] = "";
        o[4] = o_4;
        o[5] = "R";
        o[6] = dt;
        o[7] = "";
        o[8] = "";
        o[9] = "";
        o[10] = "";
        o[11] = "N";
        o[12] = "";
        o[13] = "";
        o[14] = "";
        o[15] = ""; // 試験片の種類
        o[16] = "";
        o[17] = "";
        o[18] = "";
        o[19] = "";
        o[20] = "";
        o[21] = "";
        o[22] = "";
        o[23] = "";
        o[24] = "";
        o[25] = "";
        o[26] = "O";
        rec.Add(string.Join(DlmtFld, o));
        o_no++;
    }

    return rec;
}    
    
// 終了レコードを返す
/// L|1|N
private string FooterRec()
{
    string[] l = new string[3];

    l[0] = "L";
    l[1] = "1";
    l[2] = "N";

    return string.Join(DlmtFld, l);
}

最後に生成した検査内容データを医療機器へ送り込む処理です。

// 医療機器へメッセージを送信する
private static void SendMsg(List<string> msgs)
{
    WH = new AutoResetEvent(false); 
    
    List<string> conv_msgs = new List<string>();

    // フレーム番号を付与する
    // フレーム番号は1から始まり7で終了するが、
    // それ以降は0から始めて、7で終了の繰り返しとなる
    int frm_no = 1;
    foreach (string msg in msgs)
    {
        if (frm_no == 8)
        {
            frm_no = 0;
        }
        conv_msgs.Add(frm_no.ToString() + msg);
        frm_no++;
    }

    // メッセージの受信勧誘パラメータを送信
    if (ThreadRun == true)
    {
        Send(ENQ);
        // 検査機器からの応答(<ACK>の受信)を待つ
        WH.WaitOne();
    }

    // メッセージの全レコードにパラメータを付与して送信
    foreach (string msg in conv_msgs)
    {
        if (ThreadRun == true)
        {
            string frame = STX + msg + CR + ETX;
            string chksum = GetCheckSumValue(frame);
            string rec = frame + chksum + CR + LF;
            Send(rec);
            // 検査機器からの応答(<ACK>の受信)を待つ
            WH.WaitOne();
        }
    }

    // 伝送制御の終了パラメータを送信
    if (ThreadRun == true)
    {
        Send(EOT);
    }

    return;
}
    
// シリアル通信で送信
private static bool Send(string str)
{
    // シリアルポートが開いていなければ処理しない
    if (SP.IsOpen == false)
    {
        return false;
    }

    // 送信するデータが無ければ何もしない
    if(str == null || str.Length == 0)
    {
        return true;
    }

    try
    {
        // データ送信
        SP.Write(str);
        // NAKが帰ってきた時用に、送信したデータを保持しておく
        SendData = str;
    }
        catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        return false;
    }

    return true;
}

とりあえず以上です。

色々と省いており、ツッコミどころや抜けているところもありますので、動作の保証をするものではありません。
大まかな流れ的なものを紹介ということでご了承下さい。
実際には、通信エラー時の処理や、送信した内容をログに書き込んでおくなども必要になります。

冒頭で「想像力に頼るしかない」と言いましたが、検査機器が来るまではPC2台でお互いに通信し合う格好で「多分こんな感じだろう」というテストを行うのみでした。
一応、検査機器が納品され直接つないで動作確認した所、通信自体は概ね良好でしたのでホントにホッとしましたよ。

前々回でも述べましたが、こういった業務を日常にされている方でしたら何てこと無い作業でしょうね。

僕と同じようなフリーランスでこういったお仕事を受けることはあまりないでしょうから、必要にかられてこの情報を閲覧される方は少ないかと思います。
探し方が悪かったのか、ASTMプロトコルについての参考になる資料がネット上でそれほど出ていない雰囲気でしたので、僕のようにシリアル通信の処理を忘れてしまっ方や、ASTMプロトコルのやり取りを必要とされる方の一助になれば幸いです。