Compare commits

...

10 Commits

Author SHA1 Message Date
ec63a65ba3 use new Terminal Process in Terminal 2023-05-22 22:26:00 +02:00
ec89107262 convert Terminal to Process 2023-05-22 22:25:37 +02:00
8dca828cf3 use process on KernelLog 2023-04-18 19:57:12 +02:00
2da337b8a9 added windowContext to ProcessHandle 2023-04-18 19:56:59 +02:00
17be4149db added turtle program 2023-04-14 14:36:09 +02:00
f9aadbcbe9 added CLI helper base 2023-04-14 14:35:54 +02:00
bd3402fc39 migrated the apps to processes 2023-04-14 00:19:49 +02:00
747bde4aa6 added the concept of a process 2023-04-14 00:18:21 +02:00
655439461a updated GPS bin with INS 2023-04-13 22:26:37 +02:00
b9452cd598 implemented INS 2023-04-13 22:26:26 +02:00
20 changed files with 708 additions and 260 deletions

View File

@@ -1,19 +1,18 @@
package bin; package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
import lib.cli.TermHandle;
import lib.cli.CLIApp;
using tink.CoreApi; using tink.CoreApi;
using Lambda; using Lambda;
class Disk extends CLIApp { class Disk implements Process {
private var handle:ProcessHandle;
private var handle:TermHandle;
public function new() {} public function new() {}
public function invoke(handle:TermHandle):Future<Bool> { public function run(handle:ProcessHandle):Void {
this.handle = handle; this.handle = handle;
var subcommand = handle.args[0]; var subcommand = handle.args[0];
var driveAddr:Null<String> = handle.args[1]; var driveAddr:Null<String> = handle.args[1];
@@ -27,12 +26,12 @@ class Disk extends CLIApp {
if (drive.isDiskPresent()){ if (drive.isDiskPresent()){
if (drive.hasAudio()){ if (drive.hasAudio()){
handle.writeLn('${addr} => ${label} [AUDIO]'); handle.writeLine('${addr} => ${label} [AUDIO]');
}else{ }else{
handle.writeLn('${addr} => ${label} (${id})'); handle.writeLine('${addr} => ${label} (${id})');
} }
}else { }else {
handle.writeLn('${addr} => [NO DISK]'); handle.writeLine('${addr} => [NO DISK]');
} }
return true; return true;
@@ -41,18 +40,15 @@ class Disk extends CLIApp {
var drive = Peripheral.instance.getDrive(driveAddr); var drive = Peripheral.instance.getDrive(driveAddr);
if (drive == null){ if (drive == null){
handle.writeLn("Drive not found: " + driveAddr); handle.writeLine("Drive not found: " + driveAddr);
return Future.sync(false);
} }
if (!drive.isDiskPresent()){ if (!drive.isDiskPresent()){
handle.writeLn("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
return Future.sync(false);
} }
if (!drive.hasAudio()){ if (!drive.hasAudio()){
handle.writeLn("Disk in drive " + driveAddr + " does not have audio"); handle.writeLine("Disk in drive " + driveAddr + " does not have audio");
return Future.sync(false);
} }
drive.playAudio(); drive.playAudio();
@@ -60,18 +56,15 @@ class Disk extends CLIApp {
var drive = Peripheral.instance.getDrive(driveAddr); var drive = Peripheral.instance.getDrive(driveAddr);
if (drive == null){ if (drive == null){
handle.writeLn("Drive not found: " + driveAddr); handle.writeLine("Drive not found: " + driveAddr);
return Future.sync(false);
} }
if (!drive.isDiskPresent()){ if (!drive.isDiskPresent()){
handle.writeLn("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
return Future.sync(false);
} }
if (!drive.hasAudio()){ if (!drive.hasAudio()){
handle.writeLn("Disk in drive: " + driveAddr + " does not have audio"); handle.writeLine("Disk in drive: " + driveAddr + " does not have audio");
return Future.sync(false);
} }
drive.stopAudio(); drive.stopAudio();
@@ -80,13 +73,11 @@ class Disk extends CLIApp {
var drive = Peripheral.instance.getDrive(driveAddr); var drive = Peripheral.instance.getDrive(driveAddr);
if (drive == null){ if (drive == null){
handle.writeLn("Drive not found: " + driveAddr); handle.writeLine("Drive not found: " + driveAddr);
return Future.sync(false);
} }
if (!drive.isDiskPresent()){ if (!drive.isDiskPresent()){
handle.writeLn("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
return Future.sync(false);
} }
drive.ejectDisk(); drive.ejectDisk();
@@ -95,43 +86,39 @@ class Disk extends CLIApp {
var label:String = handle.args[2]; var label:String = handle.args[2];
if (drive == null){ if (drive == null){
handle.writeLn("Drive not found: " + driveAddr); handle.writeLine("Drive not found: " + driveAddr);
return Future.sync(false);
} }
if (!drive.isDiskPresent()){ if (!drive.isDiskPresent()){
handle.writeLn("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
return Future.sync(false);
} }
if (label == null || label == ""){ if (label == null || label == ""){
handle.writeLn(drive.getDiskLabel()); handle.writeLine(drive.getDiskLabel());
}else{ }else{
var err = drive.setDiskLabel(label); var err = drive.setDiskLabel(label);
if (err != null){ if (err != null){
handle.writeLn("Failed to set lable"); handle.writeLine("Failed to set lable");
return Future.sync(false);
} }
} }
case "help": case "help":
case null: case null:
printHelp(); printHelp();
default: default:
handle.writeLn("Unknown subcommand: " + subcommand); handle.writeLine("Unknown subcommand: " + subcommand);
printHelp(); printHelp();
return Future.sync(false);
} }
return Future.sync(true); return handle.close(true);
} }
private function printHelp() { private function printHelp() {
handle.writeLn("Usage: disk <subcommand> [args]"); handle.writeLine("Usage: disk <subcommand> [args]");
handle.writeLn("Subcommands:"); handle.writeLine("Subcommands:");
handle.writeLn(" ls"); handle.writeLine(" ls");
handle.writeLn(" play <drive>"); handle.writeLine(" play <drive>");
handle.writeLn(" stop <drive>"); handle.writeLine(" stop <drive>");
handle.writeLn(" eject <drive>"); handle.writeLine(" eject <drive>");
handle.writeLn(" label <drive> [label]"); handle.writeLine(" label <drive> [label]");
} }
} }

View File

@@ -1,18 +1,19 @@
package bin; package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.gps.INS;
import lib.Pos3; import lib.Pos3;
import lib.Vec.Vec3; import lib.Vec.Vec3;
import lib.cli.TermHandle;
import lib.cli.CLIApp;
using tink.CoreApi; using tink.CoreApi;
class GPS extends CLIApp { class GPS implements Process {
private var handle:TermHandle; private var handle:ProcessHandle;
public function new() {} public function new() {}
public function invoke(handle: TermHandle):Future<Bool> { public function run(handle: ProcessHandle):Void {
this.handle = handle; this.handle = handle;
var subcommand = handle.args[0]; var subcommand = handle.args[0];
@@ -20,19 +21,37 @@ class GPS extends CLIApp {
switch (subcommand) { switch (subcommand) {
case "set": case "set":
return Future.sync(setManuelPos(subcommand_args)); handle.close(setManuelPos(subcommand_args));
case "status": case "status":
return Future.sync(getGPSStatus()); handle.close(getGPSStatus());
case "locate": case "locate":
kernel.gps.GPS.instance.locate(); kernel.gps.GPS.instance.locate().handle((pos)->{
return Future.sync(true); if (pos != null) {
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
handle.close(true);
} else {
handle.writeLine("Position not available");
handle.close(false);
}
});
case "ins":
INS.instance.align().handle(()->{
handle.writeLine("INS aligned");
handle.close(true);
});
default: default:
handle.writeLn("Unknown subcommand: " + subcommand); handle.writeLine("Unknown subcommand: " + subcommand);
return Future.sync(false); printHelp();
handle.close(false);
}
} }
return Future.sync(true); private function printHelp(){
handle.writeLine("GPS commands:");
handle.writeLine("set <x> <y> <z> - set manual position");
handle.writeLine("status - get current position and accuracy");
handle.writeLine("locate - get current position");
handle.writeLine("ins - align INS");
} }
private function setManuelPos(args: Array<String>): Bool { private function setManuelPos(args: Array<String>): Bool {
@@ -51,19 +70,26 @@ class GPS extends CLIApp {
var pos = kernel.gps.GPS.instance.getPosition(); var pos = kernel.gps.GPS.instance.getPosition();
if (pos != null) { if (pos != null) {
handle.writeLn('Position x:${pos.x} y:${pos.y} z:${pos.z}'); handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
} else { } else {
handle.writeLn("Position not available"); handle.writeLine("Position not available");
return true; return true;
} }
var acc = kernel.gps.GPS.instance.getAccuracy(); var acc = kernel.gps.GPS.instance.getAccuracy();
if (acc == 1){ if (acc == 1){
handle.writeLn("Accuracy: Low"); handle.writeLine("Accuracy: Low");
} else if (acc == 2){ } else if (acc == 2){
handle.writeLn("Accuracy: Medium"); handle.writeLine("Accuracy: Medium");
} else if (acc == 3){ } else if (acc == 3){
handle.writeLn("Accuracy: High"); handle.writeLine("Accuracy: High");
}
var ins = INS.instance.getHeading();
if (ins != null) {
handle.writeLine('INS heading: ${ins.x} y:${ins.y} z:${ins.z}');
} else {
handle.writeLine("INS heading not available");
} }
return true; return true;

View File

@@ -1,21 +1,16 @@
package bin; package bin;
import lib.cli.TermHandle; import kernel.ps.ProcessHandle;
import lib.cli.CLIApp; import kernel.ps.Process;
using tink.CoreApi; using tink.CoreApi;
class HelloWorld extends CLIApp { class HelloWorld implements Process {
public function new() {} public function new() {}
public function invoke(handle: TermHandle):Future<Bool> { public function run(handle:ProcessHandle) {
var world:String = "world"; handle.write("Hello World!");
if (handle.args.length > 0) { handle.close();
world = handle.args[0];
}
handle.write('Hello, $world!');
return Future.sync(true);
} }
} }

View File

@@ -1,5 +1,7 @@
package bin; package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import lib.Color; import lib.Color;
import lib.MathI; import lib.MathI;
import kernel.log.Log; import kernel.log.Log;
@@ -8,24 +10,22 @@ import lib.ui.UIApp;
using tink.CoreApi; using tink.CoreApi;
class KernelLog extends UIApp{ class KernelLog implements Process {
private var handle: ProcessHandle;
private var ctx: WindowContext; private var ctx: WindowContext;
private var exitTrigger: Bool -> Void;
public function new() {} public function new() {}
public function invoke(context:WindowContext):Future<Bool> { public function run(handle: ProcessHandle):Void {
this.ctx = context; this.handle = handle;
var statelessCtx = handle.createStatelessWindowContext();
this.ctx = statelessCtx.ctx;
statelessCtx.setRenderFunc(this.render);
Log.instance.onLog.handle(()->{ Log.instance.onLog.handle(()->{
render(); statelessCtx.requestRender();
});
render();
return new Future<Bool>(cb -> {
this.exitTrigger = cb;
return null;
}); });
} }

View File

@@ -1,19 +1,19 @@
package bin; package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
import kernel.net.Routing; import kernel.net.Routing;
import haxe.ds.ReadOnlyArray; import haxe.ds.ReadOnlyArray;
import lib.cli.TermHandle;
import lib.cli.CLIApp;
using tink.CoreApi; using tink.CoreApi;
class Net extends CLIApp { class Net implements Process {
private var handle:TermHandle; private var handle:ProcessHandle;
public function new() {} public function new() {}
public function invoke(handle:TermHandle):Future<Bool> { public function run(handle:ProcessHandle):Void {
this.handle = handle; this.handle = handle;
var subcommand = handle.args[0]; var subcommand = handle.args[0];
@@ -21,86 +21,82 @@ class Net extends CLIApp {
switch (subcommand) { switch (subcommand) {
case "route": case "route":
return Future.sync(route(subcommand_args)); route(subcommand_args);
return handle.close();
case "iface": case "iface":
return Future.sync(iface(subcommand_args)); iface(subcommand_args);
return handle.close();
case "help": case "help":
printHelp(); printHelp();
return Future.sync(true); return handle.close();
case "ping": case "ping":
return ping(subcommand_args); ping(subcommand_args);
// Closes itself
case "proto": case "proto":
return Future.sync(protos()); protos();
return handle.close();
default: default:
handle.writeLn("Unknown subcommand: " + subcommand); handle.write("Unknown subcommand: " + subcommand);
printHelp(); printHelp();
return Future.sync(false); return handle.close(false);
} }
} }
private function printHelp() { private function printHelp() {
handle.writeLn("net route"); handle.write("net route");
handle.writeLn("net iface"); handle.write("net iface");
handle.writeLn("net help"); handle.write("net help");
handle.writeLn("net proto"); handle.write("net proto");
} }
private function route(args:ReadOnlyArray<String>):Bool { private function route(args:ReadOnlyArray<String>):Void {
var routes = Routing.instance.getRouteTable(); var routes = Routing.instance.getRouteTable();
for(k => v in routes) { for(k => v in routes) {
handle.writeLn('${k} => ${v.interf.name()}(${v.cost})'); handle.write('${k} => ${v.interf.name()}(${v.cost})');
} }
return true;
} }
private function iface(args:ReadOnlyArray<String>):Bool { private function iface(args:ReadOnlyArray<String>):Bool {
var modems = Peripheral.instance.getModems(); var modems = Peripheral.instance.getModems();
for (modem in modems) { for (modem in modems) {
handle.writeLn(modem.name()); handle.write(modem.name());
} }
return true; return true;
} }
function ping(args:ReadOnlyArray<String>): Future<Bool> { function ping(args:ReadOnlyArray<String>): Void {
return new Future<Bool>(trigger -> {
if (args.length != 1) { if (args.length != 1) {
handle.writeLn("Usage: net ping id"); handle.write("Usage: net ping id");
trigger(false); return handle.close(false);
return null;
} }
var toID:Null<Int> = Std.parseInt(args[0]); var toID:Null<Int> = Std.parseInt(args[0]);
if (toID == null) { if (toID == null) {
handle.writeLn("Invalid ID"); handle.write("Invalid ID");
trigger(false); return handle.close(false);
return null;
} }
kernel.net.Net.instance.ping(toID).handle(result -> { kernel.net.Net.instance.ping(toID).handle(result -> {
switch (result){ switch (result){
case Success(_): case Success(_):
handle.writeLn("Ping succeeded"); handle.write("Ping succeeded");
trigger(true); return handle.close();
case Failure(failure): case Failure(failure):
handle.writeLn("Ping failed: " + failure); handle.write("Ping failed: " + failure);
trigger(false); return handle.close(false);
} }
}); });
});
} }
function protos():Bool { function protos():Void {
var protos = kernel.net.Net.instance.getActiveProtocols(); var protos = kernel.net.Net.instance.getActiveProtocols();
for (proto in protos) { for (proto in protos) {
handle.writeLn(proto); handle.write(proto);
} }
return true;
} }
} }

View File

@@ -1,27 +1,27 @@
package bin; package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
import kernel.peripherals.Side; import kernel.peripherals.Side;
import lib.cli.TermHandle;
import lib.cli.CLIApp;
using tink.CoreApi; using tink.CoreApi;
class Redstone extends CLIApp { class Redstone implements Process {
public function new() {} public function new() {}
public function invoke(handle:TermHandle):Future<Bool> { public function run(handle: ProcessHandle):Void {
var subcommand = handle.args[0]; var subcommand = handle.args[0];
if (subcommand == null) { if (subcommand == null) {
handle.writeLn("Usage: redstone <on|off|get> <side>"); handle.write("Usage: redstone <on|off|get> <side>");
return Future.sync(false); return handle.close(false);
} }
var side:Null<Side> = handle.args[1]; var side:Null<Side> = handle.args[1];
if (side == null) { if (side == null) {
handle.writeLn("Invalid side"); handle.write("Invalid side");
return Future.sync(false); return handle.close(false);
} }
switch (subcommand) { switch (subcommand) {
@@ -31,14 +31,14 @@ class Redstone extends CLIApp {
Peripheral.instance.getRedstone(side).setOutput(false); Peripheral.instance.getRedstone(side).setOutput(false);
case "get": case "get":
var value = Peripheral.instance.getRedstone(side).getAnalogInput(); var value = Peripheral.instance.getRedstone(side).getAnalogInput();
handle.writeLn("Analog input: " + value); handle.write("Analog input: " + value);
case "help": case "help":
handle.writeLn("Usage: redstone <on|off|get> <side>"); handle.write("Usage: redstone <on|off|get> <side>");
default: default:
handle.writeLn("Invalid subcommand"); handle.write("Invalid subcommand");
return Future.sync(false); return handle.close(false);
} }
return Future.sync(true); return handle.close();
} }
} }

View File

@@ -1,51 +1,56 @@
package bin; package bin;
import lib.ui.UIApp; import kernel.ps.ProcessHandle;
import lib.cli.TermHandle; import kernel.ps.Process;
import lib.cli.CLIApp; import kernel.ps.ProcessManager;
import lib.Color; import lib.Color;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
import kernel.ui.WindowManager;
using tink.CoreApi; using tink.CoreApi;
class Terminal extends UIApp { class Terminal implements Process {
private var context:WindowContext; private var context:WindowContext;
private var input:String = ""; private var input:String = "";
private var backlog:Array<String> = []; private var backlog:Array<String> = [];
private var exitTrigger: Bool -> Void; private var handle:ProcessHandle;
private var requestRender: () -> Void;
public function new() {} public function new() {}
public function invoke(context: WindowContext): Future<Bool> { public function run(handle: ProcessHandle): Void {
this.context = context; this.handle = handle;
var statelessContext = handle.createStatelessWindowContext();
this.context = statelessContext.ctx;
this.requestRender = statelessContext.requestRender;
statelessContext.setRenderFunc(this.render);
this.context.onChar.handle(char -> { this.context.onChar.handle(char -> {
this.input += char; this.input += char;
this.redrawInput(); this.requestRender();
}); });
this.context.onKey.handle(e -> { this.context.onKey.handle(e -> {
if (e.keyCode == 259) { if (e.keyCode == 259) { // Backspace
this.input = this.input.substr(0, this.input.length - 1); this.input = this.input.substr(0, this.input.length - 1);
this.redrawInput(); this.requestRender();
} else if (e.keyCode == 257) { } else if (e.keyCode == 257) { // Enter
this.backlog.push("> " + this.input); this.backlog.push("> " + this.input);
var command = this.input; var command = this.input;
this.input = ""; this.input = "";
this.redrawBacklog(); this.requestRender();
this.redrawInput();
this.invokeCommand(command); this.invokeCommand(command);
} }
}); });
WindowManager.instance.focusContextToOutput(context, "main"); this.requestRender();
this.redrawInput(); }
return new Future<Bool>(cb -> { private function render() {
this.exitTrigger = cb; redrawBacklog();
return null; redrawInput();
});
} }
private function redrawBacklog() { private function redrawBacklog() {
@@ -99,39 +104,35 @@ class Terminal extends UIApp {
var commandArgs:Array<String> = args.slice(1); var commandArgs:Array<String> = args.slice(1);
var hadInput = false; var hadInput = false;
var handle = new TermHandle(commandArgs, {
var ps = getProgByName(commandName);
if (ps == null) {
this.backlog.push("Unknown command: " + commandName);
this.redrawBacklog();
this.redrawInput();
return;
}
ProcessManager.run(ps,{
args: commandArgs,
onWrite: (s:String) -> { onWrite: (s:String) -> {
if (!hadInput) { if (!hadInput) {
this.backlog.push(""); this.backlog.push("");
hadInput = true; hadInput = true;
} }
this.backlog[this.backlog.length - 1] += s; this.backlog[this.backlog.length - 1] += s;
this.redrawBacklog(); this.requestRender();
}, },
onNewLine: () -> { onExit: (success:Bool) -> {
this.backlog.push("");
this.redrawBacklog();
}
});
var prog:CLIApp = getProgByName(commandName);
if (prog == null) {
this.backlog.push("Command not found: " + commandName);
this.redrawBacklog();
return;
}
this.context.setCursorBlink(false);
prog.invoke(handle).handle((exitCode) -> {
// Cleanup extra newline
if (this.backlog[this.backlog.length - 1] == "") { if (this.backlog[this.backlog.length - 1] == "") {
this.backlog.pop(); this.backlog.pop();
} }
this.redrawInput(); this.requestRender();
}
}); });
this.context.setCursorBlink(false);
} }
private function parseArgs(command:String):Array<String> { private function parseArgs(command:String):Array<String> {
@@ -144,7 +145,7 @@ class Terminal extends UIApp {
this.redrawBacklog(); this.redrawBacklog();
} }
private function getProgByName(name:String):CLIApp { private function getProgByName(name:String):Process {
switch (name) { switch (name) {
case "hello": case "hello":
return new HelloWorld(); return new HelloWorld();
@@ -156,6 +157,8 @@ class Terminal extends UIApp {
return new Disk(); return new Disk();
case "gps": case "gps":
return new GPS(); return new GPS();
case "turtle":
return new Turtle();
default: default:
return null; return null;
} }

44
src/bin/Turtle.hx Normal file
View File

@@ -0,0 +1,44 @@
package bin;
import lib.CLIAppBase;
using tink.CoreApi;
class Turtle extends CLIAppBase {
public function new() {
registerSyncSubcommand("forward", (args)->{
return perform(kernel.turtle.Turtle.instance.forward());
});
registerSyncSubcommand("back", (args)->{
return perform(kernel.turtle.Turtle.instance.back());
});
registerSyncSubcommand("left", (args)->{
return perform(kernel.turtle.Turtle.instance.turnLeft());
});
registerSyncSubcommand("right", (args)->{
return perform(kernel.turtle.Turtle.instance.turnRight());
});
registerSyncSubcommand("up", (args)->{
return perform(kernel.turtle.Turtle.instance.up());
});
registerSyncSubcommand("down", (args)->{
return perform(kernel.turtle.Turtle.instance.down());
});
}
private function perform(outcome: Outcome<Noise,String>): Bool {
switch outcome {
case Success(_):
return true;
case Failure(error):
handle.write(error);
return false;
}
}
}

View File

@@ -1,5 +1,6 @@
package kernel; package kernel;
import kernel.gps.INS;
import kernel.fs.FS; import kernel.fs.FS;
import kernel.gps.GPS; import kernel.gps.GPS;
import kernel.log.Log; import kernel.log.Log;
@@ -34,6 +35,7 @@ class Init {
Net.instance = new Net(); Net.instance = new Net();
GPS.instance = new GPS(); GPS.instance = new GPS();
INS.instance = new INS();
// Register default terminate handler // Register default terminate handler
KernelEvents.instance.onTerminate.handle(_->{ KernelEvents.instance.onTerminate.handle(_->{

View File

@@ -23,6 +23,8 @@ class GPS {
private var cachedPosition:Pos3; private var cachedPosition:Pos3;
private var lastPositionResponse: Array<{pos:Pos3,dist:Float}> = []; private var lastPositionResponse: Array<{pos:Pos3,dist:Float}> = [];
private var futureResolve: (pos:Null<Pos3>) -> Void = null;
@:allow(kernel.Init) @:allow(kernel.Init)
private function new() { private function new() {
this.loadCachedPosition(); this.loadCachedPosition();
@@ -39,6 +41,12 @@ class GPS {
} }
} }
@:allow(kernel.gps.INS)
private function setINSPosition(pos:Pos3) {
cachedPosition = pos;
posAccuracy = 1;
}
public function getPosition():Null<Pos3> { public function getPosition():Null<Pos3> {
return cachedPosition; return cachedPosition;
} }
@@ -52,8 +60,18 @@ class GPS {
posAccuracy = 0; posAccuracy = 0;
} }
public function locate() { public function locate():Future<Null<Pos3>> {
// TODO: implenet a timeout
// TODO: dont send a request twice if the last one is still pending or we moved
return new Future<Null<Pos3>>((resolve)->{
this.futureResolve = resolve;
sendPositionRequest(); sendPositionRequest();
return null;
});
}
private function resolveFuture(pos:Null<Pos3>) {
this.futureResolve(pos);
} }
private function persistCachedPositon() { private function persistCachedPositon() {
@@ -124,13 +142,19 @@ class GPS {
if (lastPositionResponse.length < 4) return; // We need at least 3 responses to calculate the position if (lastPositionResponse.length < 4) return; // We need at least 3 responses to calculate the position
var calculatedPosition = calculatePosition().round(); var calculatedPosition = calculatePosition();
if (calculatedPosition != null){
calculatedPosition = calculatedPosition.round();
}
lastPositionResponse = []; // Reset the response array lastPositionResponse = []; // Reset the response array
if (calculatedPosition == null) return; if (calculatedPosition == null) return;
cachedPosition = calculatedPosition; cachedPosition = calculatedPosition;
posAccuracy = 3; posAccuracy = 3;
resolveFuture(calculatedPosition);
default: default:
} }
} }

211
src/kernel/gps/INS.hx Normal file
View File

@@ -0,0 +1,211 @@
package kernel.gps;
import kernel.log.Log;
import kernel.turtle.Turtle;
import lib.Pos3;
using tink.CoreApi;
class INS {
public static var instance:INS;
private var heading: Null<Pos3> = null;
private var alingment: Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
@:allow(kernel.Init)
private function new() {
}
@:allow(kernel.turtle.Turtle)
private function moveForward() {
if (heading == null) {
this.alingment = 0;
return;
}
move(heading);
}
@:allow(kernel.turtle.Turtle)
private function moveBackward() {
if (heading == null) {
this.alingment = 0;
return;
}
move(heading.negate());
}
@:allow(kernel.turtle.Turtle)
private function moveUp() {
move({x: 0, y: 1, z: 0});
}
@:allow(kernel.turtle.Turtle)
private function moveDown() {
move({x: 0, y: -1, z: 0});
}
@:allow(kernel.turtle.Turtle)
private function turnLeft() {
if (heading == null) return;
if (heading.x == 0 && heading.z == -1) {
heading = {x: -1, y: 0, z: 0};
} else if (heading.x == -1 && heading.z == 0) {
heading = {x: 0, y: 0, z: 1};
} else if (heading.x == 0 && heading.z == 1) {
heading = {x: 1, y: 0, z: 0};
} else if (heading.x == 1 && heading.z == 0) {
heading = {x: 0, y: 0, z: -1};
}
}
@:allow(kernel.turtle.Turtle)
private function turnRight() {
if (heading == null) return;
if (heading.x == 0 && heading.z == -1) {
heading = {x: 1, y: 0, z: 0};
} else if (heading.x == -1 && heading.z == 0) {
heading = {x: 0, y: 0, z: -1};
} else if (heading.x == 0 && heading.z == 1) {
heading = {x: -1, y: 0, z: 0};
} else if (heading.x == 1 && heading.z == 0) {
heading = {x: 0, y: 0, z: 1};
}
}
private function move(dir: Null<Pos3>) {
Log.debug('INS move: $dir');
var pos = GPS.instance.getPosition();
var newPos = pos + dir;
GPS.instance.setINSPosition(newPos);
}
public function getHeading():Null<Pos3> {
return heading;
}
public function align(): Promise<Noise> {
Log.info("Aligning INS");
return new Promise<Noise>((resolve,reject)->{
if (Turtle.instance.getFuelLevel() < 2){
Log.warn("Not enough fuel to align");
reject(new Error("Not enough fuel to align"));
return null;
}
GPS.instance.locate().handle((pos1)->{
Log.debug('pos1: $pos1');
if (pos1 == null) {
Log.warn("GPS not available for 1st position");
reject(new Error("GPS not available"));
return;
}
var moved = tryMoving();
if (moved == -1) {
Log.warn("Can't move");
reject(new Error("Can't move"));
return;
}
GPS.instance.locate().handle((pos2)->{
Log.debug('pos2: $pos2');
if (pos2 == null) {
Log.warn("GPS not available for 2nd position");
reject(new Error("GPS not available for 2nd position"));
return;
}
var heading = calcHeading(pos1,pos2,moved);
if (heading == null) {
Log.error("Can't calculate heading");
reject(new Error("Can't calculate heading"));
return;
}
this.heading = heading;
moveBack(moved);
GPS.instance.setINSPosition(pos1);
resolve(Noise);
});
});
return null;
});
}
// -1 = not moved, 0 = back, 1 = forward, 2 = left, 3 = right
private function tryMoving():Int {
if (Turtle.instance.back().isSuccess()) {
return 0;
} else if (Turtle.instance.forward().isSuccess()) {
return 1;
} else {
Turtle.instance.turnLeft(); // TODO: Check if successfull
if (Turtle.instance.forward().isSuccess()){
return 2;
} else if (Turtle.instance.back().isSuccess()) {
return 3;
} else {
// Can't move
return -1;
}
}
}
private function calcHeading(pos1: Pos3,pos2:Pos3,moved:Int): Null<Pos3> {
if (moved == 0) {
return pos1 - pos2;
} else if (moved == 1) {
return pos2 - pos1;
} else if (moved == 2) {
return rotatePos3ToRight(pos2 - pos1);
} else if (moved == 3) {
return rotatePos3ToLeft(pos2 - pos1);
} else {
return null;
}
}
private function moveBack(moved:Int) {
if (moved == 0) {
Turtle.instance.forward();
// cc.Turtle.forward();
} else if (moved == 1) {
Turtle.instance.back();
// cc.Turtle.back();
} else if (moved == 2) {
Turtle.instance.back();
// cc.Turtle.back();
Turtle.instance.turnRight();
// cc.Turtle.turnRight();
} else if (moved == 3) {
Turtle.instance.forward();
// cc.Turtle.forward();
Turtle.instance.turnRight();
// cc.Turtle.turnRight();
}
}
private function rotatePos3ToRight(pos:Pos3):Pos3 {
if (pos.x == 0 && pos.z == -1) {
return {x: 1, y: 0, z: 0};
} else if (pos.x == -1 && pos.z == 0) {
return {x: 0, y: 0, z: -1};
} else if (pos.x == 0 && pos.z == 1) {
return {x: -1, y: 0, z: 0};
} else if (pos.x == 1 && pos.z == 0) {
return {x: 0, y: 0, z: 1};
} else {
return pos;
}
}
private function rotatePos3ToLeft(pos3:Pos3):Pos3 {
return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3)));
}
}

8
src/kernel/ps/Process.hx Normal file
View File

@@ -0,0 +1,8 @@
package kernel.ps;
/**
Defines an independent process that can be run by the kernel.
**/
interface Process {
public function run(handle: ProcessHandle): Void;
}

View File

@@ -0,0 +1,82 @@
package kernel.ps;
import kernel.ps.ProcessManager.PID;
import kernel.ui.WindowContext;
import kernel.ui.WindowManager;
import haxe.ds.ReadOnlyArray;
using tink.CoreApi;
typedef HandleConfig = {
?args: Array<String>,
?onWrite: Callback<String>,
?onExit: Callback<Bool>,
}
class ProcessHandle {
public var args(get,null): ReadOnlyArray<String>;
private final pid: PID;
private final config:HandleConfig;
private final closeFuture: Future<Bool>;
private var closeFutureResolev: Bool -> Void;
private final windowContexts: Array<WindowContext> = [];
@:allow(kernel.ps.ProcessManager)
private function new(config: HandleConfig,pid: PID) {
this.config = config;
this.pid = pid;
this.closeFuture = new Future<Bool>((trigger)->{
this.closeFutureResolev = trigger;
return null;
});
if (this.config.onExit != null) {
this.closeFuture.handle(this.config.onExit);
}
this.closeFuture.eager();
}
public function onExit(): Future<Bool> {
return this.closeFuture;
}
public function close(success: Bool = true): Void {
this.dispose();
this.closeFutureResolev(success);
ProcessManager.removeProcess(this.pid);
}
public function write(message: String): Void {
this.config.onWrite.invoke(message);
}
public function writeLine(message: String): Void {
this.write(message + "\n");
}
public function createBufferdWindowContext(): WindowContext {
var ctx = WindowManager.instance.createNewContext();
this.windowContexts.push(ctx);
return ctx;
}
public function createStatelessWindowContext(): {ctx:WindowContext, setRenderFunc: (() -> Void) -> Void, requestRender:() -> Void} {
var ctx = WindowManager.instance.createNewStatelessContext();
this.windowContexts.push(ctx.ctx);
return ctx;
}
public function getWindowContexts(): ReadOnlyArray<WindowContext> {
return this.windowContexts;
}
private function dispose() {
// TODO
}
function get_args():ReadOnlyArray<String> {
return this.config.args;
}
}

View File

@@ -0,0 +1,37 @@
package kernel.ps;
import kernel.ps.ProcessHandle.HandleConfig;
typedef PID = Int;
class ProcessManager {
private static final processList = new Map<PID,ProcessHandle>();
public static function run(process:Process, config: HandleConfig):PID {
var pid = createPID();
var handle = new ProcessHandle(config, pid);
processList.set(pid, handle);
process.run(handle);
return pid;
}
private static function createPID(): PID {
// TODO: better PID generation
// generate a random PID
return Math.ceil(Math.random() * 1000000);
}
@:allow(kernel.ui.WindowManager)
private static function getProcess(pid:PID):Null<ProcessHandle>{
return processList.get(pid);
}
@:allow(kernel.ps.ProcessHandle)
private static function removeProcess(pid:PID):Void {
processList.remove(pid);
}
}

View File

@@ -2,6 +2,7 @@ package kernel.turtle;
import kernel.log.Log; import kernel.log.Log;
import kernel.turtle.Types; import kernel.turtle.Types;
import kernel.gps.INS;
using tink.CoreApi; using tink.CoreApi;
@@ -34,32 +35,44 @@ class Turtle {
public function forward():Outcome<Noise, String> { public function forward():Outcome<Noise, String> {
var r = cc.Turtle.forward(); var r = cc.Turtle.forward();
return conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveForward();
return r2;
} }
public function back():Outcome<Noise, String> { public function back():Outcome<Noise, String> {
var r = cc.Turtle.back(); var r = cc.Turtle.back();
return conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveBackward();
return r2;
} }
public function up():Outcome<Noise, String> { public function up():Outcome<Noise, String> {
var r = cc.Turtle.up(); var r = cc.Turtle.up();
return conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveUp();
return r2;
} }
public function down():Outcome<Noise, String> { public function down():Outcome<Noise, String> {
var r = cc.Turtle.down(); var r = cc.Turtle.down();
return conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveDown();
return r2;
} }
public function turnLeft():Outcome<Noise, String> { public function turnLeft():Outcome<Noise, String> {
var r = cc.Turtle.turnLeft(); var r = cc.Turtle.turnLeft();
return conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.turnRight();
return r2;
} }
public function turnRight():Outcome<Noise, String> { public function turnRight():Outcome<Noise, String> {
var r = cc.Turtle.turnRight(); var r = cc.Turtle.turnRight();
return conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.turnRight();
return r2;
} }
public function dig(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> { public function dig(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {

View File

@@ -1,5 +1,7 @@
package kernel.ui; package kernel.ui;
import kernel.ps.ProcessManager;
import kernel.ps.ProcessManager.PID;
import haxe.ds.ReadOnlyArray; import haxe.ds.ReadOnlyArray;
import kernel.ui.TermWriteable; import kernel.ui.TermWriteable;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
@@ -162,4 +164,13 @@ class WindowManager {
context.setTarget(target); context.setTarget(target);
context.enable(); context.enable();
} }
public function getContextByPID(pid: PID): ReadOnlyArray<WindowContext> {
var handle = ProcessManager.getProcess(pid);
if (handle == null) {
return [];
}
return handle.getWindowContexts();
}
} }

58
src/lib/CLIAppBase.hx Normal file
View File

@@ -0,0 +1,58 @@
package lib;
import kernel.ps.Process;
import kernel.ps.ProcessHandle;
using tink.CoreApi;
abstract class CLIAppBase implements Process {
private var handle: ProcessHandle;
private final _subcommandsSync: Map<String, (Array<String>) -> Bool> = [];
private final _subcommandsAsync: Map<String, (Array<String>) -> Future<Bool>> = [];
private final _subcommandsSynopsis: Array<String> = [];
public function run(handle: ProcessHandle){
this.handle = handle;
var subcommand = handle.args[0];
if (subcommand == null || subcommand == "") {
handle.writeLine("No subcommand specified");
printHelp();
return handle.close(false);
}
var args = handle.args.slice(1);
if (_subcommandsSync.exists(subcommand)) {
var result = _subcommandsSync[subcommand](args);
return handle.close(result);
} else if (_subcommandsAsync.exists(subcommand)) {
_subcommandsAsync[subcommand](args).handle(handle.close);
} else {
handle.writeLine("Unknown subcommand: " + subcommand);
printHelp();
return handle.close(false);
}
}
private function registerSyncSubcommand(command: String, callback: (Array<String>) -> Bool, synopsis: String = null) {
_subcommandsSync.set(command, callback);
_subcommandsSynopsis.push(command + " " + synopsis);
}
private function registerAsyncSubcommand(command: String, callback: (Array<String>) -> Future<Bool>, synopsis: String = null) {
_subcommandsAsync.set(command, callback);
_subcommandsSynopsis.push(command + " " + synopsis);
}
private function printHelp() {
handle.writeLine("Usage: <subcommand> [args]");
handle.writeLine("Subcommands:");
for (subcommand in _subcommandsSynopsis) {
handle.writeLine(" " + subcommand);
}
}
}

View File

@@ -1,5 +1,7 @@
package lib; package lib;
import kernel.ps.Process;
import kernel.ps.ProcessManager;
import bin.KernelLog; import bin.KernelLog;
import lib.ui.elements.UIElement; import lib.ui.elements.UIElement;
import lib.ui.elements.TextElement; import lib.ui.elements.TextElement;
@@ -88,33 +90,18 @@ class HomeContext {
focusContext(contextId); focusContext(contextId);
} }
private function addTerminal(){ private function spawnPs(ps: Process) {
var newContext = WindowManager.instance.createNewBufferedContext(); var pid = ProcessManager.run(ps, {});
var lastContextID = -1;
// Create new terminal for ( ctx in WindowManager.instance.getContextByPID(pid)){
var term = new Terminal(); lastContextID = addContextNextWorkspace(ctx);
term.invoke(newContext); }
var contextID = addContextNextWorkspace(newContext); if (lastContextID == -1) {
if (contextID == -1) {
return; return;
} }
focusContext(contextID); focusContext(lastContextID);
}
private function addLog(){
var newContext = WindowManager.instance.createNewBufferedContext();
// Create new terminal
var term = new KernelLog();
term.invoke(newContext);
var contextID = addContextNextWorkspace(newContext);
if (contextID == -1) {
return;
}
focusContext(contextID);
} }
private function render() { private function render() {
@@ -128,8 +115,8 @@ class HomeContext {
for (i in workspaceIDs) new TextElement('Switch to ${i + 1}', {onClick: this.handleSelectContext.bind(i)}) for (i in workspaceIDs) new TextElement('Switch to ${i + 1}', {onClick: this.handleSelectContext.bind(i)})
]; ];
children.push(new TextElement('Add Terminal', {onClick: this.addTerminal})); children.push(new TextElement('Add Terminal', {onClick: this.spawnPs.bind(new Terminal())}));
children.push(new TextElement('Add Log', {onClick: this.addLog})); children.push(new TextElement('Add Log', {onClick: this.spawnPs.bind(new KernelLog())}));
children.push(new TextElement('Exit', {onClick: KernelEvents.instance.shutdown})); children.push(new TextElement('Exit', {onClick: KernelEvents.instance.shutdown}));
renderer.setChildren(children); renderer.setChildren(children);

View File

@@ -1,7 +0,0 @@
package lib.cli;
using tink.CoreApi;
abstract class CLIApp {
public abstract function invoke(handle: TermHandle): Future<Bool>;
}

View File

@@ -1,29 +0,0 @@
package lib.cli;
import haxe.ds.ReadOnlyArray;
typedef TermHandleEvents = {
onWrite: String->Void,
onNewLine: Void->Void,
}
class TermHandle {
public final args:ReadOnlyArray<String>;
private final events:TermHandleEvents;
@:allow(bin.Terminal)
private function new(args: Array<String>, events: TermHandleEvents) {
this.args = args;
this.events = events;
}
public function write(s:String) {
this.events.onWrite(s);
}
public function writeLn(s:String = "") {
if (s != "")
this.events.onWrite(s);
this.events.onNewLine();
}
}