Posted at

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

More than 5 years have passed since last update.

[Ruby] ネットワーク接続されたプリンタのステータスを取得するプログラム を mruby で動作するよう移植しました。


1. mruby をダウンロードする

$ git clone https://github.com/mruby/mruby.git


2. ビルド設定を編集する


build_config.rb

############################

# Start of your build_config

MRuby::Build.new do |conf|
toolchain :clang # :gcc, :visualcpp

conf.bins = %w(mrbc)

# mruby's Core GEMs
conf.gem 'mrbgems/mruby-bin-mirb'
conf.gem 'mrbgems/mruby-bin-mruby'
conf.gem 'mrbgems/mruby-print'
conf.gem 'mrbgems/mruby-sprintf'

# user-defined GEMs
conf.gem :git => 'https://github.com/iij/mruby-io.git'
conf.gem :git => 'https://github.com/iij/mruby-mtest.git'
conf.gem :git => 'https://github.com/iij/mruby-socket.git'
conf.gem :git => 'https://github.com/iij/mruby-pack.git'
end

# End of your build_config
############################



3. ビルドする

$ rake

CC tools/mrbc/mrbc.c -> build/host/tools/mrbc/mrbc.o
CC src/array.c -> build/host/src/array.o
... 中略 ...
Build summary:

================================================
Config Name: host
Output Directory: build/host
Binaries: mrbc
Included Gems:
mruby-bin-mirb - mirb command
- Binaries: mirb
mruby-bin-mruby - mruby command
- Binaries: mruby
mruby-print - standard print/puts/p
mruby-sprintf - standard Kernel#sprintf method
mruby-io
mruby-mtest
mruby-socket
mruby-pack
================================================


4. mrubyのスクリプトを作成する


printer_status.rb

# printer_status.rb

# SNMPクライアントプログラム (mruby 1.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 = IO.select([us], nil, nil, 3)
if not a # Timeout
us.close
return nil
end
response = us.recv(2000)
us.close
response
end
# sysName を取得する
# @return [String] 受信したsysName
def sysName
request = [
0x30, 0x27, 0x02, 0x01, 0x00, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa0, 0x1a, 0x02,
0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x02, 0x01, 0x01, 0x05, 0x00, 0x05, 0x00].pack('C*')
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
request = [
0x30, 0x27, 0x02, 0x01, 0x00, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa0, 0x1a, 0x02,
0x02, 0x03, 0xe9, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x02, 0x01, 0x01, 0x03, 0x00, 0x05, 0x00].pack('C*')
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
request = [
0x30, 0x2A, 0x02, 0x01, 0x00, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa0, 0x1d, 0x02,
0x02, 0x03, 0xea, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x11, 0x30, 0x0f, 0x06, 0x0b, 0x2b,
0x06, 0x01, 0x02, 0x01, 0x19, 0x03, 0x02, 0x01, 0x05, 0x01, 0x05, 0x00].pack('C*')
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
request = [
0x30, 0x2A, 0x02, 0x01, 0x00, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa0, 0x1d, 0x02,
0x02, 0x03, 0xeb, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x11, 0x30, 0x0f, 0x06, 0x0b, 0x2b,
0x06, 0x01, 0x02, 0x01, 0x19, 0x03, 0x05, 0x01, 0x01, 0x01, 0x05, 0x00].pack('C*')
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 = "192.168.1.40"
if ARGV.size >= 1
host = ARGV[0]
end
snmp = SnmpClient.new(host)
name = snmp.sysName
puts sprintf "sysName[#{name}]\n"
60.times { |i|
name = snmp.sysName
time = snmp.sysUpTime
deviceStatus = snmp.hrDeviceStatus
printerStatus = snmp.hrPrinterStatus
puts sprintf "sysUpTime[#{time}] deviceStatus[#{deviceStatus}] printerStatus[#{printerStatus}]\n"
break if debug
#sleep 1
}
#exit
end



CRubyスクリプトからの修正点


4.1 バイト列の生成方法の変更

変更前


mruby-string-utf8 を利用

Integer#chr で意図せぬデータ化けが発生


変更後


mruby-pack を利用

Array#pack で配列をバイト列に変換



4.2 システムコールの削除


sleep と exit をコメントアウト



4.3 select() の呼び出し方法変更


mruby では I/O も外部ライブラリ(mrbgems)に依存するため IO.select() で呼び出し。



5. 上記のスクリプトを実行する

$ cd bin

$ ./mruby printer_status.rb