LBPプリンタの状態を取得する

大雑把に作成したコントローラーとモデルとビューを紹介してみる。

controllers/lpr/host_controller.rb

class Lpr::HostController < ApplicationController
  verify :redirect_to => { :action => :index }
  
  def listall
    @page_title = "全てのプリンタ"
    @hosts = Lpr::Lprhost.paginate(:page=>params[:page], :per_page=>25,
      :order=>"id, back_day IS NULL")
  end
  
  def destroy
    Lpr::Lprhost.find(params[:id]).destroy
    flash[:notice] = "削除しますた"
    redirect_to :action => 'index'
  end
  
  def update
    @page_title = "プリンタの更新"
    @host = Lpr::Lprhost.find(params[:id])
    if @host.update_attributes(params[:host])
      flash[:notice] = "プリンタの更新は完了しました"
      redirect_to :action =>'index'
    else
      render :action =>'edit'
    end
  end
  
  def edit
    @page_title = "プリンタの編集"
    @host = Lpr::Lprhost.find(params[:id])
  end
  
  def show
    @page_title = "プリンタの詳細"
    @host = Lpr::Lprhost.find(params[:id])
  end
  
  def create
    @page_title = "プリンタの新規登録中"
    @host = Lpr::Lprhost.new(params[:host])
    if @host.save
      flash[:notice] = 'プリンタが正しく作成されました。'
      redirect_to :action=>'index'
    else
      render :action=>'new'
    end
  end
  
  def new
    @page_title = "プリンタの新規登録"
    @host = Lpr::Lprhost.new
  end
  
  def index
    @page_title = "プリンタの登録状況のTOP"
    @hosts = Lpr::Lprhost.paginate(:page=>params[:page],
    :conditions=>"back_day IS NULL", :per_page=>25, :order=>"id, back_day IS NULL")
  end
end


controllers/lpr/state_controller.rb

checkを定期的に実行する方法はまだ模索中。だって重いんだもん。

class Lpr::StateController < ApplicationController
  verify :redirect_to => { :action => :index }
  
  def gruff_line
    g = Gruff::Line.new 500
    g.title = "Print Paper"
    
    conditions = params[:cond] unless params[:cond].nil?
    lprs =  Lpr::Lprstate.find(:all, :conditions => conditions, :include => :lprhost)
    
    #ホスト名の配列を作る
    host_arry = Array.new
    lprs.each{|host| host_arry << host.lprhost.host_name }
    
    #ホスト毎の印刷枚数を取得する
    host_arry.uniq.each{|host|
      total_arry = Array.new
      lprs.each{|name|
        if name.lprhost.host_name == host
          total_arry << name.total_print
#nullとかお構いなしなので、データをプロットしたときに表示位置が変になる
        end
      }
      g.data(host, total_arry)
    }
    send_data(g.to_blob, :type => 'image/png')
  end


  def export_csv(conditions)
    find_search
    lprstates = Lpr::Lprstate.find(:all, :conditions => conditions, 
      :include => :lprhost, :order => 'lprstates.id DESC')
    filename = DateTime.now.strftime('%Y%m%d_%H%M_lprstate') + ".csv"
    stream_csv(filename) do |csv|
      csv << [
      "id".tosjis,
      "ホスト名".tosjis,
      "IP".tosjis,
      "総印刷".tosjis,
      "MSG".tosjis,
      "作成日".tosjis]
      lprstates.each do |s|
        csv << [s.id.to_s.tosjis,
                s.lprhost.host_name.to_s.tosjis,
                s.lprhost.ip.to_s.tosjis,
                s.total_print.to_s.tosjis,
                s.msg.to_s.tosjis,
                s.created_at.to_s(:jp).tosjis]
      end
    end
  end
  
  def search
    @page_title = "プリンタの状況の検索"
    find_search
    
    if request.get?
      if params[:page].nil?
        @lprstate_cond.empty!
        @lprstate_cond.host_name = params[:host_name] unless params[:host_name].nil?
        @lprstate_cond.dayfrom = params[:dayfrom].nil? ? Date.today : params[:dayfrom]
        @lprstate_cond.dayto = params[:dayto].nil? ? Date.today : params[:dayto]
      end
    else
      @lprstate_cond.set_params(params)
    end
    
    @conditions = @lprstate_cond.get_conditions
    @lprstates = Lpr::Lprstate.paginate(
      :page=>params[:page], :per_page=>50, 
      :conditions=>@conditions,
      :include => :lprhost, :order=>"lprstates.id"
    )
    export_csv(@conditions) if params[:export] == "CSV"

  end

  def index
    @page_title = "プリンタの状況のTOP"
  end
  
  def check
    @hosts =  Lpr::Lprhost.find(:all, :conditions=>"back_day IS NULL", :order=>"host_name")
    require 'ping'
    count = 0
    @hosts.each{|host_name|
      @state = Lpr::Lprstate.new
      if Ping.pingecho(host_name.ip, timeout = 1, "80")
        if host_name.model_name == "LBP1820"
          lprset = lpr_state1820(host_name.ip)
        elsif host_name.model_name == "LBP5900"
          lprset = lpr_state5900(host_name.ip)
          @state.toner_b_rest = lprset['toner_b_rest']
          @state.toner_black = lprset['toner_black']
          @state.toner_y_rest = lprset['toner_y_rest']
          @state.toner_yellow = lprset['toner_yellow']
          @state.toner_m_rest = lprset['toner_m_rest']
          @state.toner_magenta = lprset['toner_magenta']
          @state.toner_c_rest = lprset['toner_c_rest']
          @state.toner_cyan = lprset['toner_cyan']
        else
          break
        end
        @state.lprhost_id = host_name.id
        @state.total_print = lprset['total_print']
        @state.image_name = lprset['image_name']
        @state.msg = lprset['msg']
      else
        msg = "Ping応答がありまんでした"
        @state.lprhost_id = host_name.id
#この辺のエラー処理はテキトー
        @state.msg = msg
      end
      @state.save
      count += 1
    }
   redirect_to :controller=>"lpr/state", :action=>"index"
  end
  
private 
#テキトーな正規表現、プリンタのモデルによって書き換えないといけないので不便

  def lpr_state5900(ip)
    #LBP-5900用
    require 'net/http'
    Net::HTTP.version_1_2   # おまじない
    $proxy_addr = 'proxy-address'
    $proxy_port = 80
    
    lpr = Hash.new
    lpr['url'] = "http://"+ip
    env_path = '/dstatus.cgi'
    stat_path = '/dfeature.cgi'
    
    begin
      http = Net::HTTP::Proxy($proxy_addr, $proxy_port).new( ip )
      http.open_timeout = 2
      http.read_timeout = 5
      http.start do
        #総印刷ページ数
        response = http.get(stat_path)
        find_flag = 0
        response.body.toutf8.each{|l|
          #総印刷ページ数を取得する
          if find_flag == 1
            s =  l.index("<td>")
            e =  l.rindex("</td>")
            lpr['total_print'] = l.slice(s+4..e-1)
            break
          end
          find_flag = 1 if /総印刷ページ数/ =~ l.toutf8
        }
        
        #カセットの画像res.cgi?cass_42
        response = http.get(env_path)
        find_flag = 0
        response.body.toutf8.each{|l|
          #画像のファイル名
          if /res.cgi\?cass_/ =~ l
            lpr['image_name'] =  lpr_state5900_image(l)
          end
          
          #メッセージ
          if /statusinfo/ =~ l
            if find_flag == 0
              s = l.index("\"")
              e = l.rindex("\"")
              lpr['msg'] = l.slice(s+1..e-1).gsub(";","")
              find_flag = 1
            end
          end
          
          #トナーblack
          if /ink_b/ =~ l
            lpr['toner_b_rest'] = lpr_state5900_rest(l)
            lpr['toner_black'] = lpr_state5900_image(l)
          end
          
          #トナーyellow
          if /ink_y/ =~ l
            lpr['toner_y_rest'] = lpr_state5900_rest(l)
            lpr['toner_yellow'] = lpr_state5900_image(l)
          end
  
          #トナーmagenta
          if /ink_m/ =~ l
            lpr['toner_m_rest'] = lpr_state5900_rest(l)
            lpr['toner_magenta'] = lpr_state5900_image(l)
          end
          
          #トナーcyan
          if /ink_c/ =~ l
            lpr['toner_c_rest'] = lpr_state5900_rest(l)
            lpr['toner_cyan'] = lpr_state5900_image(l)
          end
        }
      end
      rescue 
        lpr['msg'] = "exception on HTTP: StandardError #{$!}"
      rescue Timeout::Error
        lpr['msg'] = "exception on HTTP: TimeoutError"
      rescue Exception
        lpr['msg'] = "exception on HTTP: #{$!}"
      end
      return lpr
  end
  
  def lpr_state5900_rest(line)
    s = line.index("alt")
    e = line.index("border")
    return line.slice(s+5..e-4)
  end
  
  def lpr_state5900_image(line)
    s = line.index("res.cgi\?")
    e = line.rindex("gif")
    return line.slice(s+8..e+2)
  end

  def lpr_state1820(ip)
    #LBP-1820用
    require 'net/http'
    Net::HTTP.version_1_2   # おまじない
    $proxy_addr = 'proxy-address'
    $proxy_port = 80
    
    lpr = Hash.new
    lpr['url'] = "http://"+ip
    env_path = '/dev/dev_env.shtml'
    stat_path = '/dev/dev_stat.shtml'
    
    begin
    http = Net::HTTP::Proxy($proxy_addr, $proxy_port).new( ip )
    http.open_timeout = 2
    http.read_timeout = 5
    http.start do
      response = http.get(env_path)
      find_flag = 0
      response.body.toutf8.each{|l|
        #総印刷ページ数を取得する
        if find_flag == 1
          s =  l.index("<td>")
          e =  l.rindex("</td>")
          lpr['total_print'] = l.slice(s+4..e-1)
          break
        end
        find_flag = 1 if /総印刷ページ数/ =~ l.toutf8
      }

      #用紙の状態を表す画像のURLを取得する。(状態によって、パスが変わる)
      response = http.get(stat_path)
      response.body.toutf8.each{|l|
      if /cassette/ =~ l
        s = l.index("image")
        e = l.index("gif")
        lpr['image_name'] = l.slice(s+6,e-s-3)
      end
      }
      #デバイスの状態を取得する
      find_flag = 0
      response.body.toutf8.each{|l|
        if find_flag == 1
          s = l.index("><b>")
          e = l.index("/b>")
          lpr['msg'] = l.slice(s+4..e-2)
          break
        end
        find_flag = 1 if /デバイス状態/ =~ l.toutf8
      }
    end
    rescue 
      lpr['msg'] = "exception on HTTP: StandardError #{$!}"
    rescue Timeout::Error
      lpr['msg'] = "exception on HTTP: TimeoutError"
    rescue Exception
      lpr['msg'] = "exception on HTTP: #{$!}"
    end
    return lpr
  end

  def find_search
    @lprstate_cond = (session[:lprstate_cond] ||= Lpr::LprstateCond.new)
  end
end

models/lpr/lprhost.rb

class Lpr::Lprhost < ActiveRecord::Base
  has_many :lprstate

  validates_presence_of :host_name, :room_name, :model_name
  validates_format_of :room_id, :with=>/\A[0-9]+\Z/
  validates_format_of :intro_day, :with=>/\d{4}-\d{2}-\d{2}/
#IPアドレスのvalidate(ナイショ)

  attr_human_name :id=>'ID',
  :ip=> "IPアドレス",
  :host_name=>"ホスト名",
  :room_name=>"設置場所",
  :room_id=>"部屋ID",
  :model_name=>"機種名",
  :intro_day=>"導入日",
  :back_day=>"返却日",
  :memo=>"メモ",
  :created_at=>"作成日",
  :updated_at=>"更新日"

  protected
  
  def validate
#IPアドレスの検証をするところ(ナイショ)    

    unless host_name.blank?
      errors.add(:host_name, "半角英数のみです") unless /\w+/ =~ host_name
    end
  end
  
end

models/lpr/lprstate.rb

class Lpr::Lprstate < ActiveRecord::Base
  belongs_to :lprhost
  attr_human_name :id=>'ID',
  :lprhost_id=>'ホストID',
  :total_print=>'総印刷数',
  :toner_black=>'黒残量',
  :toner_b_rest=>'黒%',
  :toner_yellow=>'黄残量',
  :toner_y_rest=>'黄%',
  :toner_magenta=>'マゼンタ残量',
  :toner_m_rest=>'マゼンタ%',
  :toner_cyan=>'シアン残量',
  :toner_c_rest=>'シアン%',
  :image_name=>'紙残量',
  :msg=>'メッセージ',
  :memo=>'メモ',
  :created_at=>'作成日',
  :updated_at=>'更新日'
end

models/lpr/lprstate_cond.rb

class Lpr::LprstateCond < Cond
  attr_accessor :host_name, :dayfrom, :dayto
  
  def empty!
    @host_name = ""
    @dayfrom = Date.today
    @dayto = Date.today
  end
  
  def set_params(params)
    @host_name = params[:lprstate_cond][:host_name]
    @dayfrom = params[:lprstate_cond][:dayfrom]
    @dayto = params[:lprstate_cond][:dayto]
  end
  
  def get_conditions
    w = Where.new
    unless @dayfrom.nil? && @dayto.nil?
      w.and('lprstates.created_at between ? AND ?', @dayfrom.to_date, (@dayto.to_date+1).to_date)
    end
    w.and('lprhosts.host_name LIKE ?', '%'+@host_name+'%') if !@host_name.blank?
    w.to_s
  end
end

ビューで重要なのが
views/lpr/state/search.html.erb

<%= javascript_include_tag('lpr') %>
<div id="table_area">
<div id="edit_dialog">
<h3><%= @page_title %></h3>
<%= link_to '検索', :controller => 'lpr/state', :action=>'search' %><br />
<%= link_to '登録', :controller => 'lpr/host', :action=>'index' %><br />
<%= ja_error_messages_for(:lprstate) -%>

<img src="<%= url_for :controller=>'lpr/state', :action=>'gruff_line', :cond=>@conditions %>">


<div  class='iftb_doc'>
<%= form_tag({:action=>"search"}, {:name=>"frm"}) %>
	<%= render :partial=> "lpr/state/any/search" %>
	<INPUT type="button" name="cmdReset" value="検索条件リセット" onclick="clearSearchFormLprstate();">
	<%= submit_tag "検索する" %>
	<%= submit_tag("CSV", {'name'=>'export', 'value'=>'CSV'}) %>
</form>
</div>
</div>
</div>

<%- unless @lprstates.total_entries == 0 %>
	<div class='iftb_pagenate'>検索結果: <%=h @lprstates.total_entries %> 件
	<%= will_paginate @lprstates, :prev_label => '<<前' , :next_label => '次>>' -%></div>
	<%= render :partial=> "lpr/state/any/result" %>
	<div class='iftb_pagenate'>検索結果: <%=h @lprstates.total_entries %> 件
	<%= will_paginate @lprstates, :prev_label => '<<前' , :next_label => '次>>' -%></div>
<%- else %>
<p>指定された条件に適合する結果はありませんでした。(´・ω・`)ショボーン</p>
<%- end %>

グラフを書き出すところの検索用のパラメータを渡しています。