diff --git a/src/bin/HelloWorld.hx b/src/bin/HelloWorld.hx index 789626c..0a37dd4 100644 --- a/src/bin/HelloWorld.hx +++ b/src/bin/HelloWorld.hx @@ -1,7 +1,6 @@ package bin; import lib.TermHandle; -import kernel.fs.FileHandler.WriteHandle; import lib.CLIBase; class HelloWorld extends CLIBase { diff --git a/src/bin/Net.hx b/src/bin/Net.hx new file mode 100644 index 0000000..e21c6e3 --- /dev/null +++ b/src/bin/Net.hx @@ -0,0 +1,76 @@ +package bin; + +import kernel.peripherals.Peripherals.Peripheral; +import kernel.net.Routing; +import haxe.ds.ReadOnlyArray; +import lib.TermHandle; +import lib.CLIBase; + +class Net extends CLIBase { + private var handle:TermHandle; + public function invoke(handle:TermHandle):Bool { + this.handle = handle; + + var subcommand = handle.args[0]; + var subcommand_args = handle.args.slice(1); + + switch (subcommand) { + case "route": + return route(subcommand_args); + case "iface": + return iface(subcommand_args); + case "help": + printHelp(); + return true; + case "ping": + ping(subcommand_args); + default: + handle.writeLn("Unknown subcommand: " + subcommand); + printHelp(); + return false; + } + + return true; + } + + private function printHelp() { + handle.writeLn("net route"); + handle.writeLn("net iface"); + handle.writeLn("net help"); + } + + private function route(args:ReadOnlyArray):Bool { + var routes = Routing.instance.getRouteTable(); + + for(k => v in routes) { + handle.writeLn('${k} => ${v.interf.name()}(${v.cost})'); + } + + return true; + } + + private function iface(args:ReadOnlyArray):Bool { + var modems = Peripheral.instance.getModems(); + + for (modem in modems) { + handle.writeLn(modem.name()); + } + + return true; + } + + function ping(args:ReadOnlyArray) { + if (args.length != 1) { + handle.writeLn("Usage: net ping id"); + } + + var toID:Null = Std.parseInt(args[0]); + + if (toID == null) { + handle.writeLn("Invalid ID"); + } + + // var result = kernel.net.Net.instance.ping(toID); + + } +} diff --git a/src/bin/Terminal.hx b/src/bin/Terminal.hx index 85ef723..7f724a3 100644 --- a/src/bin/Terminal.hx +++ b/src/bin/Terminal.hx @@ -139,6 +139,8 @@ class Terminal { switch (name) { case "hello": return new HelloWorld(); + case "net": + return new Net(); default: return null; } diff --git a/src/bin/TurtleRemote.hx b/src/bin/TurtleRemote.hx new file mode 100644 index 0000000..f84412f --- /dev/null +++ b/src/bin/TurtleRemote.hx @@ -0,0 +1,68 @@ +package bin; + +import lib.ui.reactive.TurtleController; +import util.ObservableValue; +import kernel.turtle.Turtle; +import util.DummyObservable; +import lib.ui.reactive.TextElement; +import lib.ui.reactive.ReactiveUI; +import kernel.ui.WindowManager; +import kernel.ui.WindowContext; + +class TurtleRemote { + + private var context:WindowContext; + private var ui:ReactiveUI; + + public function new() { + + } + + public function execute() { + this.context = WindowManager.instance.createNewContext(); + + // var fuelText = new ObservableValue(""); + + // function updateFuel() { + // fuelText.set('Fuel: ${Turtle.instance.getFuelLevel()} / ${Turtle.instance.getFuelLimit()}'); + // } + // updateFuel(); + + // var fuel = new TextElement(fuelText); + + + // var bForward = new TextElement(DummyObservable.dummy("Forward"),Black,White,{ + // onClick: function() { + // Turtle.instance.forward(); + // updateFuel(); + // } + // }); + + // var bBackward = new TextElement(DummyObservable.dummy("Backward"),Black,White,{ + // onClick: function() { + // Turtle.instance.back(); + // updateFuel(); + // } + // }); + + // var bLeft = new TextElement(DummyObservable.dummy("Turn left"),Black,White,{ + // onClick: function() { + // Turtle.instance.turnLeft(); + // } + // }); + + // var bRight = new TextElement(DummyObservable.dummy("Turn right"),Black,White,{ + // onClick: function() { + // Turtle.instance.turnRight(); + // } + // }); + + var ctl = new TurtleController(); + + this.ui = new ReactiveUI(this.context,[ctl]); + + this.ui.render(); + + WindowManager.instance.focusContextToOutput(context,"main"); + } +} diff --git a/src/kernel/net/Net.hx b/src/kernel/net/Net.hx index f0c8ac8..5e7b710 100644 --- a/src/kernel/net/Net.hx +++ b/src/kernel/net/Net.hx @@ -198,4 +198,21 @@ class Net { public function removeProto(proto:String) { protoHandlers.remove(proto); } + + /** + Sends a ping package to the given id. Returns true if there was a response. + **/ + public function ping(toID: NetworkID): Promise { + return new Promise((resolve,reject)->{ + this.sendAndAwait(toID,"ping",null).map(pack -> { + switch pack { + case Success(_): + resolve(true); + case Failure(err): + resolve(false); + } + }); + return null; + }); + } } diff --git a/src/kernel/net/Routing.hx b/src/kernel/net/Routing.hx index 2edd9ec..1e4cd9b 100644 --- a/src/kernel/net/Routing.hx +++ b/src/kernel/net/Routing.hx @@ -176,4 +176,8 @@ class Routing { return {interf: route.interf,rep: route.rep}; } } + + public function getRouteTable():Map { + return this.routingTable; + } } diff --git a/src/lib/ui/Dimensions.hx b/src/lib/ui/Dimensions.hx new file mode 100644 index 0000000..5e8b7b8 --- /dev/null +++ b/src/lib/ui/Dimensions.hx @@ -0,0 +1,8 @@ +package lib.ui; + +typedef Dimensions = { + public var ?top:Int; + public var ?bottom:Int; + public var ?left:Int; + public var ?right:Int; +} diff --git a/src/lib/ui/reactive/TextElement.hx b/src/lib/ui/reactive/TextElement.hx index d344628..fae0aba 100644 --- a/src/lib/ui/reactive/TextElement.hx +++ b/src/lib/ui/reactive/TextElement.hx @@ -1,40 +1,77 @@ package lib.ui.reactive; +import lib.ui.Dimensions; +import util.ObjMerge; import util.Pos; import util.Observable; -using tink.CoreApi; - import util.Color; import util.Vec.Vec2; -import util.MathI; + +using tink.CoreApi; + +typedef TextElementArgs = { + public var ?text:Observable; + public var ?simpleText: String; + public var ?bg:Color; + public var ?fg:Color; + public var ?padding: Dimensions; + public var ?margin: Dimensions; +} class TextElement extends UIElement { - private final text:Observable; - private final bg:Color; - private final fg:Color; + private var args:TextElementArgs; - public function new(text:Observable, ?background:Color = Black, ?textColor:Color = White, events: UIEvents = null) { + public function new(args: TextElementArgs,events: UIEvents = null) { super(events); + this.args = args; - this.text = text; - this.bg = background; - this.fg = textColor; + if (args.text != null) { + args.text.subscribe(value -> { + this.changedTrigger.trigger(null); + }); + }else if (args.simpleText == null) { + throw new Error("text or simpleText must be set"); + } + } - this.text.subscribe(value -> { - this.changedTrigger.trigger(null); - }); + private function getText() { + if (args.text != null) { + return args.text.get(); + } else { + return args.simpleText; + } } public function render(bounds:Vec2,offset: Pos):Canvas { var rtn = new Canvas(); - for (i in 0...MathI.min(Math.floor(text.get().length / bounds.x) + 1, bounds.y)) { - var line = (text.get().substr(i * bounds.x, bounds.x)); - for (char in 0...line.length) { - rtn.set({x: char, y: i}, {textColor: fg, char: line.charAt(char), bg: bg}); + var fullText = getText(); + + var writePoint: Pos = {x: 0,y: 0}; + + for (pos in 0...fullText.length) { + var char = fullText.charAt(pos); + + if (char == "\n"){ + writePoint = {x: 0, y: writePoint.y + 1}; + continue; } + + if (writePoint.y >= bounds.y) { + break; + } + + if (writePoint.x >= bounds.x) { + writePoint = {x: 0, y: writePoint.y + 1}; + } + + rtn.set(writePoint, {char: char,textColor: this.args.fg, bg: this.args.bg}); + + writePoint = {x: writePoint.x + 1, y: writePoint.y}; } - return rtn; + + + return UIElement.applyPaddignAndMargin(rtn, this.args.padding, this.args.margin); } } diff --git a/src/lib/ui/reactive/TurtleController.hx b/src/lib/ui/reactive/TurtleController.hx new file mode 100644 index 0000000..a3e0ea5 --- /dev/null +++ b/src/lib/ui/reactive/TurtleController.hx @@ -0,0 +1,62 @@ +package lib.ui.reactive; + +import util.Debug; +import util.Vec.Vec2; +import util.Pos; + +class TurtleController extends UIElement { + + public function new() { + super(); + } + + private function addButton(label:String): Canvas { + var txt = new TextElement({simpleText: '$label', fg: Red,bg: Orange}); + return txt.render({x:3,y:3},{x:0,y:0}); + } + + public function render(bounds:Vec2, offset:Pos):Canvas { + var canvas: Canvas = new Canvas(); + + canvas.combine(addButton("F"),{x:1,y:1}); + canvas.combine(addButton("F"),{x:5,y:2}); + + Debug.printCanvasToConsole(canvas); + + return canvas; + + } +} + +// var innerText = switch (sqr) { +// case 0: "D"; +// case 1: "F"; +// case 2: "U"; +// case 3: "L"; +// case 4: "B"; +// case 5: "R"; +// case 6: "D"; +// case 7: "D"; +// case 8: "D"; +// default: "?"; +// }; + + // canvas.set({x: offsetX + 0, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 1, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 2, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 3, y: offsetY + 0}, {char: " ",bg: spaceColor,textColor: textColor}); + + // canvas.set({x: offsetX + 0, y: offsetY + 1}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 1, y: offsetY + 1}, {char: innerText,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 2, y: offsetY + 1}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 3, y: offsetY + 1}, {char: " ",bg: spaceColor,textColor: textColor}); + + // canvas.set({x: offsetX + 0, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 1, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 2, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor}); + // canvas.set({x: offsetX + 3, y: offsetY + 2}, {char: " ",bg: spaceColor,textColor: textColor}); + + // canvas.set({x: offsetX + 0, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor}); + // canvas.set({x: offsetX + 1, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor}); + // canvas.set({x: offsetX + 2, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor}); + // canvas.set({x: offsetX + 3, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor}); diff --git a/src/lib/ui/reactive/UIElement.hx b/src/lib/ui/reactive/UIElement.hx index dc43ed0..f2dd04a 100644 --- a/src/lib/ui/reactive/UIElement.hx +++ b/src/lib/ui/reactive/UIElement.hx @@ -1,5 +1,6 @@ package lib.ui.reactive; +import util.ObjMerge; import util.Pos; import util.Rect; import util.Vec.Vec2; @@ -33,4 +34,14 @@ abstract class UIElement { return null; }; + + public static function applyPaddignAndMargin(canvas:Canvas,padding: Dimensions, margin: Dimensions):Canvas{ + var passing = ObjMerge.merge(padding, {top: 0, left: 0, bottom: 0, right: 0}); + var margin = ObjMerge.merge(margin, {top: 0, left: 0, bottom: 0, right: 0}); + var rtn = new Canvas(); + + rtn.combine(canvas,{x:margin.left + padding.left,y: margin.top + padding.top}); + + return rtn; + } } diff --git a/src/util/DummyObservable.hx b/src/util/DummyObservable.hx new file mode 100644 index 0000000..d9bdd27 --- /dev/null +++ b/src/util/DummyObservable.hx @@ -0,0 +1,27 @@ +package util; + +using tink.CoreApi; + +class DummyObservable implements Observable { + private var value:T; + + private function new(value:T) { + this.value = value; + } + + public function set(value:T) { + throw new haxe.exceptions.NotImplementedException(); + } + + public function get():T { + return this.value; + } + + public function subscribe(callback:Callback):CallbackLink { + return null; + } + + public static function dummy(value: T): Observable { + return new DummyObservable(value); + } +} diff --git a/src/util/ObjMerge.hx b/src/util/ObjMerge.hx new file mode 100644 index 0000000..52d662a --- /dev/null +++ b/src/util/ObjMerge.hx @@ -0,0 +1,24 @@ +package util; + +class ObjMerge { + public static function merge(obj1:T, obj2:T): T { + if (obj1 == null) { + return obj2; + } + + if (obj2 == null) { + return obj1; + } + + var rtn:T = Reflect.copy(obj1); + var fields = Reflect.fields(obj2); + + for (field in fields) { + if (Reflect.getProperty(obj1, field) == null) { + Reflect.setProperty(rtn, field, Reflect.getProperty(obj2, field)); + } + } + + return (rtn:T); + } +} diff --git a/src/util/Observable.hx b/src/util/Observable.hx index 44f314d..a284246 100644 --- a/src/util/Observable.hx +++ b/src/util/Observable.hx @@ -2,27 +2,8 @@ package util; using tink.CoreApi; -class Observable { - private var value:T; - private var callbacks:CallbackList = new CallbackList(); - - public function new(value:T) { - this.value = value; - } - - public function set(value:T) { - if (value != this.value) { - this.value = value; - callbacks.invoke(value); - } - } - - public function get():T { - return value; - } - - public function subscribe(callback:Callback):CallbackLink { - callback.invoke(value); - return callbacks.add(callback); - } +interface Observable { + public function set(value:T): Void; + public function get():T; + public function subscribe(callback:Callback):CallbackLink; } diff --git a/src/util/ObservableArray.hx b/src/util/ObservableArray.hx index ac95a4c..faa2850 100644 --- a/src/util/ObservableArray.hx +++ b/src/util/ObservableArray.hx @@ -1,6 +1,6 @@ package util; -class ObservableArray extends Observable> { +class ObservableArray extends ObservableValue> { public function new(value: Array) { super(value); } diff --git a/src/util/ObservableValue.hx b/src/util/ObservableValue.hx new file mode 100644 index 0000000..ea20560 --- /dev/null +++ b/src/util/ObservableValue.hx @@ -0,0 +1,28 @@ +package util; + +using tink.CoreApi; + +class ObservableValue implements Observable { + private var value:T; + private var callbacks:CallbackList = new CallbackList(); + + public function new(value:T) { + this.value = value; + } + + public function set(value:T) { + if (value != this.value) { + this.value = value; + callbacks.invoke(value); + } + } + + public function get():T { + return value; + } + + public function subscribe(callback:Callback):CallbackLink { + callback.invoke(value); + return callbacks.add(callback); + } +}