From 14c61e35d14adb2f8b62f266aa69da6927b896e2 Mon Sep 17 00:00:00 2001 From: Redo0 Date: Sat, 5 Jun 2021 17:49:58 -0500 Subject: [PATCH] move network functionality to new file --- sim/main.lua | 294 +++------------------------------------------ sim/network.lua | 213 ++++++++++++++++++++++++++++++++ sim/simulation.lua | 4 +- sim/utility.lua | 63 ++++++++++ sim/wire.lua | 2 +- 5 files changed, 295 insertions(+), 281 deletions(-) create mode 100644 sim/network.lua diff --git a/sim/main.lua b/sim/main.lua index 2a566fe..b9bbc1e 100644 --- a/sim/main.lua +++ b/sim/main.lua @@ -1,19 +1,22 @@ +-- External requirements +local socket = require("socket") +local ffi = require("ffi") + +-- Disallow access to undefined global variables (helps detect errors) assert(getmetatable(_G)==nil, "_G already has a metatable") setmetatable(_G, { __index = function(t, i) error("attempt to access nil variable "..i, 2) end }) +-- Set save directory OPT_SAVE_DIR = arg[1] or error("must specify save location") OPT_SAVE_DIR = OPT_SAVE_DIR:gsub("\\", "/") OPT_SAVE_DIR = OPT_SAVE_DIR:gsub("/$", "") print("Save location set to \""..OPT_SAVE_DIR.."\"") -local socket = require("socket") -local ffi = require("ffi") - +-- Local includes dofile("iosafe.lua") - FFI = ffi dofile("utility.lua") dofile("simulation.lua") @@ -24,301 +27,34 @@ dofile("port.lua") dofile("gate.lua") dofile("save.lua") dofile("compile.lua") +dofile("network.lua") FFI = nil +-- Default settings OPT_TICK_ENABLED = true OPT_TICK_TIME = 0.032 OPT_TICK_MULT = 1 OPT_FX_UPDATES = true OPT_FX_TIME = 0.032 -OPT_TICK_INF = 29 - +-- Tick rate measurement state local tickdelay = 0 local ticksperinterval = 0 - -bool_to_int = {[false] = 0, [true] = 1} - local lastticktime = 0 local ticks = 0 local tickrate = 0 local lastmeasuretime = 0 local lastfxtime = 0 - local avgticks = {} local totalticks = 0 +-- Create new simulation local sim = Simulation.new(Simulation) GSim = sim -local units = { - "uHz", - "mHz", - "Hz", - "kHz", - "MHz", - "GHz", -} +network_accept_client() -local function round(x) - return math.floor(x+0.5) -end - -local function unitize(v) - local unit = 1 - v = v*1000000 - - while v >= 1000 do - v = v/1000 - unit = unit+1 - end - - local s - if v >= 100 then - s = "" .. round(v/10)*10 - elseif v >= 10 then - s = "" .. round(v) - elseif v >= 1 then - s = "" .. round(v*10)/10 - if #s == 1 then s = s .. ".0" end - else - s = 0 - end - - return s .. " " .. units[unit] -end - -function vectotable(vec) - local tbl = {} - for comp in string.gmatch(vec, "([^%s]+)") do - tbl[#tbl+1] = tonumber(comp) - end - return tbl -end - -function tabletostring(table) - local str = tostring(table[1]) - for i = 2, #table do - str = str .. " " .. tostring(table[i]) - end - return str -end - -function toboolean(value) - local num = tonumber(value) - if num == 1 then - return true - else - return false - end -end - -local function acceptclient() - client = server:accept() - client:settimeout(0) - local ip, port = client:getsockname() - print("Connection from " .. ip .. ":" .. port) -end - -server = assert(socket.bind("*", 25000)) -client = nil - -local ip, port = server:getsockname() -print("Server listening on " .. ip .. ":" .. port) - -acceptclient() - -while 1 do - local line, err = client:receive() - - if not err then - local data = {} - local i = 1 - line = line:gsub(";;", "; ;") - line = line:gsub(";$", "; ") - - for str in string.gmatch(line, "([^;]+)") do - data[i] = str or "" - i = i + 1 - end - - local i = 1 - while i <= #data do - if data[i] == "W" then - local min = vectotable(data[i+3]) - local max = vectotable(data[i+4]) - local bounds = {min[1], min[2], min[3], max[1], max[2], max[3]} - - local wire = Wire.new(tonumber(data[i+1]), tonumber(data[i+2]), bounds) - Simulation.addwire(sim, wire) - - i = i + 4 - elseif data[i] == "G" then - local objref = tonumber(data[i+1]) - local definition = Simulation.getdefinitionbyref(sim, tonumber(data[i+2])) - - assert(definition, "No gate definition for objref "..objref.." defref "..tonumber(data[i+1])) - - local position = vectotable(data[i+3]) - local rotation = tonumber(data[i+4]) - local gate = GateDefinition.constructgate(definition, objref, position, rotation) - - Simulation.addgate(sim, gate) - --print(gate.objref) - --Gate.init(gate) - --Gate.logic(gate) - - i = i + 4 - elseif data[i] == "RW" then - Simulation.removewire(sim, tonumber(data[i+1])) - i = i + 1 - elseif data[i] == "RG" then - Simulation.removegate(sim, tonumber(data[i+1])) - i = i + 1 - elseif data[i] == "GD" then - --print("---------------------------------------[[[[") - --print(table.concat(data, "]]]]\n[[[[", i, math.min(#data, i+100))) - --print("]]]]---------------------------------------") - local objref = tonumber(data[i+1]) - local name = data[i+2] - local desc = data[i+3] - local init = data[i+4] - local logic = data[i+5] - local input = data[i+6] - local global = data[i+7] - local numports = tonumber(data[i+8]) - local ports = {} - - for a = i+9, numports*5+i+8, 5 do - local portd = { - type = tonumber(data[a]), - position = vectotable(data[a+1]), - direction = tonumber(data[a+2]), - causeupdate = toboolean(data[a+3]), - name = data[a+4], - } - ports[#ports+1] = portd - - if not portd.direction then print(line) end - end - - local definition = GateDefinition.new(objref, name, desc, init, logic, input, global, ports) - Simulation.addgatedefinition(sim, definition) - - i = i + 8 + numports*5 - elseif data[i] == "SL" then - local wire = Simulation.getwirebyref(sim, tonumber(data[i+1])) - if wire ~= nil then - Wire.setlayer(wire, tonumber(data[i+2])) - end - - i = i + 2 - elseif data[i] == "OPT" then - local option = data[i+1] - local value = tonumber(data[i+2]) - - if option == "TICK_ENABLED" then - OPT_TICK_ENABLED = toboolean(value) - elseif option == "TICK_TIME" then - if value < 0 or value > 999999 then - value = 0 - end - if value<=0.001 then value = 0.0001 end - OPT_TICK_TIME = value - elseif option == "FX_UPDATES" then - OPT_FX_UPDATES = toboolean(value) - elseif option == "FX_TIME" then - if value < 0 or value > 999999 then - value = 0 - end - OPT_FX_TIME = value - elseif option=="TICK_MULT" then - OPT_TICK_MULT = value - end - - i = i + 2 - elseif data[i] == "GINFO" then - local userid = data[i+1] - local objref = tonumber(data[i+2]) - - local info = "" - - local wire = Simulation.getwirebyref(sim, objref) - if wire then - local group = Wire.getgroup(wire) - local numwires = 0; for k, wire2 in pairs(group.wires ) do numwires = numwires +1 end - local numportsi = 0; for k, port in pairs(group.in_ports ) do numportsi = numportsi+1 end - local numgatesu = group.num_gates_update - local numportso = 0; local numportson=0; - for k, port in pairs(group.out_ports) do - numportso = numportso+1 - if Port.getstate(port)==1 then numportson = numportson+1 end - end - - info = "\\c5Net " .. tostring(group):match("table: 0x(.+)"):upper() .. "\n" .. (Wire.getgroup(wire).state==1 and "\\c2On" or "\\c0Off") .. "\n" .. - "Wires: "..numwires.."\n".. - "In Ports: " ..numportsi.."\n".. - "Out Ports: "..numportso.."\n".. - "Gates Update: "..numgatesu.."\n".. - "Out Ports On: "..(group.state_num) - ; - end - - local gate = Simulation.getgatebyref(sim, objref) - if gate then - local def = Gate.getdefinition(gate) - info = "\\c5" .. def.name .. "
" - for i = 1, #gate.ports do - local port = gate.ports[i] - local state - if port.type==PortTypes.input then - state = Gate.getportstate(gate, i) - else - state = Port.getstate(port) - end - info = info .. (state==1 and "\\c2" or "\\c0") .. def.ports[i].name .. (i ~= #gate.ports and " " or "") - end - end - - if info ~= "" then - client:send("GINFO\t" .. userid .. "\t" .. expandescape(info) .. "\n") - end - - i = i + 2 - elseif data[i] == "SINFO" then - client:send("SINFO\t" .. data[i+1] .. "\t" .. sim.nwires .. "\t" .. sim.ngates .. "\t" .. sim.ninports .. "\t" .. sim.noutports .. "\n") - i = i + 1 - elseif data[i] == "TICK" then - Simulation.tickinit(sim) - Simulation.tickinput(sim) - Simulation.ticklogic(sim) - ticks = ticks + 1 - elseif data[i] == "IN" then - local gate = Simulation.getgatebyref(sim, tonumber(data[i+1])) - local argc = tonumber(data[i+2]) - local argv = {} - for a = i+3, i+3+argc-1 do - argv[#argv+1] = collapseescape(data[a]) - end - if gate then - Simulation.queuegateinput(sim, gate, argv) - end - - i = i+2+argc - elseif data[i] == "SAVE" then - print("saving all data") - logicsave() - else - print("invalid data "..data[i]) - end - - i = i + 1 - end - elseif err == "closed" then - sim = Simulation.new(Simulation) - acceptclient() - end - +while true do local time = os.clock() if OPT_TICK_ENABLED then @@ -348,6 +84,8 @@ while 1 do end if time-lastfxtime >= OPT_FX_TIME then + network_update() + if OPT_FX_UPDATES then Simulation.sendfxupdate(sim) end @@ -365,7 +103,7 @@ while 1 do ticks = 0 - client:send("TPS\t" .. unitize((totalticks/#avgticks)/0.1) .. "\n") + network_send("TPS\t" .. unitize((totalticks/#avgticks)/0.1) .. "\n") lastmeasuretime = os.clock() end end diff --git a/sim/network.lua b/sim/network.lua new file mode 100644 index 0000000..0b91bc6 --- /dev/null +++ b/sim/network.lua @@ -0,0 +1,213 @@ + +function network_send(data) + client:send(data) +end + +function network_accept_client() + server = assert(socket.bind("*", 25000)) + client = nil + + local ip, port = server:getsockname() + print("Server listening on " .. ip .. ":" .. port) + + client = server:accept() + client:settimeout(0) + local ip, port = client:getsockname() + print("Connection from " .. ip .. ":" .. port) +end + +function network_update() + local sim = GSim + + local line, err = client:receive() + + if not err then + local data = {} + local i = 1 + line = line:gsub(";;", "; ;") + line = line:gsub(";$", "; ") + + for str in string.gmatch(line, "([^;]+)") do + data[i] = str or "" + i = i + 1 + end + + local i = 1 + while i <= #data do + if data[i] == "W" then + local min = vectotable(data[i+3]) + local max = vectotable(data[i+4]) + local bounds = {min[1], min[2], min[3], max[1], max[2], max[3]} + + local wire = Wire.new(tonumber(data[i+1]), tonumber(data[i+2]), bounds) + Simulation.addwire(sim, wire) + + i = i + 4 + elseif data[i] == "G" then + local objref = tonumber(data[i+1]) + local definition = Simulation.getdefinitionbyref(sim, tonumber(data[i+2])) + + assert(definition, "No gate definition for objref "..objref.." defref "..tonumber(data[i+1])) + + local position = vectotable(data[i+3]) + local rotation = tonumber(data[i+4]) + local gate = GateDefinition.constructgate(definition, objref, position, rotation) + + Simulation.addgate(sim, gate) + --print(gate.objref) + --Gate.init(gate) + --Gate.logic(gate) + + i = i + 4 + elseif data[i] == "RW" then + Simulation.removewire(sim, tonumber(data[i+1])) + i = i + 1 + elseif data[i] == "RG" then + Simulation.removegate(sim, tonumber(data[i+1])) + i = i + 1 + elseif data[i] == "GD" then + --print("---------------------------------------[[[[") + --print(table.concat(data, "]]]]\n[[[[", i, math.min(#data, i+100))) + --print("]]]]---------------------------------------") + local objref = tonumber(data[i+1]) + local name = data[i+2] + local desc = data[i+3] + local init = data[i+4] + local logic = data[i+5] + local input = data[i+6] + local global = data[i+7] + local numports = tonumber(data[i+8]) + local ports = {} + + for a = i+9, numports*5+i+8, 5 do + local portd = { + type = tonumber(data[a]), + position = vectotable(data[a+1]), + direction = tonumber(data[a+2]), + causeupdate = toboolean(data[a+3]), + name = data[a+4], + } + ports[#ports+1] = portd + + if not portd.direction then print(line) end + end + + local definition = GateDefinition.new(objref, name, desc, init, logic, input, global, ports) + Simulation.addgatedefinition(sim, definition) + + i = i + 8 + numports*5 + elseif data[i] == "SL" then + local wire = Simulation.getwirebyref(sim, tonumber(data[i+1])) + if wire ~= nil then + Wire.setlayer(wire, tonumber(data[i+2])) + end + + i = i + 2 + elseif data[i] == "OPT" then + local option = data[i+1] + local value = tonumber(data[i+2]) + + if option == "TICK_ENABLED" then + OPT_TICK_ENABLED = toboolean(value) + elseif option == "TICK_TIME" then + if value < 0 or value > 999999 then + value = 0 + end + if value<=0.001 then value = 0.0001 end + OPT_TICK_TIME = value + elseif option == "FX_UPDATES" then + OPT_FX_UPDATES = toboolean(value) + elseif option == "FX_TIME" then + if value < 0 or value > 999999 then + value = 0 + end + OPT_FX_TIME = value + elseif option=="TICK_MULT" then + OPT_TICK_MULT = value + end + + i = i + 2 + elseif data[i] == "GINFO" then + local userid = data[i+1] + local objref = tonumber(data[i+2]) + + local info = "" + + local wire = Simulation.getwirebyref(sim, objref) + if wire then + local group = Wire.getgroup(wire) + local numwires = 0; for k, wire2 in pairs(group.wires ) do numwires = numwires +1 end + local numportsi = 0; for k, port in pairs(group.in_ports ) do numportsi = numportsi+1 end + local numgatesu = group.num_gates_update + local numportso = 0; local numportson=0; + for k, port in pairs(group.out_ports) do + numportso = numportso+1 + if Port.getstate(port)==1 then numportson = numportson+1 end + end + + info = "\\c5Net " .. tostring(group):match("table: 0x(.+)"):upper() .. "\n" .. (Wire.getgroup(wire).state==1 and "\\c2On" or "\\c0Off") .. "\n" .. + "Wires: "..numwires.."\n".. + "In Ports: " ..numportsi.."\n".. + "Out Ports: "..numportso.."\n".. + "Gates Update: "..numgatesu.."\n".. + "Out Ports On: "..(group.state_num) + ; + end + + local gate = Simulation.getgatebyref(sim, objref) + if gate then + local def = Gate.getdefinition(gate) + info = "\\c5" .. def.name .. "
" + for i = 1, #gate.ports do + local port = gate.ports[i] + local state + if port.type==PortTypes.input then + state = Gate.getportstate(gate, i) + else + state = Port.getstate(port) + end + info = info .. (state==1 and "\\c2" or "\\c0") .. def.ports[i].name .. (i ~= #gate.ports and " " or "") + end + end + + if info ~= "" then + network_send("GINFO\t" .. userid .. "\t" .. expandescape(info) .. "\n") + end + + i = i + 2 + elseif data[i] == "SINFO" then + network_send("SINFO\t" .. data[i+1] .. "\t" .. sim.nwires .. "\t" .. sim.ngates .. "\t" .. sim.ninports .. "\t" .. sim.noutports .. "\n") + i = i + 1 + elseif data[i] == "TICK" then + Simulation.tickinit(sim) + Simulation.tickinput(sim) + Simulation.ticklogic(sim) + ticks = ticks + 1 + elseif data[i] == "IN" then + local gate = Simulation.getgatebyref(sim, tonumber(data[i+1])) + local argc = tonumber(data[i+2]) + local argv = {} + for a = i+3, i+3+argc-1 do + argv[#argv+1] = collapseescape(data[a]) + end + if gate then + Simulation.queuegateinput(sim, gate, argv) + end + + i = i+2+argc + elseif data[i] == "SAVE" then + print("saving all data") + logicsave() + else + print("invalid data "..data[i]) + end + + i = i + 1 + end + elseif err == "closed" then + --sim = Simulation.new(Simulation) + --acceptclient() + print("Connection closed") + error() + end +end \ No newline at end of file diff --git a/sim/simulation.lua b/sim/simulation.lua index 4d2a86e..6eb8de9 100644 --- a/sim/simulation.lua +++ b/sim/simulation.lua @@ -365,7 +365,7 @@ function Simulation.sendfxupdate(sim) data = data .. "\t" .. Wire.getobjref(wire) end - client:send("WU\t" .. data .. "\n") + network_send("WU\t" .. data .. "\n") end end @@ -384,7 +384,7 @@ function Simulation.sendcallbacks(sim) data = data .. "\t" .. objref .. "\t"..(#escargs)..(#escargs>0 and ("\t"..table.concat(escargs, "\t")) or "") end - client:send(data .. "\n") + network_send(data .. "\n") sim.callbacks = nil end end diff --git a/sim/utility.lua b/sim/utility.lua index b9520b7..e8df85d 100644 --- a/sim/utility.lua +++ b/sim/utility.lua @@ -1,4 +1,6 @@ +bool_to_int = {[false] = 0, [true] = 1} + local escapes = { {"\\", "b"}, {"\t", "t"}, @@ -85,3 +87,64 @@ function array_add(array, value) end table.insert(array, value) end + +function round(x) + return math.floor(x+0.5) +end + +local units = { + "uHz", + "mHz", + "Hz", + "kHz", + "MHz", + "GHz", +} +function unitize(v) + local unit = 1 + v = v*1000000 + + while v >= 1000 do + v = v/1000 + unit = unit+1 + end + + local s + if v >= 100 then + s = "" .. round(v/10)*10 + elseif v >= 10 then + s = "" .. round(v) + elseif v >= 1 then + s = "" .. round(v*10)/10 + if #s == 1 then s = s .. ".0" end + else + s = 0 + end + + return s .. " " .. units[unit] +end + +function vectotable(vec) + local tbl = {} + for comp in string.gmatch(vec, "([^%s]+)") do + tbl[#tbl+1] = tonumber(comp) + end + return tbl +end + +function tabletostring(table) + local str = tostring(table[1]) + for i = 2, #table do + str = str .. " " .. tostring(table[i]) + end + return str +end + +function toboolean(value) + local num = tonumber(value) + if num == 1 then + return true + else + return false + end +end diff --git a/sim/wire.lua b/sim/wire.lua index 93f290b..fee0308 100644 --- a/sim/wire.lua +++ b/sim/wire.lua @@ -20,7 +20,7 @@ function Wire.setlayer(wire, layer) end function Wire.update(wire) - client:send("WU\t" .. (wire.group.state~=0 and "1" or "0") .. "\t" .. wire.objref .. "\n") + network_send("WU\t" .. (wire.group.state~=0 and "1" or "0") .. "\t" .. wire.objref .. "\n") end function Wire.setgroup(wire, group)