ネットワーク接続されたプリンタのステータスを取得する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)となる。
参考リンク
- Printer MIB
http://www.ietf.org/rfc/rfc1759.txt -
chihayafuruさん にこのプログラムを
mrubyに移植
していただきました。