SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Curlで構築する長崎電子県庁システム(ポータル・スケジューラー編)(AD)

Curlで構築する電子県庁システム(ポータル・スケジューラー編)
RSSリーダーとメッセンジャー

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード サンプルソース (2.6 KB)

 今回は、長崎県庁グループウェアポータルサイトのトップ画面にあるRSSリーダーと、スケジューラーに実装したメッセージャーについて書いていきます。RSSリーダーは、RSS情報を非同期で取得して、記事を表示する際は文字をスクロールさせて表示しています。メッセンジャーは、CurlのUDPソケットの機能を使用して実装しています。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 今回は、ポータルサイトのトップ画面にあるRSSリーダーと、スケジューラーに実装したメッセージャーについて書いていきます。RSSリーダーは、RSS情報を非同期で取得して、記事を表示する際は文字をスクロールさせて表示しています。メッセンジャーは、CurlのUDPソケットの機能を使用して実装しています。

これまでの記事

RSSリーダー

 RSSリーダーには、主に以下の機能があります。

  • ヘッドラインをティッカー表示
  • ヘッドラインの一覧表示
  • ヘッドラインをクリックすると目的の記事をブラウザを起動して表示
  • フィードの登録・削除、記事の更新間隔・文字のスクロール速度の設定
ヘッドラインをティッカー表示
ヘッドラインをティッカー表示
ヘッドラインの一覧表示
ヘッドラインの一覧表示
フィードの登録・削除
フィードの登録・削除
記事の更新間隔・文字のスクロール速度の設定
記事の更新間隔・文字のスクロール速度の設定

 ポータルサイトを起動すると、RSS情報を一定時間ごとに自動的にダウンロードして、記事をティッカー表示するようにしました。RSS情報をダウンロードする際は、非同期で取得するようにして、ダウンロード中に画面が固まらないようにしています。

非同期でRSS情報を取得する
{define-proc public {my-async-read-open
                      p_src: Url,
                      p_after-access-proc: {proc-type {#TextInputStream}: void},
                      error-proc: #{proc-type {Exception}: void} = null
                    }: void
  let v_file: HttpFile =
        {p_src.instantiate-File} asa HttpFile
  {with-file-caching-style FileCachingStyle.resynchronize do
    let v_async-file-opener: AsyncFileOpener =
          {v_file.async-read-open
            {on t: AsyncFileOpenEvent do
              let v_stream: #TextInputStream = null
              {try
                {if-non-null t.exception then
                  {if-non-null error-proc then
                    {error-proc t.exception asa Exception}
                  }
                else
                  {if v_async-file-opener.done? and
                      not t.canceled? then
                    set v_stream = t.stream asa TextInputStream
                  }
                  {p_after-access-proc v_stream}
                }
              finally
                {if v_stream != null and
                    v_stream.open? then
                  {v_stream.close}
                }
              }
            }
          }
  }
}

||RSS情報を非同期で取得する
{my-async-read-open
  {url "http://rss.rssad.jp/rss/codezine/new/20/index.xml"},
  {proc {p_in: #TextInputStream}: void
    ||取得完了したときに呼ばれる
    {if-non-null p_in then
      let (v_buf: StringBuf,
           v_num: int) = {p_in.read-one-string}
              :
            省 略
              :
    }
  },
  error-proc =
    {proc {e: Exception}: void
      ||エラーが発生したときに呼ばれる
    }
}

RSSリーダーの表示

 RSSリーダーで情報を表示する部分は部品化して作成しました。文字列のスクロールスピード(普通/速い/遅い)、スクロール種類(文字列が見えなくなるまでスクロール/右端にきたらスクロールを止める/すべて表示されたらスクロールを止める)、次の文字列を表示するまでの間隔を指定できるようにしました。コントロールのソースコードを簡単に説明します。コントロールのソースコードはダウンロードして実行できます。

文字列をティッカー表示するコントロール
文字列をティッカー表示するコントロール

 コントロールはグラフィカルな子を1つ格納するコンテナの基本クラスBaseFrameクラスを継承して作成しました。

BaseFrameクラスを継承
{define-class public MyScrollMessageFrame {inherits BaseFrame}
              :
            省 略
              :
}

 コンストラクタは、「①スクロールスピード」「②スクロール種類」「③次の文字列を表示するまでの間隔」「④表示を繰り返すか?」「⑤画面に表示されたら自動的にスクロールを再開するか?」を引数で受け取るようにしました。

コンストラクタ
{constructor public {default
                     scroll-speed: int  = MyScrollMessageFrame.NORMAL, ||①スクロールスピード
                     type: int          = MyScrollMessageFrame.SCROLL, ||②スクロール種類
                     delay: int         = 20,         ||③次の文字列を表示するまでの間隔
                     repeat?: bool      = false,      ||④表示を繰り返すか?
                     auto-resume?: bool = false, ...} ||⑤画面に表示されたら自動的にスクロールを再開するか?
  {construct-super ...}
            :
          省 略
            :
}

 文字列のティッカー表示はタイマーを使用しています。タイマーはTimerクラスを使用するば簡単に実現できます。

Timerクラスを使用
self._timer         =
    {Timer
        enabled? = false,
        interval = 100ms,
        delay = 0ms,
        {on t: TimerEvent do
            ||タイムアウト
            {if-non-null self.layout
             then
                {self.on-timeout-timer t}
            }
        }
    }

 文字列を描画している部分です。drawメソッドは描画が必要になったときに自動的に呼ばれるメソッドです。drawメソッドをオーバーライドして現在のスクロール位置に文字列を直接描画しています。

drawメソッドで文字列の描画
{method public {draw r2d: Renderer2d}: void
  {with-render-properties
      font          = {Font
                          self.font-family,
                          {self.any-to-distance
                              self.font-size,
                              {self.get-display-context}
                          },
                          weight = self.font-weight,
                          style  = self.font-style
                      },
      fill-pattern  = self.color,
      translation-x = self._message-xpos,
      translation-y = {r2d.get-font-ascent}
      on r2d do
      {r2d.render-string
          0pt,
          0pt,
          self._message
      }
  }
}

 コントロールを使用している部分です。コントロールを生成してappendメソッドで表示する文字列を登録します。startメソッドでティッカー表示が始まります。

コントロールの使用方法
{let v_frame: MyScrollMessageFrame =
    {MyScrollMessageFrame width = 500pt, height = 15pt,
        background = "silver", font-size = 12pt,
        type = MyScrollMessageFrame.STOP-IN-MESSAGE-END,
        repeat? = false, auto-resume? = true}
}

{do
    ||表示する文字列をセット
    {v_frame.append "Windows PowerShell 活用編(3) ファイル操作 3"}
    {v_frame.append "「会社の成長」と「自身の成長」を実感できるカカクコム"}
    {v_frame.append "VB.NETで入力エラーチェック機能を持ったフォームを作成する"}
    {v_frame.append "PR: 迷惑メール対策なら信頼のマトリックススキャンAPEX"}
    ||表示の開始
    {v_frame.start}
}

メッセンジャー

 メッセンジャーには、主に以下の機能があります。

  • スケジューラーを起動している者同士で、簡単なメッセージのやり取りをリアルタイムに行える。
  • 相手が不在の時にメッセージをデータベースに残すことができる。不在の人がスケジューラーを起動した際に、メッセージがあることを通知して後からメッセージを見ることができる。
メッセージの送信画面
メッセージの送信画面
メッセージを受信したときの画面
メッセージを受信したときの画面

UDPソケットの生成

 UDPで送受信を行うときは、UDPパケットの送受信のためのクラスであるUDPSocketを使います。引数で使用するポート番号、タイムアウト時間、データを受信した時に呼ばれるイベントハンドラを指定します。bindメソッドを実行すると送受信できる状態になります。

let v_socket: UDPSocket=
      {UDPSocket
        local-port       = 5000,
        timeout          = Socket.zero-timeout,
        readable-handler =
          {on t: ReadableStreamEvent do
            ||データを受信したときに呼ばれる
            {recv-data-proc}
          }
      }
{v_socket.bind}

UDPで文字列を送信

 文字列を送信するには、UDPSocketクラスのwrite-packetメソッドを使用します。write-packetメソッドで送信できるデータ形式はバイト配列です。よって、送信する前に文字列をencode-charactersプロシージャでバイト配列に変換しています。

{define-proc {my-udp-send-string? p_socket: #UDPSocket,
                                  p_ip: String,
                                  p_port: uint16,
                                  p_data: String
             }: bool
  {if p_socket == null or
      not p_socket.open? then
    {return false}
  }
  let v_enc: CharEncoding =
        {get-character-encoding-by-name "shift-jis"},
      v_outsize: int =
        p_data.size * v_enc.transcode-max-expansion-factor,
      v_out: ByteVec =
        {ByteVec max-size = v_outsize},
      (v_used: int,
       v_outmade: int) =
        {encode-characters p_data, v_out, v_enc},
      v_temp: {Array-of byte} =
        {new {Array-of byte}}
  {for v_byte: byte in v_out do
    {v_temp.append v_byte}
  }
  {return
    {p_socket.write-packet
      v_temp,
      remote-address = {SocketInetAddress p_ip},
      remote-port    = p_port
    }
  }
}

||192.168.1.123に"あいうえお"を送信する
{my-udp-send-string?
  v_socket,
  "192.168.1.123",
  5000,
  "あいうえ"
}

UDPで送信された文字列の受信

 データを受信するときは、UDPSocketクラスを生成するときのreadable-handlerに指定したイベントハンドラで行います。 UDPSocketクラスのread-packetメソッドで、受信したデータを取得します。バイト配列で取得するので、decode- charactersプロシージャを使ってバイト配列から文字列に変換しています。

{define-proc {recv-data-proc}: void
  {try
    let (v_out: #{Array-of byte},
         v_length: int,
         v_remote-address: #SocketInetAddress,
         v_remote-port: uint16) =
          {v_socket.read-packet}
    {if-non-null v_out then
      let v_temp: ByteVec = {ByteVec max-size = v_out.size}
      {for v_byte: byte in v_out do
        {v_temp.append v_byte}
      }
      let (v_num-bytes-decoded: int,
           v_recv: String) =
            {decode-characters
              v_temp,
              {get-character-encoding-by-name "shift-jis"}
            }
              :
            省 略
              :
    }
  catch e: ConnectionAbortedSocketException do
  }
}

 メッセンジャーの機能を応用して、グループメンバのスケジューラーの起動の有無を名前の前のアイコンで分かるようにしました。スケジューラーを起動すると、グループメンバのスケジューラーとメッセージの送受信を開始します。応答がある/なしによってアイコンの切り替えを行っています。

スケジューラーの起動の有無によって名前の前に表示するアイコンの切り替え
スケジューラーの起動の有無によって名前の前に表示するアイコンの切り替え

まとめ

 Curlで、デスクトップアプリケーションのようなRSSリーダー・メッセンジャーが簡単に作成できることがお分かりいただけたと思います。

 3回にわたり、Curlで「ポータル・スケジューラー」を開発するにあたり苦労した点、工夫した点などを書いてきました。本連載で少しでもCurlの良さを伝えることができたならば幸いです。ありがとうございました。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加

【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3470 2009/02/18 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング