require 'rubyserial' require 'observer' require 'timers' # # User Configs # # 9600 baud, 8 data bits, and no parity device = '/dev/ttyUSB0' baud = 9600 bits = 8 parity = :none @serial_line_ending = "\r" # # Setup # @serial = Serial.new device, baud, bits, parity @buffer = Queue.new @timers = Timers::Group.new # # Convenience function # def serial_writeln(str) @serial.write(str+@serial_line_ending) end # # Board interface functions # def b_date() serial_writeln("d") end def b_show(num) serial_writeln("s #{num}") end def b_modify(num, mask, tag) serial_writeln("m #{num} #{mask} #{tag}") end def b_all() serial_writeln("a") end def b_remove(num) serial_writeln("r #{num}") end def b_open(num) serial_writeln("o #{num}") end def b_unlock() # Listen for an unlock event; raise an error if not found foo = @timers.after(5) { if @events[:unlocked].changed? print "foo" else print "didn't get an unlock event in time" end } print foo.inspect SerialListener.new(@events[:unlocked], Proc.new{ |value| print "got unlock event #{value}" } ) serial_writeln("u") @timers.fire end def b_lock() serial_writeln("l") end def b_1() serial_writeln("1") end def b_2() serial_writeln("2") end def b_3() serial_writeln("3") end def b_9() serial_writeln("9") end def b_time(sec,min,hr,day,date,mon,yr) serial_writeln("t #{sec} #{min} #{hr} #{day} #{date} #{mon} #{yr}") end def b_enable(num) serial_writeln("e #{num}") end def b_hardware(num) serial_writeln("h #{num}") end def b_help() serial_writeln("?") end # # Thread functions # def console_in() run = true while run # Get console input cline = gets unless cline.nil? case cline when /^d/ b_date() when /^s (\d+)/ b_show($1) when /^m (\d+) (\d+) (\d+)/ b_modify($1,$2,$3) when /^a/ b_all() when /^r (\d+)/ b_remove($1) when /^o (\d+)/ b_open($1) when /^u/ b_unlock() when /^l/ b_lock() when /^1/ b_1() when /^2/ b_2() when /^3/ b_3() when /^9/ b_9() when /^t (\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+)/ b_time($1,$2,$3,$4,$5,$6,$7) when /^e (\d+)/ b_enable($1) when /^h (\d+)/ b_hardware($1) when /^?/ b_help() end end end end def serial_in() run = true readbuffer = "" while run # begin # Get serial input char by char sdata = @serial.read(1) # If we have data unless sdata == "" # Look for newlines if /\n|\r/.match(sdata) # Flush the existing buffer and reset @buffer.push(readbuffer) readbuffer = "" else # Append non-newlines to the buffer readbuffer += sdata end end # rescue Exception => e # print e # sline = nil # end end end def parse_in() run = true while run sline = @buffer.pop() if sline != "" case sline when /(\d*) unlocked$/ # ends line print ">Door #{$1} unlocked\n" @events[:unlocked].send($1) when /All Doors relocked/ print ">All Doors relocked\n" when /Door (\d*) locked$/ # ends line print ">Door #{$1} locked\n" when /User ([\dA-F]*) presented tag at reader (\d)/ print ">User #{$1} presented tag at reader #{$2}\n" when /User ([\dA-F]*) granted access at reader (\d)/ print ">User #{$1} granted access at reader #{$2}\n" when /User ([\dA-F]*) denied access at reader (\d)/ print ">User #{$1} denied access at reader #{$2}\n" when /Command ([\dA-F]*) entered at reader (\d)/ print ">Command #{$1} entered at reader #{$2}\n" when /Zone (\d?) sensor activated/ print ">Zone #{$1} sensor activated\n" when /User (\d?) unlocked door (\d)/ print ">User #{$1} unlocked door #{$2}\n" when /Door 1 locked for (\d+) bed time/ print "Door 1 locked for #{$1} bed time\n" when /User (\d+) locked out/ print "User #{$1} locked out\n" when /User (\d+) locked out/ print "User #{$1} locked out\n" when /Sensor (\d+) value:(\d+)/ print "Sensor #{$1} value:#{$2}\n" when /Alarm triggered/ print "Alarm triggered\n" when /Alarm level changed to (\d*)/ print ">Alarm level changed to #{$1}\n" when /Alarm armed level changed to (\d*)/ print ">Alarm armed level changed to #{$1}\n" when /User database erased/ print "User database erased\n" when /Invalid user modify attempted/ print "Invalid user modify attempted\n" when /User (\d*) successfully modified/ print ">User #{$1} successfully modified\n" when /Invalid user delete attempted/ print "Invalid user delete attempted\n" when /User deleted at position (\d*)/ print "User deleted at position #{$1}\n" when /User deleted at position (\d*)/ print ">User deleted at position #{$1}\n" when /User (\d+) authenticated/ print "User #{$1} authenticated\n" when /User not found/ print "User not found\n" when /Bad user number/ print "Bad user number\n" when /User dump started/ print ">User dump started\n" when /Invalid door number/ print "Invalid door number\n" when /Alarm armed state \(1=armed\):(\d*)/ print ">Alarm armed state: #{$1}\n" when /Alarm siren state \(1=activated\):(\d*)/ print ">Alarm siren state: #{$1}\n" when /Front door open state \(0=closed\):(\d*)/ print ">Front door open state: #{$1}\n" when /Roll up door open state \(0=closed\):(\d*)/ print ">Roll up door open state: #{$1}\n" when /Door 1 unlocked state\(1=locked\):(\d*)/ print ">Door 1 unlocked state: #{$1}\n" when /Door 2 unlocked state\(1=locked\):(\d*)/ print ">Door 2 unlocked state: #{$1}\n" when /^(\d+)\t+(\d+)\t+([\dA-Fa-f]+)$/ print ">User #{$1} mask #{$2} tag #{$3}\n" # User dump info when /Priveleged mode enabled./ print ">Priveleged mode enabled.\n" when /Priveleged mode disabled./ print ">Priveleged mode disabled.\n" when /Access Denied. Priveleged mode is not enabled./ print ">Access Denied. Priveleged mode is not enabled.\n" when /Valid commands are/ print "?Valid commands are\n" # Help output when /^\(/ print "?"+sline+"\n" # Help output when /^ / print "?"+sline+"\n" # Help output when /^[\d:]{5,8}\s+[\d\/]{5,8}\s+[A-Z]{3}\s+$/ print ">"+sline+"\n" # Date output only when /^Old time :([\d:]{5,8})\s+([\d\/]{5,8})\s+([A-Z]{3})\s+$/ print ">Old time: #{$1} #{$2} #{$3}\n" when /^New time :([\d:]{5,8})\s+([\d\/]{5,8})\s+([A-Z]{3})\s+$/ print ">New time: #{$1} #{$2} #{$3}\n" when /Reader1-0:(\d*)/ print ">Reader1-0:#{$1}\n" # Status output when /Reader1-1:(\d*)/ print ">Reader1-1:#{$1}\n" # Status output when /Reader2-0:(\d*)/ print ">Reader2-0:#{$1}\n" # Status output when /Reader2-1:(\d*)/ print ">Reader2-1:#{$1}\n" # Status output when /Zone 1:(\d*)/ print ">Zone 1:#{$1}\n" # Status output when /Zone 2:(\d*)/ print ">Zone 2:#{$1}\n" # Status output when /Zone 3:(\d*)/ print ">Zone 3:#{$1}\n" # Status output when /Zone 4:(\d*)/ print ">Zone 4:#{$1}\n" # Status output when /Zone TAMP:(\d*)/ print ">Zone TAMP:#{$1}\n" # Status output when /Relays and LEDs ([a-z]*)/ print ">Relays and LEDs #{$1}\n" # Status output when /Relays and LEDs ([a-z]*)/ print ">Relays and LEDs #{$1}\n" # Status output when /UserNum: Usermask: TagNum:/ # print nothing, we don't care when /Pass: / # print nothing, we don't care when /^\d+$/ # print nothing, we don't care else print "ERR: UNKNOWN: "+sline.inspect+"\n" # Error output end end end end # # Classes # class SerialEvents include Observable @oldvalue = nil def send(value) if value != @oldvalue changed @oldvalue = value notify_observers(Time.now, value) end end end class SerialListener @callback = nil def initialize(observable, callback) @callback = callback observable.add_observer(self) end def update(time, value) @callback.call(value) end end # # Runtime # @events = { unlocked: SerialEvents.new, locked: SerialEvents.new, } puts "> Started threads at #{Time.now}" t1 = Thread.new{console_in()} t2 = Thread.new{serial_in()} t3 = Thread.new{parse_in()} t1.join t2.join t3.join puts "> End threads at #{Time.now}"