feat: net and eventloop v2

This commit is contained in:
Guillaume ARM 2022-07-17 19:48:33 +02:00
parent dab6e0011e
commit 280a28777c
8 changed files with 206 additions and 95 deletions

View File

@ -6,7 +6,8 @@ wget run https://raw.githubusercontent.com/guillaumearm/cc-libs/master/install.l
``` ```
## Apis ## 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 ## Servers
- `router`: route messages (you need to setup a router to be able to use `apis/net`) - `router`: route messages (you need to setup a router to be able to use `apis/net`)

View File

@ -1,3 +1,5 @@
-- eventloop 2.0.0
-- Basic event loop library for computer craft -- Basic event loop library for computer craft
-- --
-- Example usage: -- Example usage:
@ -23,7 +25,7 @@ end
local STOP = '@libeventloop/STOP_HANDLER_SUBSCRIPTION'; local STOP = '@libeventloop/STOP_HANDLER_SUBSCRIPTION';
local function createEventLoop(net) -- TODO listenMessages local function createEventLoop()
local api = {} local api = {}
local runningLoop = false; local runningLoop = false;
@ -358,16 +360,6 @@ local function createEventLoop(net) -- TODO listenMessages
end end
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 -- onStop
function api.onStop(handler) function api.onStop(handler)
assert(type(handler) == 'function', 'bad argument #1 (function expected)') assert(type(handler) == 'function', 'bad argument #1 (function expected)')

View File

@ -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_TIMEOUT_WAIT_MESSAGE = 0.5; -- in seconds
local DEFAULT_ROUTING_CHANNEL = 10; local DEFAULT_ROUTING_CHANNEL = 10;
-- Utilitaire pour savoir si un payload nous est destiné. -- Utilitaire pour savoir si un packet nous est destiné.
-- le parametre 'payload' est une table avec les champs suivants: -- le parametre 'packet' est une table avec les champs suivants:
-- - sourceId: l'id de la machine qui a envoyé le message -- - 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 -- - 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 -- - 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) -- - message: le contenu du message (qui sera le plus souvent une table)
-- return un boolean -- return un boolean
local function isPayloadOk(payload) local function isPacketOk(packet)
if type(payload) ~= "table" then if type(packet) ~= "table" then
return false; return false;
end end
if not payload.routerId or not payload.sourceId then if not packet.routerId or not packet.sourceId then
return false; return false;
end end
if payload.sourceId == os.getComputerID() then if packet.sourceId == os.getComputerID() then
return false; return false;
end end
if payload.destId == nil then if packet.destId == nil then
return true; return true;
end 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; return true;
end 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; return true;
end end
@ -69,24 +71,27 @@ end
-- --
-- local createNet = require('apis/net'); -- local createNet = require('apis/net');
-- net = createNet(); -- 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 local function createNetwork(el, modem, routingChannel, timeoutInSec)
-- net.send(9, 'ping'); el = el or createEventLoop();
--
-- -- 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)
modem = modem or peripheral.find("modem") or error("modem not found"); modem = modem or peripheral.find("modem") or error("modem not found");
routingChannel = routingChannel or DEFAULT_ROUTING_CHANNEL; routingChannel = routingChannel or DEFAULT_ROUTING_CHANNEL;
timeoutInSec = timeoutInSec or DEFAULT_TIMEOUT_WAIT_MESSAGE; timeoutInSec = timeoutInSec or DEFAULT_TIMEOUT_WAIT_MESSAGE;
local function openChannel(chan)
return modem.open(chan);
end
-- net.send function -- net.send function
local function send(channel, message, destId) local function sendRaw(channel, message, destId)
local payload = { local packet = {
sourceId = os.getComputerID(), sourceId = os.getComputerID(),
sourceLabel = os.getComputerLabel(), sourceLabel = os.getComputerLabel(),
routerId = nil, routerId = nil,
@ -94,51 +99,151 @@ local function createNetwork(modem, routingChannel, timeoutInSec)
message = message message = message
} }
modem.transmit(routingChannel, channel, payload); return modem.transmit(routingChannel, channel, packet);
end end
-- net.waitMessage function local function listenRaw(channel, handler)
local function waitMessage(channelToListen) openChannel(channel);
local receivedPayload;
modem.open(channelToListen); return el.register('modem_message', function(_, _, replyChannel, packet)
if isPacketOk(packet) and channel == replyChannel then
local timerId = os.startTimer(timeoutInSec); handler(packet.message, packet);
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;
end end
until channelOk and payloadOk or timedOut end)
if timedOut then
return nil;
end
if receivedPayload then
return receivedPayload.message, receivedPayload;
end
return nil;
end end
local function openChannel(chan) local function send(channel, eventType, payload, destId)
modem.open(chan); 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 end
return { return {
sendRaw = sendRaw,
listenRaw = listenRaw,
send = send, send = send,
waitMessage = waitMessage, listen = listen,
isPayloadOk = isPayloadOk, sendRequest = sendRequest,
sendMultipleRequests = sendMultipleRequests,
listenRequest = listenRequest,
createRequest = createRequest,
createEvent = createEvent,
isPacketOk = isPacketOk,
openChannel = openChannel, openChannel = openChannel,
events = el,
start = start,
stop = stop,
} }
end end

View File

@ -1,12 +1,15 @@
local LIST_FILES = { local LIST_FILES = {
'ping.lua',
'ping-server.lua',
'router.lua',
'startup/servers.lua', 'startup/servers.lua',
'servers/ping-server.lua',
'ping.lua',
'router.lua',
'apis/net.lua', 'apis/net.lua',
'apis/eventloop.lua', 'apis/eventloop.lua',
}; };
-- remove old files
fs.delete('ping-server.lua')
local REPO_PREFIX = 'https://raw.githubusercontent.com/guillaumearm/cc-libs/master/' local REPO_PREFIX = 'https://raw.githubusercontent.com/guillaumearm/cc-libs/master/'
local previousDir = shell.dir() local previousDir = shell.dir()

View File

@ -1,4 +1,4 @@
-- ping v1.2.0 -- ping v2.0.0
local PING_CHANNEL = 9; local PING_CHANNEL = 9;
local createNet = require('apis/net'); local createNet = require('apis/net');
@ -7,7 +7,6 @@ local net = createNet();
local args = table.pack(...); local args = table.pack(...);
local targetComputerId = tonumber(args[1]) or args[1]; local targetComputerId = tonumber(args[1]) or args[1];
local sourceId = os.getComputerID() local sourceId = os.getComputerID()
local sourceLabel = os.getComputerLabel(); local sourceLabel = os.getComputerLabel();
@ -18,16 +17,20 @@ if targetComputerId == nil or targetComputerId == sourceId or targetComputerId =
end end
-- envoyer un message sur le canal 9 à la machine cible -- 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 local ok, results, packets = net.sendMultipleRequests(PING_CHANNEL, 'ping', 'ping', targetComputerId);
while true do
local message, payload = net.waitMessage(PING_CHANNEL);
if message == "pong" then if not ok then
print("=> pong from " .. tostring(payload.sourceId) error(results)
.. (payload.sourceLabel and " (label=" .. tostring(payload.sourceLabel) .. ")" or "")); end
elseif message == nil then
break 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
end end

View File

@ -1,4 +1,4 @@
-- router v1.1.0 -- router v1.2.0
local ROUTER_CHANNEL = 10; local ROUTER_CHANNEL = 10;
local VERBOSE = true; local VERBOSE = true;
@ -6,10 +6,17 @@ local VERBOSE = true;
local modem = peripheral.find("modem") or error("modem not found"); local modem = peripheral.find("modem") or error("modem not found");
modem.open(ROUTER_CHANNEL); modem.open(ROUTER_CHANNEL);
print('started router on port ' .. tostring(ROUTER_CHANNEL) .. '...')
local routerId = os.getComputerID(); local routerId = os.getComputerID();
local function isPingForServer(payload) 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; return false;
end end
@ -44,7 +51,7 @@ while true do
sourceLabel = os.getComputerLabel(), sourceLabel = os.getComputerLabel(),
routerId = routerId, routerId = routerId,
destId = payload.sourceId, destId = payload.sourceId,
message = 'pong' message = { type = "ping_response", payload = "pong" }
} }
modem.transmit(replyChannel, replyChannel, responseRouterPayload) modem.transmit(replyChannel, replyChannel, responseRouterPayload)
end end

View File

@ -1,10 +1,10 @@
-- ping-server v1.0.0 -- ping-server v2.0.0
-- -- Example: implementation simple de ping-server -- -- Example: implementation simple de ping-server
local PING_CHANNEL = 9; local PING_CHANNEL = 9;
local MODEM_DETECTION_TIME = 3; -- in seconds local MODEM_DETECTION_TIME = 3; -- in seconds
local createNet = require('apis/net'); local createNet = require('/apis/net');
local modem = peripheral.find('modem'); local modem = peripheral.find('modem');
@ -18,13 +18,13 @@ while not modem do
os.sleep(MODEM_DETECTION_TIME); os.sleep(MODEM_DETECTION_TIME);
end end
local net = createNet(modem); local net = createNet(nil, modem);
while true do
local message, payload = net.waitMessage(PING_CHANNEL);
net.listenRequest(PING_CHANNEL, 'ping', function(message, reply)
if message == 'ping' then if message == 'ping' then
-- le troisième parametre de la fonction `net.send` est pour router un message a une machine specifique reply('pong');
net.send(PING_CHANNEL, 'pong', payload.sourceId);
end end
end end)
net.start();

View File

@ -1,7 +1,7 @@
-- Server Launcher v1.0.0 -- Server Launcher v1.0.0
local SERVERS = { local SERVERS = {
"ping-server", "servers/ping-server",
}; };
local function shellFn() local function shellFn()