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/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`)

View File

@ -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)')

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_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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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();

View File

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