2021-12-20 01:55:30 +01:00
|
|
|
package kernel.net;
|
|
|
|
|
2022-02-22 01:47:55 +01:00
|
|
|
using tink.CoreApi;
|
|
|
|
|
2021-12-20 01:55:30 +01:00
|
|
|
import kernel.peripherals.Peripherals.Peripheral;
|
|
|
|
import kernel.Log;
|
|
|
|
import kernel.KernelEvents;
|
|
|
|
import haxe.Exception;
|
|
|
|
import util.Promise;
|
|
|
|
import kernel.Timer;
|
|
|
|
import util.EventBus;
|
|
|
|
import cc.OS;
|
|
|
|
|
|
|
|
using Lambda;
|
|
|
|
using util.Extender.LambdaExtender;
|
|
|
|
|
|
|
|
/**
|
|
|
|
Class responsible for everything network related.
|
|
|
|
Used to send and recceive packages.
|
|
|
|
**/
|
2022-02-21 15:35:37 +01:00
|
|
|
class Net {
|
2022-02-21 01:50:19 +01:00
|
|
|
public static var instance:Net;
|
2021-12-20 01:55:30 +01:00
|
|
|
|
2022-02-22 01:47:55 +01:00
|
|
|
public final onNewNeigbor: Signal<Int>;
|
|
|
|
private final onNewNeigborTrigger: SignalTrigger<Int> = Signal.trigger();
|
|
|
|
|
2022-02-21 01:50:19 +01:00
|
|
|
@:allow(kernel.Init)
|
2022-02-21 15:35:37 +01:00
|
|
|
private function new() {
|
2022-02-22 01:47:55 +01:00
|
|
|
onNewNeigbor = onNewNeigborTrigger.asSignal();
|
2022-02-21 15:35:37 +01:00
|
|
|
KernelEvents.instance.onModemMessage.handle(params -> {
|
|
|
|
var pack:Package = {
|
2022-02-21 15:17:38 +01:00
|
|
|
fromID: params.replyChannel,
|
|
|
|
toID: params.channel,
|
|
|
|
msgID: params.message.msgID,
|
|
|
|
type: params.message.type,
|
|
|
|
data: params.message.data,
|
|
|
|
};
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
handelIncomming(pack, params.addr);
|
2021-12-20 01:55:30 +01:00
|
|
|
});
|
2022-02-21 15:17:38 +01:00
|
|
|
|
2021-12-20 01:55:30 +01:00
|
|
|
allModems = Peripheral.instance.getModems();
|
|
|
|
open();
|
|
|
|
discoverNeighbors();
|
|
|
|
}
|
2022-02-21 15:35:37 +01:00
|
|
|
|
2022-02-21 01:50:19 +01:00
|
|
|
public static inline final BRODCAST_PORT:Int = 65533;
|
|
|
|
public static inline final MESSAGE_TIMEOUT:Int = 3;
|
|
|
|
|
|
|
|
private var networkID:Int = OS.getComputerID();
|
2022-02-21 15:35:37 +01:00
|
|
|
private var responseBus:util.EventBus<Package> = new EventBus();
|
|
|
|
private var protoHandlers:Map<String, Package->Void> = new Map();
|
2022-02-21 01:50:19 +01:00
|
|
|
private var allModems:Array<kernel.peripherals.Modem>;
|
2022-02-21 15:35:37 +01:00
|
|
|
private var routingTable:Map<Int, kernel.peripherals.Modem> = new Map();
|
2021-12-20 01:55:30 +01:00
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function handelIncomming(pack:Package, ?addr:String) {
|
2021-12-20 01:55:30 +01:00
|
|
|
switch pack.type {
|
|
|
|
case Data(_):
|
|
|
|
routeTo(pack);
|
|
|
|
case DataNoResponse(_):
|
|
|
|
routeTo(pack);
|
|
|
|
case Response | RouteDiscoverResponse:
|
2022-02-21 15:35:37 +01:00
|
|
|
responseBus.emit(Std.string(pack.msgID), pack);
|
2021-12-20 01:55:30 +01:00
|
|
|
case RouteDiscover:
|
2022-02-21 15:35:37 +01:00
|
|
|
handleRoute(pack, addr);
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function newRoutPackage():Package {
|
|
|
|
var pack:Package = {
|
2021-12-20 01:55:30 +01:00
|
|
|
type: RouteDiscover,
|
|
|
|
toID: BRODCAST_PORT,
|
|
|
|
msgID: generateMessageID(),
|
|
|
|
fromID: networkID,
|
|
|
|
data: null,
|
|
|
|
}
|
|
|
|
|
|
|
|
return pack;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function discoverNeighbors() {
|
|
|
|
for (modem in allModems) {
|
|
|
|
var pack = newRoutPackage();
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
var timeout:Timer = null;
|
|
|
|
var responeListner = responseBus.on(Std.string(pack.msgID), pack -> {
|
|
|
|
addRoute(pack.fromID, modem.addr);
|
2021-12-20 01:55:30 +01:00
|
|
|
});
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
timeout = new Timer(MESSAGE_TIMEOUT, () -> {
|
2021-12-20 01:55:30 +01:00
|
|
|
responseBus.removeListner(responeListner);
|
|
|
|
});
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
modem.transmit(BRODCAST_PORT, OS.getComputerID(), pack);
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function handleRoute(pack:Package, addr:String) {
|
|
|
|
addRoute(pack.fromID, addr);
|
|
|
|
|
2021-12-20 01:55:30 +01:00
|
|
|
// Respond to peer
|
2022-02-21 15:35:37 +01:00
|
|
|
var response:Package = {
|
2021-12-20 01:55:30 +01:00
|
|
|
toID: pack.fromID,
|
|
|
|
fromID: OS.getComputerID(),
|
|
|
|
msgID: pack.msgID,
|
|
|
|
type: RouteDiscoverResponse,
|
|
|
|
data: null
|
|
|
|
}
|
|
|
|
|
|
|
|
for (reponseModem in allModems.filter(m -> m.addr == addr)) {
|
2022-02-21 15:35:37 +01:00
|
|
|
reponseModem.transmit(pack.fromID, networkID, response);
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function addRoute(toID:Int, addr:String) {
|
|
|
|
Log.debug("Added new route to " + toID + " via " + addr);
|
2022-02-22 01:47:55 +01:00
|
|
|
this.onNewNeigborTrigger.trigger(toID);
|
2022-02-21 15:35:37 +01:00
|
|
|
routingTable.set(toID, allModems.find(item -> item.addr == addr));
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function getAllNeighbors():Array<Int> {
|
2021-12-20 01:55:30 +01:00
|
|
|
return routingTable.mapi((index, item) -> index);
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function generateMessageID():Int {
|
2021-12-20 01:55:30 +01:00
|
|
|
return Std.random(2147483647); // TODO: better uniqe number
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Open all wireless and wired modem.
|
|
|
|
**/
|
|
|
|
private function open() {
|
|
|
|
for (m in allModems) {
|
|
|
|
m.open(networkID);
|
|
|
|
m.open(BRODCAST_PORT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Send a message. Dont care if its reaches its destination nor it has a response.
|
|
|
|
**/
|
2022-02-21 15:35:37 +01:00
|
|
|
public function sendAndForget(dest:Int, proto:String, data:Dynamic) {
|
|
|
|
var pack:Package = {
|
2021-12-20 01:55:30 +01:00
|
|
|
toID: dest,
|
|
|
|
fromID: networkID,
|
|
|
|
msgID: generateMessageID(),
|
|
|
|
type: DataNoResponse(proto),
|
|
|
|
data: data
|
|
|
|
}
|
|
|
|
|
|
|
|
sendRaw(pack);
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function respondTo(pack:Package, data:Dynamic) {
|
|
|
|
if (pack.type.match(DataNoResponse(_))) {
|
2021-12-20 01:55:30 +01:00
|
|
|
Log.warn("Responed to a no response package. Ignoring");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var response = pack.createResponse(data);
|
|
|
|
|
|
|
|
sendRaw(response);
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function routeTo(pack:Package) {
|
|
|
|
var proto:String = switch pack.type {
|
2021-12-20 01:55:30 +01:00
|
|
|
case Data(proto):
|
|
|
|
proto;
|
|
|
|
case DataNoResponse(proto):
|
|
|
|
proto;
|
|
|
|
case _:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
if (!protoHandlers.exists(proto) && protoHandlers[proto] != null) {
|
2021-12-20 01:55:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protoHandlers[proto](pack);
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function sendRaw(pack:Package) {
|
|
|
|
if (pack.toID == networkID) {
|
2021-12-20 01:55:30 +01:00
|
|
|
// Loopback
|
|
|
|
handelIncomming(pack);
|
2022-02-21 15:35:37 +01:00
|
|
|
} else {
|
|
|
|
if (routingTable.exists(pack.toID)) {
|
|
|
|
routingTable[pack.toID].transmit(pack.toID, pack.fromID, pack);
|
|
|
|
} else {
|
2021-12-20 01:55:30 +01:00
|
|
|
// Route not found
|
|
|
|
// TODO: forward package or report not reachable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function sendAndAwait(dest:Int, proto:String, data:Dynamic):Promise<Package> {
|
2021-12-20 01:55:30 +01:00
|
|
|
return new Promise<Package>((resolve, reject) -> {
|
2022-02-21 15:35:37 +01:00
|
|
|
var pack:Package = {
|
2021-12-20 01:55:30 +01:00
|
|
|
toID: dest,
|
|
|
|
fromID: networkID,
|
|
|
|
msgID: generateMessageID(),
|
|
|
|
type: Data(proto),
|
|
|
|
data: data
|
|
|
|
}
|
2022-02-21 15:35:37 +01:00
|
|
|
|
|
|
|
var timeout:Timer = null;
|
|
|
|
var responeListner = responseBus.once(Std.string(pack.msgID), p -> {
|
2021-12-20 01:55:30 +01:00
|
|
|
resolve(p);
|
2022-02-21 15:35:37 +01:00
|
|
|
if (timeout != null) {
|
2021-12-20 01:55:30 +01:00
|
|
|
timeout.cancle();
|
|
|
|
}
|
|
|
|
});
|
2022-02-21 15:35:37 +01:00
|
|
|
|
|
|
|
timeout = new Timer(MESSAGE_TIMEOUT, () -> {
|
2021-12-20 01:55:30 +01:00
|
|
|
responseBus.removeListner(responeListner);
|
|
|
|
reject(new Exception("Timeout"));
|
|
|
|
});
|
2022-02-21 15:35:37 +01:00
|
|
|
|
2021-12-20 01:55:30 +01:00
|
|
|
sendRaw(pack);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function registerProto(proto:String, cb:Package->Void) {
|
|
|
|
if (protoHandlers.exists(proto)) {
|
2021-12-20 01:55:30 +01:00
|
|
|
// Failed. Handler already exist.
|
|
|
|
// TODO: return error
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protoHandlers[proto] = cb;
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function removeProto(proto:String) {
|
2021-12-20 01:55:30 +01:00
|
|
|
protoHandlers.remove(proto);
|
|
|
|
}
|
|
|
|
}
|