Skip to content

Instantly share code, notes, and snippets.

@paralleltree
Last active March 20, 2019 09:57
Show Gist options
  • Save paralleltree/d14153bdf2891878070c4f23833ed1f3 to your computer and use it in GitHub Desktop.
Save paralleltree/d14153bdf2891878070c4f23833ed1f3 to your computer and use it in GitHub Desktop.
require 'optparse'
require 'pasori'
require_relative 'suica'
parser = OptionParser.new
args = {}
parser.on('--idm') { |v| args[:mode] = :idm }
parser.on('--balance') { |v| args[:mode] = :balance }
parser.on('--gate-history') { |v| args[:mode] = :gate_history }
parser.on('--purchase-history') { |v| args[:mode] = :purchase_history }
parser.on('-v', '--verbose') { |v| args[:verbose] = true }
parser.parse!(ARGV)
unless args[:mode]
warn 'No options supplied. Exiting.'
exit 1
end
Pasori.open do |p|
warn 'Waiting for a card...' if args[:verbose]
begin
p.felica_polling(Felica::POLLING_SUICA) do |f|
warn "Reading..." if args[:verbose]
suica = Suica.new(f)
case args[:mode]
when :idm
puts sprintf("%016X", suica.idm)
when :balance
puts suica.balance
when :gate_history
suica.gate_history.reverse.each do |h|
puts "#{h[:time].strftime("%FT%R")} #{h[:gate_type]} #{h[:line_code]} #{h[:station_code]}"
end
when :purchase_history
suica.purchase_history.reverse.each do |h|
vendor = Suica::VendorType[h[:vendor_type]]
event = Suica::EventType[h[:event_type]]
puts "#{h[:serial_number]} #{h[:date]} #{format("%4d", h[:balance])} #{event} #{vendor}"
end
end
end
break
rescue PasoriError
sleep 1
end while true
end
require 'date'
require 'pasori'
class Suica
def initialize(felica)
@felica = felica
end
def fetch(service, mode = 0)
[].tap do |arr|
@felica.foreach(service) { |l| arr << l }
end
end
def idm
@felica.idm.unpack('Q>').first
end
def balance
@felica.read(0x008B, 0).unpack('NNCnvCn')[4]
end
def purchase_history
fetch(Felica::SERVICE_SUICA_HISTORY).map do |l|
d = l.unpack('CCCCnCCCCvCvC')
{
vendor_type: d[0],
event_type: d[1],
entered_with_pass: d[3] == 3,
exited_with_pass: d[3] == 4,
origin_line_code: d[5],
origin_station_code: d[6],
dest_line_code: d[7],
dest_station_code: d[8],
date: Date.new((d[4] >> 9) + 2000, (d[4] >> 5) & 0b1111, d[4] & 0b11111),
balance: d[9],
serial_number: d[11],
special: d[12]
}
end
end
def gate_history
fetch(Felica::SERVICE_SUICA_IN_OUT).map do |l|
d = l.unpack('CCCCnnnvvn')
{
gate_type: d[0],
line_code: d[2],
station_code: d[3],
time: Time.new((d[5] >> 9) + 2000, (d[5] >> 5) & 0x0f, d[5] & 0x1f, (d[6] >> 8).to_s(16).to_i, (d[6] & 0xff).to_s(16).to_i, 0),
extra: d[7]
}
end
end
VendorType = {
0x03 => '精算機',
0x05 => '車載端末',
0x07 => '券売機',
0x08 => '券売機',
0x09 => '入金機',
0x12 => '券売機',
0x14 => '券売機等',
0x15 => '券売機等',
0x16 => '改札機',
0x17 => '簡易改札機',
0x18 => '窓口端末',
0x19 => '窓口端末',
0x1A => '改札端末',
0x1B => '携帯電話',
0x1C => '乗継精算機',
0x1D => '連絡改札機',
0x1F => '簡易入金機',
0x23 => '新幹線改札機',
0x46 => 'VIEW ALTTE',
0x48 => 'VIEW ALTTE',
0xC7 => '物販端末',
0xC8 => '自販機',
}
EventType = {
0x01 => '改札出場',
0x02 => 'チャージ',
0x03 => '磁気券購入',
0x04 => '精算',
0x05 => '入場精算',
0x06 => '改札窓口処理',
0x07 => '新規発行',
0x08 => '窓口控除',
0x0d => 'バス',
0x0f => 'バス',
0x11 => '再発行処理',
0x13 => '支払(新幹線利用)',
0x14 => '入場時オートチャージ',
0x15 => '出場時オートチャージ',
0x1f => 'バスチャージ',
0x23 => 'バス路面電車企画券購入',
0x46 => '物販',
0x48 => '特典チャージ',
0x49 => 'レジ入金',
0x4a => '物販取消',
0x4b => '入場物販',
0xc6 => '現金併用物販',
0xcb => '入場現金併用物販',
0x84 => '他社精算',
0x85 => '他社入場精算',
}
GateType = {
0x20 => '出場',
0x40 => '出場(定期)',
0xa0 => '入場',
0xc0 => '入場(定期)',
0x00 => '清算',
}
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment