LoginSignup
7
7

More than 5 years have passed since last update.

[Ruby] ネットワーク接続されたプリンタのステータスを取得するプログラム

Posted at

ネットワーク接続されたプリンタのステータスを取得するRubyプログラム。

ライブラリを使わずSNMPパケットを自分で作って送信し、レスポンスを受信する。IPアドレスを持ち、SNMPプロトコルをサポートし、MIBを持つネットワークプリンタから、sysName, sysUpTime, hrDeviceStatus, hrPrinterStatusの4つを取得して表示する。

ソースコード

printer_status.rb
# encoding: Shift_JIS
require 'socket'

# printer_status.rb
# SNMPクライアントプログラム (Ruby2.0.0)
# ライブラリを使わずSNMPパケットを自分で作って送信し、レスポンスを受信する。
# sysName,sysUpTime,hrDeviceStatus,hrPrinterStatusの4つを取得する
# @see http://www.ietf.org/rfc/rfc1759.txt Printer MIB
# @note 使い方 ruby printer_status.rb プリンタのIPアドレス
# @author @saltheads
# @version 1.0.0

# Hex Dump クラス
class HexDump
  # 1行ダンプ
  # @param [String] data ダンプする対象データ
  # @param [Integer] offset 先頭アドレス
  # @param [Integer] step 1行あたりのデータ数
    def self.dumpline(data,offset,step)
      if (data.length > 0) then
        line = sprintf("%08x  ",offset)
        chars = ""
        data.each_byte{ |c|
          line << sprintf("%02x ",c)
          chars << ((c >= 0x20 && c <= 0x7e) ? c : '.')
        }
        line << "   "*(step - data.size + 1)
        line << chars
        puts line
      end
    end
  # データ全体をダンプ
  # @param [String] fulldata ダンプする対象データ
    def self.dump(fulldata)
      offset = 0
      step = 16
      while data = fulldata[offset,step]
        dumpline(data,offset,step)
        offset += step
      end
    end
end

# Snmp Client クラス
class SnmpClient
  # コンストラクタ
  # @param [String] host IPアドレス("1.2.3.4")
  # @param [Integer] port snmp ポート番号 通常161
  # @return [SnmpClient] オブジェクト
  def initialize(host,port=161)
    @host = host
    @port = port
    @debug = true & false
    printf "host #{host}\n" if @debug
  end
  # パケットダンプ
  # @param [String] title タイトル文字列
  # @param [String] data ダンプする対象データ
  def dump(title,data)
    if @debug
      printf "#{title}\n"
      HexDump.dump(data)
    end
  end
  # UDPでrequestを送信し、受信したデータを返す
  # @param [String] request 送信データ
  # @return [String] 受信したデータ
  def sendReceive(request)
    us = UDPSocket.open()
    us.connect(@host,@port)
    us.send(request, 0)
    a=select([us],nil,nil,3)
    if not a     # Timeout
      us.close
      return nil
    end
    response = us.recv(2000,0)
    us.close
    response
  end
  # スペース区切りの文字列から送信データを作る
  # @param [String] packet スペース区切り文字列
  # @return [String] 変換したバイナリデータ
  def packet2data(packet)
    array = packet.split(' ')
    request = ""
    array.each{ |c| request += c.to_i(16).chr }
    request
  end
  # sysName を取得する
  # @return [String] 受信したsysName
  def sysName
    packet  = "30 27 02 01 00 04 06 70 75 62 6c 69 63 a0 1a 02 "
    packet += "02 03 e8 02 01 00 02 01 00 30 0e 30 0c 06 08 2b "
    packet += "06 01 02 01 01 05 00 05 00 "   

    request = packet2data(packet)
    dump('send',request)   
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)   

    len = response[40].bytes.to_a[0]
    len -= 1
    value = response[41..(41+len)]
    hexdumpdata(value) if @debug
    printf("sysName [#{value}]\n") if @debug
    return value
  end

  # sysUpTime を取得する
  # @return [Integer] 受信したsysUpTime
  def sysUpTime
    packet  = "30 27 02 01 00 04 06 70 75 62 6c 69 63 a0 1a 02 "
    packet += "02 03 e9 02 01 00 02 01 00 30 0e 30 0c 06 08 2b "
    packet += "06 01 02 01 01 03 00 05 00 "

    request = packet2data(packet)
    dump('send',request)   
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)   

    time = response[41..43]
    hexdumpdata(time) if @debug
    t = 0
    time.each_byte { |c|
      # printf("%x\n",c)
      t = t*256 + c
    }
    printf("sysUpTime %06x = %d\n",t,t) if @debug
    return t
  end

  # hrDeviceStatusを取得する
  # @return [String] 受信したhrDeviceStatusの意味を表す文字列
  def hrDeviceStatus
    packet  = "30 2A 02 01 00 04 06 70 75 62 6c 69 63 a0 1d 02 "
    packet += "02 03 ea 02 01 00 02 01 00 30 11 30 0f 06 0b 2b "
    packet += "06 01 02 01 19 03 02 01 05 01 05 00 "

    request = packet2data(packet)
    dump('send',request)   
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)   

    status = response[44]
    hexdumpdata(status) if @debug
    val = status.bytes.to_a[0]
    case val
    when 1 then s = "unknown(1)"
    when 2 then s = "running(2)"
    when 3 then s = "warning(3)"
    when 4 then s = "testing(4)"
    when 5 then s = "down(5)"
    else s = "unknown(#{val})"
    end
    return s
  end

  # hrPrinterStatusを取得する
  # @return [String] 受信したhrPrinterStatusの意味を表す文字列
  def hrPrinterStatus
    packet  = "30 2A 02 01 00 04 06 70 75 62 6c 69 63 a0 1d 02 "
    packet += "02 03 eb 02 01 00 02 01 00 30 11 30 0f 06 0b 2b "
    packet += "06 01 02 01 19 03 05 01 01 01 05 00 "

    request = packet2data(packet)
    dump('send',request)   
    response = sendReceive(request)
    return if response == nil
    dump('recv',response)   

    status = response[44]
    hexdumpdata(status) if @debug
    val = status.bytes.to_a[0]
    case val
    when 1 then s = "other(1)"
    when 3 then s = "idle(3)"
    when 4 then s = "printing(4)"
    when 5 then s = "warmup(5)"
    else s = "unknown(#{val})"
    end
    return s
  end
end

if __FILE__ == $0
  debug = false & true
  host = "10.0.1.99"
  if ARGV.size >= 1
    host = ARGV[0]
  end
  snmp = SnmpClient.new(host)
  name = snmp.sysName
  printf "sysName[#{name}]\n"
  60.times { |i|
    name = snmp.sysName
    time = snmp.sysUpTime
    deviceStatus = snmp.hrDeviceStatus
    printerStatus = snmp.hrPrinterStatus
    printf "sysUpTime[#{time}] deviceStatus[#{deviceStatus}] printerStatus[#{printerStatus}]\n"
    break if debug
    sleep 1
  }
  exit
end

実行例

プログラムを先に走らせた状態で、プリンタの電源をいれ、紙切れにしたあと、紙をセットし、印刷したようす。

result.txt
C:>ruby printer_status.rb
sysName[]
sysUpTime[] deviceStatus[] printerStatus[]
sysUpTime[] deviceStatus[] printerStatus[]
sysUpTime[] deviceStatus[] printerStatus[]
sysUpTime[] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[660] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[1396] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[29265] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[29383] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[29488] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[38746] deviceStatus[down(5)] printerStatus[other(1)]
sysUpTime[38851] deviceStatus[down(5)] printerStatus[other(1)]
sysUpTime[38956] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[39061] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[39896] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[40001] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[267976] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[268081] deviceStatus[running(2)] printerStatus[other(1)]
sysUpTime[268186] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[268291] deviceStatus[running(2)] printerStatus[warmup(5)]
sysUpTime[270931] deviceStatus[running(2)] printerStatus[printing(4)]
sysUpTime[271036] deviceStatus[running(2)] printerStatus[printing(4)]
sysUpTime[271141] deviceStatus[running(2)] printerStatus[idle(3)]
sysUpTime[271246] deviceStatus[running(2)] printerStatus[idle(3)]

私の手持ちのプリンターでは、
印刷できないときに、deviceStatusがdown(5)となり、
印刷中か印刷すべきものが残っているときに、printerStatusがprinting(4)となる。


参考リンク

  1. Printer MIB
    http://www.ietf.org/rfc/rfc1759.txt
  2. chihayafuruさん にこのプログラムを
    mrubyに移植 していただきました。
7
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7