« 2011年1月 | トップページ | 2011年3月 »

2011年2月

2011年2月20日 (日)

年齢ソート

Agesort

TableRowSorter に Comparator を設定して,年齢・生年月日で正しくソートできるようにした。

plugin/PatientSearchImpl.java

private void initComponents() {
 ・
 ・
  TableRowSorter sorter = new TableRowSorter(tableModel) {
    // ASCENDING -> DESENDING -> 初期状態 と切り替える
    @Override
    public void toggleSortOrder(int column) {
      if(column >= 0 && column < getModelWrapper().getColumnCount() && isSortable(column)) {
         List keys = new ArrayList(getSortKeys());
         if(!keys.isEmpty()) {
           SortKey sortKey = keys.get(0);
           if(sortKey.getColumn() == column && sortKey.getSortOrder() == SortOrder.DESCENDING) {
             setSortKeys(null);
             return;
           }
         }
      }
      super.toggleSortOrder(column);
    }
  };
  // 生年月日コラムに comparator を設定「32.10 歳(S60-01-01)」というのをソートできるようにする
  sorter.setComparator(AGE_COLUMN, new Comparator(){
    @Override
    public int compare(Object o1, Object o2) {
      String birthday1;
      String birthday2;
      if (ageDisplay) {
         birthday1 = ModelUtils.getMmlBirthdayFromAge((String)o1);
         birthday2 = ModelUtils.getMmlBirthdayFromAge((String)o2);
         return birthday2.compareTo(birthday1);
      } else {
         birthday1 = (String)o1;
         birthday2 = (String)o2;
         return birthday1.compareTo(birthday2);
      }
    }
  });
  table.setRowSorter(sorter);

util/ModelUtils.java

/**
 * 年齢付きの生年月日「32.10 歳 (S50-01-01)」形式から mmlBirthday を返す
 * @param birthdayWithAge
 * @return
 */
public static String getMmlBirthdayFromAge(String birthdayWithAge) {
 String[] s = birthdayWithAge.split("[()]");
 return toSeireki(s[1]);
}

2011年2月18日 (金)

初回実施月日の自動入力

初回実施月日が未設定の場合,今日の日付を入れるようにした。

client/KartePane.java

public void stamp(final ModuleModel stamp) {
  if (stamp != null) {
//pns^   text stamp がここに入った時の対策(新規カルテにテキストスタンプ挿入するときここに来る)
  ・
  ・
    // 「月 日」の自動挿入
    StampModifier.modify(stamp);
//pns$

client/StampModifier.java

package open.dolphin.client;

import java.util.Calendar;
import open.dolphin.infomodel.ClaimBundle;
import open.dolphin.infomodel.ClaimItem;
import open.dolphin.infomodel.IInfoModel;
import open.dolphin.infomodel.ModuleModel;

/**
 * drop された stamp を加工する
 * @author pns
 */
public class StampModifier {

  public static void modify(ModuleModel stamp) {

    // 「初回実施 月 日」の日付自動入力

    if (IInfoModel.ENTITY_TREATMENT.equals(stamp.getModuleInfo().getEntity())) {
      ClaimBundle bundle = (ClaimBundle) stamp.getModel();
      ClaimItem[] items = bundle.getClaimItem();
      for (ClaimItem c : items) {
        // 初回実施で number が入力されていない場合,今日の日付を入れる
        if ("840000085".equals(c.getCode()) && "1".equals(c.getNumber())) {
          Calendar calendar = Calendar.getInstance();
          c.setNumber(String.format("%02d-%02d", calendar.get(Calendar.MONTH)+1, calendar.get(Calendar.DATE)));
          break;
        }
      }

    }
  }
}

2011年2月15日 (火)

シェーマの jpeg保存 vs png保存

KarteEditor で,シェーマを jpeg 保存した場合と png 保存した場合を比較してみた。jpeg は結構ノイズが入ってしまっており,画質は明らかに png が上だが,その分容量が 1.6倍になっている。ただ,容量が大きくなっているとはいってもせいぜい 15K bytes 程度なので,画質を優先して png 保存することにした。

 1回保存5回保存byte[].length
jpeg

Schemajpeg1

Schemajpeg5

  8,969 bytes
png

Schemapng1

Schemapng5

 14,948 bytes

2011年2月14日 (月)

最近3ヶ月の来院歴表示

カレンダーの左上にボタンを付けて,最近3ヶ月の来院歴が表示されるようにした。

Calendar3

 

Calendar2

client/CalendarCardPanel.java

2011年2月 7日 (月)

com.sun.image.codec の置き換え

com.sun.image.codec は今後使えなくなるかもしれないので,ImageIO で置き換えた方がいいらしい。

警告:com.sun.image.codec.jpeg.JPEGCodec は Sun が所有する API であり、今後のリリースで削除される可能性があります。
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(writer);

client/KarteEditor.java

/**
 * Courtesy of Junzo SATO
 * ImageIO by pns
 */
private byte[] getJPEGByte(Image image) {
  byte[] ret = null;
// BufferedOutputStream writer = null;
  try {
    JPanel myPanel = getUI();
    Dimension d = new Dimension(image.getWidth(myPanel), image.getHeight(myPanel));
    BufferedImage bf = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB);
    Graphics g = bf.getGraphics();
    g.setColor(Color.white);
    g.drawImage(image, 0, 0, d.width, d.height, myPanel);

    ByteArrayOutputStream bo = new ByteArrayOutputStream();
//  writer = new BufferedOutputStream(bo);
//  JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(writer);
//  encoder.encode(bf);
    ImageIO.write(bf, "jpeg", bo);
      
//  writer.flush();
//  writer.close();
    ret = bo.toByteArray();
  } catch (IOException e) {
    e.printStackTrace();
//  if (writer != null) {
//    try {
//      writer.close();
//    } catch (IOException e2) {
//      System.out.println("KarteEditor.java: "+ e2);
//    }
//  }
  }
  return ret;
}

"jpeg" を "png" に変えると,png 形式で保存されるようになる。保存した画像の読み出しは byte[] を ImageIcon に入れているだけなので(DocumentDelegater#getDocuments 参照),java 側で jpeg か png か判断してくれる。

2011年2月 3日 (木)

ORCL0030CheckServer

OpenDolphin で CLAIM 送信した後,送った CLAIM の処理が終了したことを知らせるサーバを ruby で作った。CLAIM 処理が完了すると,ORCA サーバのシステムログに

Feb 24 12:22:22 orca panda/dbstub[3720]: module ORCL0030: /var/tmp/claim_rcv_0

というログが出るのを検出して知らせるといういかにも素人くさい方法。CLAIM 処理終了が確認できるので,CLAIM 処理が終わる前にORCAの「中途終了」ボタンを押してしまって,「中途終了データはありません」と言われなくてすむようになる。

ORCA サーバでの立ち上げ方法。11792 は適当な空きポート。

$ sudo tail -f -n0 /var/log/syslog | ruby ORCL0030CheckServer.rb 11792

ORCL0030CheckServer.rb

#!/usr/bin/ruby
require "socket"
require "timeout"

default_port = "11792"
if ARGV.length == 0
  port = default_port
else
  port = ARGV.shift
end

ACK = 0x06.chr

# receive ACK from client
class GetAck
  def initialize(s)
    @sock = s
  end
  def get
    begin
      timeout(1) {
        while ACK != @sock.read(1); end
      }
      return true
    rescue Timeout::Error
    end
  end
end

# send ORCL0030 to client
class SendOrcl
  def initialize(s)
    @sock = s
  end
  def send
    ack = GetAck.new(@sock)
    max = 5
    str = "ORCL0030"
    for i in 1..max do
      begin
        @sock.puts(str)
        break if ack.get
      rescue Errno::EPIPE
        $stdout.puts("retry: " + i.to_s)
        sleep 1
      end
    end
    return i < max
  end
end

# クライアントと接続したスレッドを保持する配列
thr = []   

# stdin を監視するスレッド
Thread.start() {
  while line = $stdin.gets
    line.chop!
    if /ORCL0030.*claim_rcv/ =~ line
      for t in thr
        t.wakeup
      end
    end
  end
}

gsock = TCPServer.open(port)

# クライアントと接続したスレッドを作り thr に登録
while true
  $stdout.puts("Wait for another connection")
  Thread.start(gsock.accept) do |sock|
    addr = sock.peeraddr
    $stdout.puts("Connected " + addr.join(":"))
    thr.push(Thread.current)

    # クライアントと接続したら指示があるまで待つ
    while true
      Thread.stop
      break if !SendOrcl.new(sock).send
    end
    sock.close
    $stdout.puts("Close socket")
    thr.delete(Thread.current)
  end
end

ORCL0030Listener

java で書いたクライアント。(NetBeans 6.9.1 のプロジェクト)
ダウンロード ORCL0030.zip (1475.0K)

Orcl00302

Orcl0030

起動画面 ORCL0030 を受けると,うさぎアイコンが跳ねる

2011年2月 1日 (火)

3年間の運用まとめ

当院では,開業当初から OpenDolphin,ORCA というオープンソースソフトウェアを使用し,紙カルテなしで診療を続け,この2月で丸3年を迎えた。
 OpenDolphin については,勤務医時代に OpenDolphin 1.0.1 から研究を開始(このバージョンは結局動かせなかった)。その後,バージョン 1.2.3 になって初めて自宅マシンで稼働に成功。ORCA で hospnum 導入に伴って OpenDolphin が動かなくなったが,それを自力で動くように修正できたので,開業後の自己導入・自己運用に自信をもった。その後バージョン 1.3 になったころに諸般の事情で開業を決意,OpenDolphin 1.3.0.1 + ORCA 3.4.0 で開業した。
 プログラミングは学生時代からの趣味なので,趣味に実益をもたらしてくれたオープンソースソフトウェアに感謝している。当院でのカスタマイズが本家に取り入れられているものも2〜3あるようなので,オープンソースコミュニティーにも少し貢献できているのかもしれない。

  • データベースの PatientModel の件数
    dolphin=# select count(*) from d_patient ;
     count 
    -------
      8360
    (1 row)
    
  • データベースの ModuleModel の件数
    dolphin=# select count(*) from d_module ;
     count  
    --------
     308833
    (1 row)
    
  • Dolphinサーバの df (ディスク余りまくり)
    Filesystem            サイズ  使用  残り 使用% マウント位置
    /dev/sda1              56G   12G   41G  23% /
    varrun                385M   52K  385M   1% /var/run
    varlock               385M     0  385M   0% /var/lock
    udev                  385M  8.0K  385M   1% /dev
    devshm                385M     0  385M   0% /dev/shm
    lrm                   385M   19M  366M   5% /lib/modules/2.6.24-28-xen/volatile
    
  • ORCA サーバの df (もっと余りまくり)
    Filesystem            サイズ  使用  残り 使用% マウント位置
    /dev/sda1              56G  5.4G   48G  11% /
    varrun                769M   84K  769M   1% /var/run
    varlock               769M     0  769M   0% /var/lock
    udev                  769M  8.0K  769M   1% /dev
    devshm                769M     0  769M   0% /dev/shm
    lrm                   769M   19M  750M   3% /lib/modules/2.6.24-28-xen/volatile
    
  • データベースの dump ファイルのサイズ
    Backup/dolphin_db.dump.gz.gpg  467,090,874  
    Backup/orca_db.dump.gz.gpg  31,902,253  
    
  • 作成したスタンプ数
    $ grep -c stampInfo stamp.xml 
    1805
    

トラブル記録

  • 1回サーバのメモリが壊れたが,診療を止めることなく対応できた。
  • サーバのディスプレイが壊れた。
  • iMac の挙動が怪しくなって新しいのを買った。(新しいのが欲しかっただけという噂も)

幸いなことに,診療が止まるようなトラブルは3年間で1度もなかった。カルテの読み出し速度など,レスポンスの悪化も感じない。JBoss AS,Postgresql は個人使用のこの程度のデータ量なら,余裕ありまくりなのだろう。

« 2011年1月 | トップページ | 2011年3月 »