class Plc::Emulator::EmuPlc
Constants
- CLEAR_PROGRAM_FLAG
- CYCLE_RUN_FLAG
- INDEX_BIT_STACK
- INDEX_BIT_STACK_COUNT
- SIZE_OF_BIT_STACK
- STOP_PLC_FLAG
- SUFFIXES
Attributes
device_dict[R]
errors[R]
program_data[RW]
program_pointer[R]
Public Class Methods
new()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 52 def initialize SUFFIXES.each do |k| eval "@#{k}_devices = []" end @lock = Mutex.new reset end
Public Instance Methods
bool()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 111 def bool (stack_device.word & 1) != 0 ? true : false end
bool=(value)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 115 def bool= value if value stack_device.word |= 1 else stack_device.word &= 0xfffe; end end
device_by_name(name)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 60 def device_by_name name @lock.synchronize { d = device_dict[name] unless d d = EmuDevice.new name device_dict[name] = d end d } end
device_by_type_and_number(type, number)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 71 def device_by_type_and_number type, number d = EmuDevice.new type, number device_by_name d.name end
execute_console_commands(line)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 131 def execute_console_commands line a = line.chomp.split(/\s+/) case a.first when /^ST/i d = device_by_name a[1] d.set_value true, :in "OK\r" when /^RS/i d = device_by_name a[1] d.set_value false, :in "OK\r" when /^RDS/i d = device_by_name a[1] c = a[2].to_i r = [] if d.bit_device? c.times do r << (d.bool(:out) ? 1 : 0) d = device_by_name (d+1).name end else case d.suffix when "PRG" c.times do r << program_data[d.number * 2, 2].pack("c*").unpack("n").first d = device_by_name (d+1).name end else c.times do r << d.word(:out) d = device_by_name (d+1).name end end end r.map{|e| e.to_s}.join(" ") + "\r" when /^WRS/i d = device_by_name a[1] c = a[2].to_i case d.suffix when "PRG" a[3, c].each do |v| program_data[d.number * 2, 2] = [v.to_i].pack("n").unpack("c*") d = device_by_name (d+1).name end else if d.bit_device? a[3, c].each do |v| d.set_value v == "0" ? false : true, :in d = device_by_name (d+1).name end else a[3, c].each do |v| d.word = v.to_i d.set_value v.to_i, :in d = device_by_name (d+1).name end end end "OK\r" when /E/ eval(a[1..-1].join(" ")).inspect else raise "Unknown command #{a.first}" end end
reset()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 76 def reset @word = 0 @program_data = [] @device_dict ||= {} @lock.synchronize { @device_dict.values.each do |d| d.reset end } end
run()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 123 def run Thread.new do loop do run_cycle end end end
run_cycle()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 87 def run_cycle status_to_plc = device_by_name "SD0" status_form_plc = device_by_name "SD1" sync_input case status_to_plc.value & (STOP_PLC_FLAG | CLEAR_PROGRAM_FLAG) when STOP_PLC_FLAG status_form_plc.value = 0 sleep 0.1 when STOP_PLC_FLAG | CLEAR_PROGRAM_FLAG reset status_form_plc.value = CLEAR_PROGRAM_FLAG sleep 0.1 when 0 status_form_plc.value = CYCLE_RUN_FLAG @program_pointer = 0 clear_stacks while fetch_and_execution; end sleep 0.0001 else sleep 0.1 end sync_output end
Private Instance Methods
add_error(reason)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 382 def add_error reason @errors << {pc:@program_pointer, reason:reason} end
anb()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 418 def anb b = self.bool pop_stack self.bool &= b true end
and(inverse=false)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 402 def and inverse=false b = fetch_bool_value inverse return false if b.nil? self.bool &= b true end
and_join_stack()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 280 def and_join_stack d = stack_device b = d.word == 0xffff d.word = 0xffff push_stack b end
ani()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 408 def ani; send :and, true; end
clear_stacks()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 224 def clear_stacks stack_device.word = 0xffff stack_count_device.word = 0 end
fetch_1_byte()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 326 def fetch_1_byte if @program_pointer < program_data.size program_data[@program_pointer].tap do @program_pointer += 1 end else nil end end
fetch_and_execution()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 314 def fetch_and_execution code = fetch_1_byte return false unless code mnemonic = mnenonic_table[code] if mnemonic && respond_to?(mnemonic, true) r = send mnemonic r else nil end end
fetch_bool_value(inverse=false)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 372 def fetch_bool_value inverse=false d = fetch_device_or_value return false unless d unless d.is_a? EmuDevice add_error "ld must be specify a device nor number #{d}" return false end inverse ? !d.bool : d.bool end
fetch_device_or_value()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 336 def fetch_device_or_value c = fetch_1_byte return false unless c # check value type case c & 0xe0 when 0x80 # bit device when 0x00 # immediate number return c else add_error "invalidate value type #{c.to_h(16)}" return false end # device type device_type = c & 0x0f # number number = 0 if (c & 0x10) == 0 number = fetch_1_byte else number = fetch_2_byte end # make device d = device_by_type_and_number device_type, number unless d add_error "invalid device type #{c&0x3} and number #{number}" return false end d end
inv()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 396 def inv and_join_stack self.bool = !self.bool true end
ld(inverse=false)
click to toggle source
— mnenonic —
# File lib/plc/emulator/emu_plc.rb, line 388 def ld inverse=false b = fetch_bool_value inverse return false if b.nil? push_stack b true end
ldi()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 394 def ldi; ld true; end
mnenonic_table()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 287 def mnenonic_table @mnemonic_table ||= begin s = <<EOB |00|END|INV|MEP|ANB|MEF|ORB|FEND|NOP| |10|LD|LDI|LDP|LDPI|LDF|LDFI|MC|MCR| |20|AND|ANI|ANDP|ANPI|ANDF|ANFI| |30|OR|ORI|ORP|ORPI|ORF|ORFI| |40|OUT|OUTI|MPS|MRD|MPP| | |50|SET|RST|PLS| |PLF|| |60|FF|||||| |70| |80|SFT|SFTP|SFL|SFLP|SFR|SFRP| |90|BSFL|BSFLP|DSFL|DSFLP|BSFR|BSFRP|DSFR|DSFRP| |A0|SFTL|SFTLP|WSFL|WSFLP|SFTR|SFTRP|WFSR|WSFRP| EOB table = {} s.lines.each_with_index do |line, h| line.chomp.split("|")[2..-1].each_with_index do |mnemonic, l| unless mnemonic.length == 0 table[h << 4 | l] = mnemonic.downcase.to_sym end end end table end end
mpp()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 449 def mpp; mrd; end
mps()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 434 def mps and_join_stack b = self.bool push_stacks push_stack b true end
mrd()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 442 def mrd b = self.bool pop_stacks push_stacks push_stack b true end
nop()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 432 def nop; true; end
or(inverse=false)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 410 def or inverse=false b = fetch_bool_value inverse return false if b.nil? self.bool |= b true end
orb()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 425 def orb b = self.bool pop_stack self.bool |= b true end
ori()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 416 def ori; send :or, true; end
out(inverse=false)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 451 def out inverse=false and_join_stack d = fetch_device_or_value unless d.is_a? EmuDevice add_error "ld must be specify a device nor number #{d}" return false end d.bool = inverse ? !self.bool : self.bool unless d.input? pop_stack true end
outi()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 462 def outi; out true; end
pop_stack()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 219 def pop_stack stack_device.word >>= 1 stack_device.word |= 0x8000 end
pop_stacks()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 246 def pop_stacks if stack_count_device.word <= 0 puts "ERROR: stacks underflow pc: H#{program_pointer.to_s(16).rjust(4, "0")}" return end values = [] (SIZE_OF_BIT_STACK - 1).downto 0 do |i| values << stack_device(i).word end v = values.pop values.each_with_index do |v, i| stack_device(i).word = v end stack_count_device.word -= 1 v end
push_stack(flag = false)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 209 def push_stack flag = false stack_device.word <<= 1 if flag stack_device.word |= 1 else stack_device.word &= 0xfffe end stack_device.word &= 0xffff end
push_stacks()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 229 def push_stacks if stack_count_device.word > SIZE_OF_BIT_STACK puts "ERROR: stacks overflow pc: H#{program_pointer.to_s(16).rjust(4, "0")}" return end values = [] (SIZE_OF_BIT_STACK - 1).downto 0 do |i| values << stack_device(i).word end values.shift values << 0xffff SIZE_OF_BIT_STACK.times do |i| stack_device(i).word = values.pop end stack_count_device.word += 1 end
rst()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 475 def rst; set true; end
set(inverse=false)
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 464 def set inverse=false and_join_stack d = fetch_device_or_value unless d.is_a? EmuDevice add_error "ld must be specify a device nor number #{d}" return false end d.bool = !inverse if self.bool unless d.input? pop_stack true end
stack_count_device()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 205 def stack_count_device device_by_name (device_by_name("SD0") + INDEX_BIT_STACK_COUNT).name end
stack_device(index=0)
click to toggle source
stack operations
# File lib/plc/emulator/emu_plc.rb, line 201 def stack_device index=0 device_by_name (device_by_name("SD0") + INDEX_BIT_STACK + index).name end
sync_input()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 264 def sync_input @lock.synchronize { device_dict.values.each do |d| d.sync_input end } end
sync_output()
click to toggle source
# File lib/plc/emulator/emu_plc.rb, line 272 def sync_output @lock.synchronize { device_dict.values.each do |d| d.sync_output end } end