From 280a28777cb8c2a584125e609f6f8b5a9a2ca0b7 Mon Sep 17 00:00:00 2001 From: Guillaume ARM Date: Sun, 17 Jul 2022 19:48:33 +0200 Subject: [PATCH] feat: net and eventloop v2 --- README.md | 3 +- apis/eventloop.lua | 14 +- apis/net.lua | 217 +++++++++++++++------ install.lua | 9 +- ping.lua | 25 +-- router.lua | 13 +- ping-server.lua => servers/ping-server.lua | 18 +- startup/servers.lua | 2 +- 8 files changed, 206 insertions(+), 95 deletions(-) rename ping-server.lua => servers/ping-server.lua (53%) diff --git a/README.md b/README.md index a324fe0..32d0419 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ wget run https://raw.githubusercontent.com/guillaumearm/cc-libs/master/install.l ``` ## Apis -- `apis/net`: api to simplify sending and receiving routed messages +- `apis/eventloop`: a simple eventloop library +- `apis/net`: api to simplify sending and receiving routed messages (based on eventloop library) ## Servers - `router`: route messages (you need to setup a router to be able to use `apis/net`) diff --git a/apis/eventloop.lua b/apis/eventloop.lua index 8b57557..25baef0 100644 --- a/apis/eventloop.lua +++ b/apis/eventloop.lua @@ -1,3 +1,5 @@ +-- eventloop 2.0.0 + -- Basic event loop library for computer craft -- -- Example usage: @@ -23,7 +25,7 @@ end local STOP = '@libeventloop/STOP_HANDLER_SUBSCRIPTION'; -local function createEventLoop(net) -- TODO listenMessages +local function createEventLoop() local api = {} local runningLoop = false; @@ -358,16 +360,6 @@ local function createEventLoop(net) -- TODO listenMessages end end - function api.listenMessages(givenChannel, handler) - net.openChannel(givenChannel) - - return api.register("modem_message", function(_, channel, _, payload) - if net.isPayloadOk(payload) and channel == givenChannel then - handler(payload.message); - end - end) - end - -- onStop function api.onStop(handler) assert(type(handler) == 'function', 'bad argument #1 (function expected)') diff --git a/apis/net.lua b/apis/net.lua index 9845669..5e08267 100644 --- a/apis/net.lua +++ b/apis/net.lua @@ -1,37 +1,39 @@ --- Network API v1.0.1 +-- Network API v2.0.0 + +local createEventLoop = require('/apis/eventloop'); local DEFAULT_TIMEOUT_WAIT_MESSAGE = 0.5; -- in seconds local DEFAULT_ROUTING_CHANNEL = 10; --- Utilitaire pour savoir si un payload nous est destiné. --- le parametre 'payload' est une table avec les champs suivants: +-- Utilitaire pour savoir si un packet nous est destiné. +-- le parametre 'packet' est une table avec les champs suivants: -- - sourceId: l'id de la machine qui a envoyé le message -- - destId: l'id du destinataire, si l'id est nil le message est routé a tout le monde -- - routerId: l'id du routeur qui s'est occupé de transmettre le message -- - message: le contenu du message (qui sera le plus souvent une table) -- return un boolean -local function isPayloadOk(payload) - if type(payload) ~= "table" then +local function isPacketOk(packet) + if type(packet) ~= "table" then return false; end - if not payload.routerId or not payload.sourceId then + if not packet.routerId or not packet.sourceId then return false; end - if payload.sourceId == os.getComputerID() then + if packet.sourceId == os.getComputerID() then return false; end - if payload.destId == nil then + if packet.destId == nil then return true; end - if type(payload.destId) == 'number' and payload.destId == os.getComputerID() then + if type(packet.destId) == 'number' and packet.destId == os.getComputerID() then return true; end - if type(payload.destId) == 'string' and payload.destId == os.getComputerLabel() then + if type(packet.destId) == 'string' and packet.destId == os.getComputerLabel() then return true; end @@ -69,24 +71,27 @@ end -- -- local createNet = require('apis/net'); -- net = createNet(); +-- local net = createNet(nil, modem); + +-- net.listenRequest(PING_CHANNEL, 'ping', function(message, reply) +-- if message == 'ping' then +-- reply('pong'); +-- end +-- end) -- --- -- envoyer un message sur le canal 9 --- net.send(9, 'ping'); --- --- -- recevoir et afficher un message sur le canal 9 --- local message = net.waitMessage(9); --- if message == 'pong' then --- print('pong recu'); --- end --- -local function createNetwork(modem, routingChannel, timeoutInSec) +local function createNetwork(el, modem, routingChannel, timeoutInSec) + el = el or createEventLoop(); modem = modem or peripheral.find("modem") or error("modem not found"); routingChannel = routingChannel or DEFAULT_ROUTING_CHANNEL; timeoutInSec = timeoutInSec or DEFAULT_TIMEOUT_WAIT_MESSAGE; + local function openChannel(chan) + return modem.open(chan); + end + -- net.send function - local function send(channel, message, destId) - local payload = { + local function sendRaw(channel, message, destId) + local packet = { sourceId = os.getComputerID(), sourceLabel = os.getComputerLabel(), routerId = nil, @@ -94,51 +99,151 @@ local function createNetwork(modem, routingChannel, timeoutInSec) message = message } - modem.transmit(routingChannel, channel, payload); + return modem.transmit(routingChannel, channel, packet); end - -- net.waitMessage function - local function waitMessage(channelToListen) - local receivedPayload; + local function listenRaw(channel, handler) + openChannel(channel); - modem.open(channelToListen); - - local timerId = os.startTimer(timeoutInSec); - local timedOut = false; - - repeat - local messageType, timerIdOrSide, channel, _, payload = pullMultipleEvents("modem_message", "timer"); - local channelOk, payloadOk; - - if messageType == "modem_message" then - receivedPayload = payload; - channelOk = channel == channelToListen; - payloadOk = isPayloadOk(payload); - elseif messageType == 'timer' and timerIdOrSide == timerId then - timedOut = true; + return el.register('modem_message', function(_, _, replyChannel, packet) + if isPacketOk(packet) and channel == replyChannel then + handler(packet.message, packet); end - until channelOk and payloadOk or timedOut - - if timedOut then - return nil; - end - - if receivedPayload then - return receivedPayload.message, receivedPayload; - end - - return nil; + end) end - local function openChannel(chan) - modem.open(chan); + local function send(channel, eventType, payload, destId) + local event = { type = eventType, payload = payload }; + return sendRaw(channel, event, destId); + end + + local function listen(channel, eventType, handler) + return listenRaw(channel, function(event, packet) + if event.type == eventType then + handler(event.payload, packet) + end + end) + end + + local function listenRequest(channel, eventType, handler) + return listen(channel, eventType, function(payload, packet) + local reply = function(responsePayload) + send(channel, eventType .. "_response", responsePayload, packet.sourceId); + end + + handler(payload, reply, packet); + end) + end + + local function sendRequest(channel, eventType, payload, destId) + local ok = false; + local result = nil; + local packetResult = nil; + + local privateEventLoop = createEventLoop(); + local privateNet = createNetwork(privateEventLoop, modem, routingChannel, timeoutInSec); + + privateNet.listen(channel, eventType .. "_response", function(responsePayload, packet) + ok = true; + result = responsePayload + packetResult = packet; + privateNet.stop(); + end) + + privateEventLoop.setTimeout(function() + result = "net.sendRequest timeout!" + privateNet.stop(); + end, timeoutInSec); + + privateNet.send(channel, eventType, payload, destId); + privateNet.start(); + + return ok, result, packetResult; + end + + local function sendMultipleRequests(channel, eventType, payload, destId) + local ok = false; + local results = {}; + local packetResults = {}; + + local privateEventLoop = createEventLoop(); + local privateNet = createNetwork(privateEventLoop, modem, routingChannel, timeoutInSec); + + privateNet.listen(channel, eventType .. "_response", function(responsePayload, packet) + ok = true; + table.insert(results, responsePayload) + table.insert(packetResults, packet); + end) + + privateEventLoop.setTimeout(function() + if #results == 0 then + results = "net.sendRequest timeout!" + end + privateNet.stop(); + end, timeoutInSec); + + privateNet.send(channel, eventType, payload, destId); + privateNet.start(); + + return ok, results, packetResults; + end + + local function createRequest(channel, eventType) + local requestApi = {}; + + function requestApi.send(payload, destId) + return sendRequest(channel, eventType, payload, destId); + end + + function requestApi.sendMultiple(payload, destId) + return sendMultipleRequests(channel, eventType, payload, destId); + end + + function requestApi.listen(handler) + return listenRequest(channel, eventType, handler) + end + + return requestApi; + end + + local function createEvent(channel, eventType) + local eventApi = {} + + + function eventApi.send(payload, destId) + return send(channel, eventType, payload, destId); + end + + function eventApi.listen(handler) + return listen(channel, eventType, handler) + end + + return eventApi; + end + + local function start() + return el.startLoop(); + end + + local function stop() + return el.stopLoop(); end return { + sendRaw = sendRaw, + listenRaw = listenRaw, send = send, - waitMessage = waitMessage, - isPayloadOk = isPayloadOk, + listen = listen, + sendRequest = sendRequest, + sendMultipleRequests = sendMultipleRequests, + listenRequest = listenRequest, + createRequest = createRequest, + createEvent = createEvent, + isPacketOk = isPacketOk, openChannel = openChannel, + events = el, + start = start, + stop = stop, } end diff --git a/install.lua b/install.lua index 1da0256..e10a8e7 100644 --- a/install.lua +++ b/install.lua @@ -1,12 +1,15 @@ local LIST_FILES = { - 'ping.lua', - 'ping-server.lua', - 'router.lua', 'startup/servers.lua', + 'servers/ping-server.lua', + 'ping.lua', + 'router.lua', 'apis/net.lua', 'apis/eventloop.lua', }; +-- remove old files +fs.delete('ping-server.lua') + local REPO_PREFIX = 'https://raw.githubusercontent.com/guillaumearm/cc-libs/master/' local previousDir = shell.dir() diff --git a/ping.lua b/ping.lua index 1b4f7b6..96efd94 100644 --- a/ping.lua +++ b/ping.lua @@ -1,4 +1,4 @@ --- ping v1.2.0 +-- ping v2.0.0 local PING_CHANNEL = 9; local createNet = require('apis/net'); @@ -7,7 +7,6 @@ local net = createNet(); local args = table.pack(...); local targetComputerId = tonumber(args[1]) or args[1]; - local sourceId = os.getComputerID() local sourceLabel = os.getComputerLabel(); @@ -18,16 +17,20 @@ if targetComputerId == nil or targetComputerId == sourceId or targetComputerId = end -- envoyer un message sur le canal 9 à la machine cible -net.send(PING_CHANNEL, "ping", targetComputerId); --- recevoir et afficher un message sur le canal 9 -while true do - local message, payload = net.waitMessage(PING_CHANNEL); +local ok, results, packets = net.sendMultipleRequests(PING_CHANNEL, 'ping', 'ping', targetComputerId); - if message == "pong" then - print("=> pong from " .. tostring(payload.sourceId) - .. (payload.sourceLabel and " (label=" .. tostring(payload.sourceLabel) .. ")" or "")); - elseif message == nil then - break +if not ok then + error(results) +end + +for k, message in ipairs(results) do + if message == 'pong' then + local packet = packets[k]; + + -- if targetComputerId == nil or targetComputerId == packet.sourceId or targetComputerId == packet.sourceLabel then + print("=> pong from " .. tostring(packet.sourceId) + .. (packet.sourceLabel and " (label=" .. tostring(packet.sourceLabel) .. ")" or "")); + -- end end end diff --git a/router.lua b/router.lua index 5c509a1..f68c1e0 100644 --- a/router.lua +++ b/router.lua @@ -1,4 +1,4 @@ --- router v1.1.0 +-- router v1.2.0 local ROUTER_CHANNEL = 10; local VERBOSE = true; @@ -6,10 +6,17 @@ local VERBOSE = true; local modem = peripheral.find("modem") or error("modem not found"); modem.open(ROUTER_CHANNEL); +print('started router on port ' .. tostring(ROUTER_CHANNEL) .. '...') + local routerId = os.getComputerID(); local function isPingForServer(payload) - if payload.message ~= 'ping' then + + if not payload.message then + return false; + end + + if payload.message.type ~= 'ping' then return false; end @@ -44,7 +51,7 @@ while true do sourceLabel = os.getComputerLabel(), routerId = routerId, destId = payload.sourceId, - message = 'pong' + message = { type = "ping_response", payload = "pong" } } modem.transmit(replyChannel, replyChannel, responseRouterPayload) end diff --git a/ping-server.lua b/servers/ping-server.lua similarity index 53% rename from ping-server.lua rename to servers/ping-server.lua index c0aa4d1..c319d4d 100644 --- a/ping-server.lua +++ b/servers/ping-server.lua @@ -1,10 +1,10 @@ --- ping-server v1.0.0 +-- ping-server v2.0.0 -- -- Example: implementation simple de ping-server local PING_CHANNEL = 9; local MODEM_DETECTION_TIME = 3; -- in seconds -local createNet = require('apis/net'); +local createNet = require('/apis/net'); local modem = peripheral.find('modem'); @@ -18,13 +18,13 @@ while not modem do os.sleep(MODEM_DETECTION_TIME); end -local net = createNet(modem); - -while true do - local message, payload = net.waitMessage(PING_CHANNEL); +local net = createNet(nil, modem); +net.listenRequest(PING_CHANNEL, 'ping', function(message, reply) if message == 'ping' then - -- le troisième parametre de la fonction `net.send` est pour router un message a une machine specifique - net.send(PING_CHANNEL, 'pong', payload.sourceId); + reply('pong'); end -end +end) + + +net.start(); diff --git a/startup/servers.lua b/startup/servers.lua index ed62ab9..ec5f9ef 100644 --- a/startup/servers.lua +++ b/startup/servers.lua @@ -1,7 +1,7 @@ -- Server Launcher v1.0.0 local SERVERS = { - "ping-server", + "servers/ping-server", }; local function shellFn()