[KEKB Bunch Feedback Group]

EPICSを使ったAR温度モニターメモ (Japanese)


For more information, please contact with M. Tobiyama.



Contents

1. はじめに

2. システム構成

3. GP-IBデバイスサポート

4. デバイスサポートのコンパイル、登録

5. データベース作成

6. シーケンサー

7. IOCへのローディング

8. medmでの画面作成

9. まとめになっていないまとめ


1. はじめに

AR大電流実験は1996年12月2日をもって、大成功裏(???)に赫々たる大成果をあげ(???)終了しました。 この第3期には、多くのフィードバック用機器をリングに設置しましたが、未だ経験のない、かなり危ないデバイスを大バンチ電流、大平均電流環境下に無慈悲にさらすため、機器の、主にパワーの出入りに関する箇所(フィードスルー等)の温度をモニターする こととしました。 ローカルな温度モニターシステム自身は、測温抵抗体をハイブリッドレコーダーで 計るだけのもので、配線設置がとても面倒くさいことを除けば本質的な大きな問題はありません。 今回は欲を出して、ハイブリッドレコーダーをGP-IBから読んでやり、ネットワークでモニターできるEPICS温度モニターシステムを作ることとしました。 こうしておけば、コントロール室や居室など、ネットワークが届くところならどこでも温度監視が出来るだろうという目論見でした。

このメモで述べる作業は1996年10月ころに(既に期間もあやしい)行いました。 すぐにせめてメモでも残しておけば良かったのですが、なにかと取り紛れているうちに(というより、未だかつてなく殺人的に忙しかった)、月日は無情にもあれよあれよと 過ぎてゆき、記憶ももはや極めて怪しいこととなってしまいました。 しかしながらこのまま放っておくと遠からず完全に忘れてしまうのは明らかでありますので、せめてここで覚えていることだけでも(主に自分のために)メモに残すこととしました。

なお、私が作業した時点と今では状況はきっと変わっているに違いなく、記憶も極めて曖昧でありますから、このメモを信用するような向こう見ずな大それたことをしてはいけません。 必す専門の先生方に確認の上、必要な準備あるいは作業をしてください。 また、EPICSの基礎とか、準備とか、入門とか、出家入道とかについては、特に必要なもの以外は一切ふれません。 この部分について、知識あるいは助けが必要な方は、コントロールグループの方々、各グループリンクパーソン殿あるいは経験者にお聞きください。

目次に戻る


2. システム構成

温度センサーの配置については、ここでは重要でないので、省略します。 なお、測温抵抗体の場合、熱電対と違い補償導線が絡み合うことでの地獄をみることはないですが、一カ所に多くのセンサーを配置する場合、やはり取り合いはとても大変であることには違いはありません。ハイブリッドレコーダーへの配線も60本×3本ともなると、とても大変でした。

目次に戻る


3. GP-IBデバイスサポート

GP-IB機器をEPICSで使おうという大それたことをする場合、このデバイスサポートは各自が用意しなければなりません(実は他のcamacやvmeではどうなのかは知らずにこんなことを書いている)。 このデバイスサポートは、昔は呪われたもろCで書いていたらしく不評だったそうですが、私はGP-IB device Decsription Language(GDL)で作ることになりました。 このGDLの仕様については関東情報サービスの吉田奨氏のレポートを参照してください。 確かに楽にはなってはいますが、やはりかなりのCの覚悟及び技術が必要です。

GP-IBコマンド

MH2000はすべての操作をGP-IBから行うことが出来ますが、面倒くさいし、設定をリモートで変えなければならない理由がないので、唯一、データを読み込む部分のみ作ることにしました。 ただし、何らかの都合で機械がエラーを返したときは正しく処理する必要があります。また、SRQを発行することも出来ますが、これもプログラムが面倒くさくなるし、さしたるメリットがないので使用しません。

データ収集の要求コマンドは幾種かありますが、ここではgroup番号で指定された10チャンネル分のデータを応答させるコマンドを使います。

外部→MH2000

11,チャンネルグループ番号,デリミタ

ここでチャンネルグループ番号は

01: CH 01から10まで
02: CH 11から20まで
03: CH 21から30まで
実はチャンネルは60までありますが、31から60は演算結果を入れるためのもので、今回は使用しません。MH2000の応答は、エラーがないときは

MH2000→外部

11,チャンネルグループ番号, データ1,データ2,..., データ10,デリミタ

となります。データは1チャンネルあたり、下記の12バイトで構成されています。

A1 A2 A3 A4 S D1 D2 D3 D4 D5 D6 D7

A1〜A4は警報発生情報をレベル毎(4レベル)示すもので、
0 : 未発生
1 : 上限警報
2 : 下限警報
3 : 変化率上限警報
4 : 変化率下限警報
5 : 差上限警報
6 : 差下限警報

Sはデータの状態を示し、
0 : 正常
1 : 断線(burn out)
2 : 入力オーバーフロー
3 : 入力アンダーフロー
4 : データーオーバーフロー
5 : データーアンダーフロー
9 : 無効データ

D1〜D7で、データを7桁で右詰に示します。
データが上の様な、尋常でない形をしているので、読み込んだデーターから温度部分を取り出す必要があります。
STATIC float get_onetemp(char wa1[], int *s)
{
#define E_TEMP  -1000.0

  *s = (wa1[4]-'0');
  if (*s != 0) return E_TEMP;
	else return atof(wa1+5);
}

STATIC int rd_temp(char wav[], float temp[], int s[])
{
  int i;
  char wa1[255];
#define NAK 0x15  

  if (wav[0]!=NAK) 
    {
      strtok(wav,",");
      strtok(NULL,",");
      for (i=0; i<10; i++)
	{
	  strcpy(wa1,strtok(NULL,","));
	  temp[i] = get_onetemp(wa1,&s[i]);
	}
      return 0;
    }
  else
    {
      s[0] = atoi(wav+1);
      return 1;
    }
}
function rd_tempで読み込んだデータ(全部で10ポイントのデータを含んでいる)を、カンマ毎に分け、get_onetempでヘッダをチェックし、正しいデータなら温度部分を取り出します。何らかのエラーがあったら、-1000度に設定します。

パラメータテーブルには、GP-IBコマンドを記述します。

ParamTable{
	read_temp1	{rec=wf,  command="11,01,\r\n",
			leng=255, conv=rd_wf_temp}
	read_temp2	{rec=wf,  command="11,02,\r\n",
		        leng=255, conv=rd_wf_temp}
	read_temp3      {rec=wf,  command="11,03,\r\n",
			leng=255, conv=rd_wf_temp}
}
各チャンネルグループ毎にコマンドを書きます(デリミタも)。 EPICSレコードタイプのうち、使えるのはwaveformとなりますが、この場合は必ずCファンクションで変換規則ファンクションを記述する必要があります。 変換は
STATIC int rd_wf_temp(struct gpibDpvt *pdpvt,  int p1, int p2, char **p3)
{
  struct waveformRecord * pwf = (struct waveformRecord *) (pdpvt->precord);
  char	*craw;
  float	*temparray;
  float temp[255],*ptemp;
  int	s[255],ok_flag;        
  unsigned long	numElem;

  temparray = (float *) pwf->bptr;
  craw = pdpvt->msg; 
  craw = strtok(craw,"\n\r");
/*  printf("input data: %s\n",craw);  */
  if (craw == NULL) /* fairly unlikely ... */
    {
      devGpibLib_setPvSevr(pwf, READ_ALARM, INVALID_ALARM);
      return(ERROR);
    }
  ok_flag = rd_temp(craw,temp,s);
  if (ok_flag != 0) 
    {
      devGpibLib_setPvSevr(pwf,READ_ALARM, INVALID_ALARM);
      return(ERROR);
    }
  
  numElem = 10;
  if (numElem > pwf->nelm)      numElem = pwf->nelm;
  pwf->nord = numElem;
  ptemp=temp;	
  while (numElem--)
    {
    /*  printf( "\n%d-th data: %f",numElem, *ptemp); */
      *temparray++ = (float) *ptemp++;
    }
  /*  printf( "\nread_wf end normally\n"); */
  return(OK);
}
のようなコードになります。なにを隠そう、実はとびやまもこの詳細を完全に理解している訳ではなく、サンプルファイルをもとに作っただけです。 機器から送り返されたデータは、msgフィールドに入っているので、それをcrawに入れ、処理をします。結果はbptrに入れて返します。型はfloat型になっていますが、これはデータベース側と一致させる必要があります。 一応エラー処理をしているように見えますが、エラーが起きたことがないので、これでいいのか分かりません。全体はファイル名

devMH2000Gpib.gt

という名前でセーブしておきます。なお、このソースが気に入らない方は、こっそりとびやままでお知らせください。

目次に戻る


4. デバイスサポートのコンパイル、登録

  1. /cont/epics/R312/epics/extensions/src/kekb/gdl/ディレクトリからgdlMakefile.includeファイルをもって来ます。
  2. makefileを編集して、include applMakefile.includeの次の行にinclude gdlMakefile.includeを入れます。
  3. devMH2000Gpib.gtがコンパイル出来るように、コンパイル定義を入れます。
    #   Can link multiple objects:
    #test: test1.o test2.o
    #       $(LOAD) -o test test1.o test2.o
    devMH2000Gpib.o:$(SRC)/devMH2000Gpib.gt
    	$(MAKE_GPIB)
    
    $(MAKE_GPIB)のまえの空白は、タブでないといけないらしい。
  4. gmake devMH2000Gpib.oでれっつコンパイル。なお、Makefileの全文がみたい奇特な人はここをクリックしてください。
  5. 奇跡的にエラーが出なかったら、objfrc40ディレクトリにオブジェクトファイルと一緒にデバイス名_command.listというファイルが出来ます。この場合はMH2000_command.listで、中身は
    /*** COMMAND LIST ***/
    
    No.  0   WF      read_temp1
    No.  1   WF      read_temp2
    No.  2   WF      read_temp3
    
    となる。みんなWFなので単純明快ですね(おおうそ)。
  6. GPIBデバイスサポートの登録を行う。
    • cat_ascii/devSup.asciiファイルに次のように登録する。
      "waveform"      ET488_IO        "devWfMH2000Gpib"       "MH2000"
      
    • 1つ目はレコードタイプ、この場合はwaveform
    • 2つ目はリンクタイプ、NIのGP-IB cardだとGPIB_IO、今回はET-488だからET488_IO
    • 3つ目はデバイスサポート名。自分でつけた名前の前に、dev+レコードタイプが付き、後ろにデバイスタイプ(Gpib)がつく。
    • 4つ目はモジュール名。
  7. ファイルを保存して、makesdrコマンドを実行する。

目次に戻る


5. データベース作成

データベース作成及び保守はDCTは論外として、XDCTを使用しても、capfastを使用してもどちらも一短一短でとても苦労しました。 別段構造的に複雑怪奇だという高尚な理由ではなく、このエディター類が 千早ぶる神代の昔の構造か、一般ユーザーのことを考えずに作ってあるからだと思います。 しかしデータベースを作らない限り、先には進めないので、我慢して作るしかありません。 ここでは、リンクを使う関係上、capfast(/cont/capfast_new/wcs/bin/xschedit)を使うことにします。 DCTでファイルを作っていてもcapfastには読み込めず無駄になるので、注意します。 capfastの出力は、convertというコマンド(在処は吉田さんに聞いてくだされ)で変換する必要があります。この出力はDCTでいじれるが、capfastには反映されないので注意します。capfast自体の使用法はここではふれません(というかすっかり忘れた)。

capfastでみたデータベース全体図を覗く

GP-IBデバイスサポートが使うレコードはwaveform型だけだが、これを1点ずつ表示するためには、ai型が必要です。 このwaveformからai型への変換は次に述べるシーケンサーを使って行いますが、シーケンサーを起動するタイミングを作るため、calcも使います。

使うレコードタイプ

これをしこしこと作ります。一括変更とかいった、常識的な機能が備わっていると期待しては決していけません。また、XDCTの場合、とても親切なことにフィールドの選択肢を示してくれますが、capfastの場合はすべて手で打ち込まなければなりません。よく分からない場合や値を忘れた時は、仕方ないからXDCTで値を調べることですな。

完成したら、fb_temp_arという名前でsaveし、convertでデータベースに変換します。 私が作業したときは、capfastもconvertもsad2の上でしか動きませんでした。今はどうなっているか知りません。

目次に戻る


6. シーケンサー

なぜかは知りませんが、今のところwaveformの配列内の値を別々に取り出したり、表示したりするのは困難です(というか、出来ません)。そこで、waveformに値が入り次第その値をaiに移し替えるシーケンサーを作りました(これのどこがシーケンサーなんだろう)。シーケンサーの文法はCによく似ているという俗説もありますが、決してCそのものではなく、落とし穴が口をあけて待っております。

はじめに、使う変数を宣言します。

 int i1;
 float TEMP_01[10];
 float CTEMP_01[10];
 int   CMON_01;
 int   CMONP_01 = 0;
 ..まだまだつづく...
次に、変数をデータベースのレコードにassignします。
 assign TEMP_01 to "fb_temp_ar:temp01";
 assign CTEMP_01 to {"fb_temp_ar:t00","fb_temp_ar:t01",
                     "fb_temp_ar:t02","fb_temp_ar:t03",
                     "fb_temp_ar:t04","fb_temp_ar:t05",
                     "fb_temp_ar:t06","fb_temp_ar:t07",
                     "fb_temp_ar:t08","fb_temp_ar:t09"};
 assign CMON_01 to "fb_temp_ar:tempm01";
 ..まだまだつづく...
次に、モニター(変化を監視する)を宣言します。
 monitor CMON_01;
 monitor CMON_02;
 monitor CMON_03;
 monitor CMON_04;
 monitor CMON_05;
 monitor CMON_06;
この後、シーケンサー動作を記述します。なお、ssは並列で動作しますが、ss内のstateは上から順に動作していくので(シーケンサーだから)、上が動かないから動かないという間抜けなことにならないよう注意します。今回は、すべて並列動作ということになります(だからシーケンサーではない)。
ss state_main1
{
        state sta1
        {
                when(CMON_01!=CMONP_01)
                {
                %{
                printf("state 1 on\n");
                }%
                pvGet(TEMP_01);
                for (i1=0; i1<10; i1++)
                {

                 CTEMP_01[i1] = TEMP_01[i1];
                 pvPut(CTEMP_01[i1]);
                }
                CMONP_01 = CMON_01;

                }state sta1
        }
}
 ..まだまだつづく..
CMON_01の値が変わったら(=waveformに新しい値が入った)、変換を行うプログラムになっています。 データのinportおよびexportにpvGetとpvPutを使っていることに注意してください。全体のソースを見たいひとはこちらをバシバシたたいて下さい。

完成したシーケンサーをコンパイルするため、Makefileを変更し、makeします。

目次に戻る


7. IOCへのローディング

startup.frc40を編集します。 全体を見たい人はここをくりっくして下さい。 ここからは、割り当てられたvme計算機に入って、所定の設定を行い(ここもすっかり忘れてしまいました。ごめんなさい)、リブートすれば運がよければいつかは動くこともあるでしょう。動作をモニターするコマンドもいくつかありますが、私は使っていないので、聞いても無駄です。

目次に戻る


8. medmでの画面作成

ここまで動けば、もう出来たも同然よとうそぶいてもよいでしょう。あとは見てくれだけで、medm(Motif based Editor and Dispaly Manager)を起動して、画面上に部品を配置して、それにリソースの値(データベースで決めた名前)を指定すれば出来上がりです。いままでの労苦に比べて、なんと楽しい作業でしょう。 なお、こいつも今時のグラフィックスエディターと考えては決していけません。結構まぬけで、腹が立つこともあるでしょうが、ひろ−い心でつきあえば、いつかはきっと改善される日も来るでしょう。 なお、medmが作成するのはディスプレイリストというテキストファイルなので、どうしてもいやなら使い慣れたeditorで作ることも不可能ではないですが、面倒くさいのでやめといた方がいいでしょう。修正はeditorでやった方が結局早くてきれいに出来ることが多いと思います。完成したら、medmを実行モードにして実行します。

作成したdisplayを見たい人はここをクリック

目次に戻る


9. まとめになっていないまとめ

ここまで、主に作業手順に従って紹介してきました。実際には、この後メニューでディスプレイ項目を選べるようにしたりしましたが、これはほぼmedmでの作業で、単純なので割愛します。今回はachiverは一切使用しなかったので、データの記録に関しては私は全く無知です。 前にも述べたように、既にいろいろなところが変わっている可能性が大きいので、実際に新たにこのようなことをしようという奇特な方は、まずコントロールグループに相談されることを強くおすすめします。

いまとなっては、それほど「うなる」作業であったかは、もはや辛い記憶も定かではないのでなんともいえませんが(大体、地獄の大電流実験の苦しみさえも忘れかけている)、特に手間がかかった部分を述べると、

  1. EPICSの構造自身の理解。
  2. デバイスサポートのdebug。私は、たんぼたすかる、Delphiぷろぐらまのつもりですが、Cは至って不調法で、多大なご迷惑を各方面にかけてしまいました。
  3. WaveformのDTYPフィールド設定。はじめ間違った設定をしていたので、いきなり 動かず、大騒ぎしてしまいました。
といったところでしょうか。アラームの設定とかは、あとからしようと思っていたのですが、データベースを更新する手間がいやで(一括更新がないので、ほとんど作り直すくらいの気力が必要)、結局出来なかった(しなかった)です。

作業にあたりましては、多くの方の多大なご支援をいただきました。デバイスサポートの変換プログラムに関しては、RFの赤坂展昌さんに相談にのっていただきました。 作業の最初にコントロールグループの駒田一孝さんに相談にのっていただきました。 コントロールグループの山本昇さんにははじめから最後までいろいろ教えていただき、またお手数をおかけしました。関東情報サービスの吉田奨さんにはつきっきりでいろいろ教えていただきました。これらの方々の暖かいご支援がなければ、決して実用的には動かなかったと思われます。深く感謝いたします。

Makoto Tobiyama
22/Jan/97

Return to FB Home Page...