diff --git a/Minitel-RAM.js b/Minitel-RAM.js new file mode 100644 index 0000000..8173505 --- /dev/null +++ b/Minitel-RAM.js @@ -0,0 +1,417 @@ +var Minitel=require("Minitel.js"); + +// +function DinOS(){ + var app,appl,api,ui; + var apps={}; + var m=global.m=new Minitel(); + // === STARTUP === + setWatch(()=>{ + m.println("Reiniciando..."); + setTimeout(load,200); + },BTN1,{ + repeat:true, + edge:'falling', + debounce:10 + }); + m.onInput=(ch,code)=>{ + if (code==m.keyCode.GUIDE){ + selectApp("selector"); + return; + } + if (code==m.keyCode.SOMMAIRE){ + selectApp("conf"); + return; + } + if (code==m.keyCode.FIN){ + rst(); + return; + } + if (appl && appl.onInput) + appl.onInput(ch,code); + else if (ui) + if (ui.onInput) + ui.onInput(ch,code); + }; + // === PRIVATE API === + function draw(){ + if (!ui) + return; + ui.draw(); + } + function rst(){ + m.defaults().showCursor(false); + if (!app){ + header(DinOS.name()); + //TODO: Mostrar el menu de aplicaciones + }else{ + appl=new app(api,m); + header(appl.name()); + ui=appl.go(); + draw(); + } + } + function header(name){ + //m.locate(1,0).write(95).repeat(40); + m.locate(1,0).graphics().write(127).repeat(40).text(); + m.locate(2,0).print(name); + m.locate(1,1); + } + // === PUBLIC API === + function registerApp(app){ + apps[app.prototype.id()]=app; + } + function selectApp(id){ + appl=null; + app=apps[id]; + rst(); + } + // === INIT === + header(DinOS.name()); + m.println("Loading..."); + + // === PUBLIC API === + api={ + getApps:()=>apps, + selectApp:selectApp, + show:(w)=>{ + ui=w; + draw(); + }, + draw:draw + }; + // === PUBLISHING === + this.registerApp=registerApp; + this.selectApp=selectApp; + this.reset=rst; +} +DinOS.name=()=>`dinOS v0.3`; + +function Window(api,m,data){ + if (!data) + throw new Error("Missing controller"); + if (!data.controls || data.controls.length==0) + throw new Error("Empty window"); + var focs,foci=-1,foca=[]; + function draw(){ + m.frame(data.x,data.y,data.w,data.h,{ + title:data.title, + fill:data.fill + }); + data.controls.forEach(ctrl=>ctrl.draw(data.x,data.y)); + } + function onInput(ch,code){ + switch (code){ + case m.keyCode.ANNULATION: + if (data.onClose) + data.onClose(this); + break; + case m.keyCode.RETOUR: + if (focs && focs.prev) + focs.prev(); + else + prev(); + break; + case m.keyCode.SUITE: + if (focs && focs.next) + focs.next(); + else + next(); + break; + case m.keyCode.REPETITION: + next(); + break; + default: + if (focs && focs.onInput) + focs.onInput(ch,code); + } + } + function next(){//next control (tab) + if (foca.length==0) + return; + foci++; + if (foci>=foca.length) + foci=0; + focs=foca[foci]; + draw(); + } + function prev(){//previous control (shift tab) + if (foca.length==0) + return; + foci--; + if (foci<0) + foci=foca.length-1; + focs=foca[foci]; + draw(); + } + for( var i in data.controls){ + data.controls[i].parent=this; + if (data.controls[i].tabstop) + foca.push(data.controls[i]); + } + + next(); + this.draw=draw; + this.onInput=onInput; + this.next=next; + this.prev=prev; +} + +function List(api,m,data){ + if (!data) + throw new Error("Missing controller"); + if (!data.items || data.items.length==0) + throw new Error("No items"); + + function draw(ox,oy){ + for(var i=0;i=data.items.length) + data.sel=0; + draw(); + } + function prev(){ + data.sel--; + if (data.sel<0) + data.sel=data.items.length-1; + draw(); + } + function onInput(ch,code){ + switch(code){ + case m.keyCode.RETOUR: + prev(); + break; + case m.keyCode.SUITE: + next(); + break; + case m.keyCode.ENVOI: + if (data.onSelect) + data.onSelect(this,data.items[data.sel].id); + break; + } + } + + if (!data.sel) + data.sel=0; + this.draw=draw; + this.next=next; + this.prev=prev; + this.onInput=onInput; +} +List.prototype.tabstop=true; + +function Text(api,m,data){ + function draw(ox,oy){ + } + + this.draw=draw; +} +// + +function APP_Selector(api,m){ + this.go=()=>{ + var applst=[],len=0,apps=api.getApps(); + for(var k in apps){ + if (apps[k].prototype.hide) + continue; + var itm={id:k,name:apps[k].prototype.name()}; + applst.push(itm); + if (itm.name.length > len) + len=itm.name.length; + } + return new Window(api,m,{x:2,y:2,w:len+1,h:applst.length+1,controls:[ + new List(api,m,{x:1,y:1,w:len,h:applst.length,items:applst, + onSelect:(sl,id)=>{ + api.selectApp(id); + }}) + ]}); + }; +} +APP_Selector.prototype.id=()=>"selector"; +APP_Selector.prototype.name=()=>DinOS.name(); +APP_Selector.prototype.hide=true; //Hide from selector + +function APP_DinOSCon(api,m){ + var cmd=""; + function motd(){ + var motds=["NT significa No Testado", +//"Los ordenadores son como los dioses del Antiguo Testamento, repletos de reglas y faltos de piedad", +"Un idiota con un ordenador es\n\rmás rápido y mejor idiota.", +//"UNIX es muy simple, lo que sucede es que solo un genio puede entender su\r\nsimplicidad.", +"Creo que tal vez haya mercado para cinco ordenadores.\r\n-Thomas Watson, presidente de IBM, 1943-", +//"¿Windows95 poco util? ¿Y con que te crees que juega ahora el perro?", +"Y cuando todo lo demas falle, lea las instrucciones.", +"Yo se lo que es trabajar duro...\r\nlo he visto!", +"Soy shareware. A mi que me registren!!.", +"Supongamos que no hay situaciones hipoteticas...", +//"Quien es el General Failure y que hace\r\nleyendo mi disco duro!", +"¡¡ QQuuiittaa eell LLooccaall EEcchhoo,, MMaannoolloo !!", +"Se dan clases de ensamblador por\r\nprofesor nativo!", +"Mis programas NO tienen errores, tienen caracteristicas involuntarias.", + ]; + m.println(motds[Math.floor(Math.random()*motds.length)]); + } + function prompt(){ + m.write(">"); + m.showCursor(true); + } + this.go=()=>{ + m.clear(); + motd(); + prompt(); + }; + this.onInput=(data,code)=>{ + if (!data){ + print("MiniCON: "+code); + if (code==m.keyCode.CORRECTION && cmd.length>0){ + m.write([8,32,8]); + cmd=cmd.substr(0,cmd.length-1); + return; + } + if (code==m.keyCode.ENVOI) + data="\n"; + } + cmd+=data; + var idx = cmd.indexOf("\n"); + while (idx>=0) { + m.showCursor(false).write("\r\n"); + var line = cmd.substr(0,idx); + print(" "+line); + cmd = cmd.substr(idx+1); + if (line.length>0) + try{ + var out = eval(line); + if (out!==undefined){ + print(out); + Serial1.println("="+out); + } + }catch(e){ + print(e); + Serial1.println(e); + } + prompt(); + idx = cmd.indexOf("\r"); + } + }; +} +APP_DinOSCon.prototype.id=()=>"js"; +APP_DinOSCon.prototype.name=()=>"Javascript console"; + +function APP_CONF(api,m){ + function showWifi(){ + api.show( + new Window(api,m,{ + x:13,y:2,w:26,h:20,title:"Wifi", + controls:[new Text(api,m,{ + x:1,y:1,w:10,h:1, + text:"Hola", + }) + ] + }) + ); + } + + this.go=()=>{ + return new Window(api,m,{ + x:2,y:2,w:8,h:15, + controls:[new List(api,m,{ + x:1,y:1,w:7,h:14, + items:[{id:"wifi",name:"Wifi"}], + onSelect:(sl,id)=>{ + switch (id){ + case "wifi": + showWifi(); + break; + } + } + })] + }); + }; +} +APP_CONF.prototype.id=()=>"conf"; +APP_CONF.prototype.name=()=>"Configuracion"; + +function NETCON(){ + require("net").createServer(function (connection) { + connection.pipe(LoopbackA); + LoopbackA.pipe(connection); + LoopbackB.setConsole(); + }).listen(23); +} + +function WIFI(ssid,password){ + if (!ssid){ + m.println("Usar: WIFI(ssid,password)"); + return; + } + var WIFI_OPTIONS = { password : password }; + var wifi = require("Wifi"); + m.println("Connecting to: "+ssid); + wifi.connect(ssid, WIFI_OPTIONS, function(err) { + if (err) { + console.log("Connection error: "+err); + m.println("Connection error: "+err); + return; + } + console.log("Connected!"); + m.println("Connected!"); + wifi.getIP((e,i)=>{ + print("ip: "+i.ip+" mac:"+i.mac); + m.println("ip: "+i.ip+" mac:"+i.mac); + }); + //getPage(); + }); +} + +function WIFIHABA(){ + WIFI("PajueloGodoy","WifiCasa55"); +} + +function SCAN(){ + var wifi = require("Wifi"); + m.println("Escaneando..."); + wifi.scan((err,list)=>{ + if (err){ + m.println(err); + print(err); + return; + } + print(list); + for(var i in list){ + var net=list[i]; + m.println(" "+net.ssid+" ("+net.authMode+") "); + } + }); +} + +var dinOS; + +function bootDinOS(){ + dinOS=new DinOS(); + dinOS.registerApp(APP_Selector); + dinOS.registerApp(APP_DinOSCon); + dinOS.registerApp(APP_CONF); + dinOS.selectApp(APP_DinOSCon.prototype.id()); + print(process.memory()); +} + +E.on('init', function() { + USB.setConsole(); + setTimeout(bootDinOS,700); +}); + +//bootDinOS(); + +//var m=new Minitel(); +print(process.memory()); \ No newline at end of file diff --git a/Minitel.js b/Minitel.js new file mode 100644 index 0000000..3bc2f78 --- /dev/null +++ b/Minitel.js @@ -0,0 +1,313 @@ +function Minitel(speed, tx, rx) { + const DEBUG_INPUT = true; + var term = {}; + var s = Serial1; + var input = ""; + const keyCode = { + ENVOI: 65, + GUIDE: 68, + CORRECTION: 71, + RETOUR: 66, + SUITE: 72, + ANNULATION: 69, + REPETITION: 67, + SOMMAIRE: 70, + FIN: 89, + }; + + /** + * INPUT HANDLING + */ + var callInput = ((ch, code) => { + if (this.onInput) + this.onInput(ch, code); + else + print("INPUT DISCONNECTED!!"); + }).bind(this); + + function inputHandler(data) { + input += data; + if (input.length == 0) + return; + if (input.charCodeAt(0) == 19) { + if (input.length < 2) + return; + switch (input.charCodeAt(1)) { + case keyCode.ENVOI: + case keyCode.GUIDE: + case keyCode.CORRECTION: + case keyCode.RETOUR: + case keyCode.SUITE: + case keyCode.ANNULATION: + case keyCode.REPETITION: + case keyCode.SOMMAIRE: + callInput("", input.charCodeAt(1)); + input = input.substr(2); + break; + case keyCode.FIN: + if (input.length < 4) + return; + if (input.charCodeAt(2) == 19 && input.charCodeAt(3) == keyCode.FIN) { + callInput("", input.charCodeAt(1)); + input = input.substr(4); + return; + } else + input = input.substr(2); + } + } else { + callInput(input[0], 0); + input = input.substr(1); + } + } + + /** + * PROTOCOL + */ + function defaults() { + text(); + clear(); + bgColor(0); + fgColor(7); + return this; + } + function clear() { + s.write(12); + return this; + } + function graphics() { + s.write(14); + term.gm = true; + return this; + } + function text() { + s.write(15); + term.gm = false; + return this; + } + function home() { //CR / Line home + s.write(13); + return this; + } + function end() { //Line end + s.write(24); + return this; + } + + //COLORS + function bgColor(col) { + if (col < 0 || col > 7) + return; + s.write([27, 80 + col]); + term.bg = col; + return this; + } + function fgColor(col) { + if (col < 0 || col > 7) + return; + s.write([27, 64 + col]); + term.fg = col; + return this; + } + function blink(blink) { + s.write([27, blink ? 72 : 73]); + return this; + } + + //CURSOR + function locate(x, y) { + s.write([31, 64 + y, 64 + x]); + return this; + } + function showCursor(show) { + term.cursor = show; + s.write(show ? 17 : 20); + return this; + } + + //TEXT + function mprint(str) { + s.print(str); + return this; + } + function println(str) { + s.println(str === undefined ? "" : str); + return this; + } + function write(data) { + s.write(data); + return this; + } + function font(mode) { + s.write([27, mode]); + return this; + } + function repeat(times) { + s.write([18, times + 64]); + return this; + } + + /** + * GRAPHICS + */ + var start = ((x, y, opts) => { + this.locate(x, y).graphics(); + if (opts && opts.fg) + this.fgColor(opts.fg); + if (opts && opts.bg) + this.bgColor(opts.bg); + return this; + }).bind(this); + + function gCurStart() { + if (term.cursor) + this.write(20); + } + + function gCurEnd() { + if (term.cursor) + this.write(17); + } + + function graph(x, y, val, opts) { + start(x, y, opts).write(val).text(); + return this; + } + + function frame(x, y, w, h, opts) { + gCurStart(); + var i; + start(x, y, opts).write(118); + if (opts && opts.title) { + var s = String(opts.title); + this.write(53).text(); + if (opts && opts.titleFg); + this.fgColor(opts.titleFg); + if (opts && opts.titleBg); + this.fgColor(opts.titleBg); + this.write(s); + this.graphics().write(106); + this.write(115).repeat(w - (s.length + 4)); + } else + this.write(115).repeat(w - 2); + this.write(121); + if (opts && opts.fill) { + for (i = 1; i < h; i++) + start(x, y + i, opts).write([53, 32]).repeat(w - 2).write(106); + } else + for (i = 1; i < h; i++) { + start(x, y + i, opts).write(53); + start(x + w, y + i, opts).write(106); + } + start(x, y + h, opts).write([103, 115]).repeat(w - 2).write(59).text(); + gCurEnd(); + return this; + } + + function fill(x, y, w, h, ch, opts) { + if (w <= 0 || h <= 0) + return; + gCurStart(); + if (ch === undefined) + ch = 32; + for (var yy = y; yy < y + h; yy++) + start(x, yy, opts).write(ch).repeat(w - 1); + gCurEnd(); + } + + // *** PUBLISHING *** + + + /** + * CONSTRUCTOR + */ + (() => { + if (!speed) + speed = 1200; + if (!tx) + tx = B6; + if (!rx) + rx = B7; + print(`Configuring ESPruino in tx: ${tx}, rx: ${rx}, speed: ${speed}`); + s.setup(speed, { + tx: tx, + rx: rx, + bytesize: 7, + parity: "e", + errors: true + }); + s.on('parity', function () { + console.log("[!!] Parity error"); + }); + s.on('framing', function () { + console.log("[!!] Framing error"); + }); + s.on('data', data => { + if (DEBUG_INPUT) { + var s = " (" + data.length + ") ["; + for (var i in data) { + if (i > 0) + s += ", "; + s += data.charCodeAt(i); + } + print(s + "]"); + } + //*************** + inputHandler(data); + }); + })(); + + // *** PUBLSHING *** + this.defaults = defaults; + this.reset = reset; + this.clear = clear; + this.graphics = graphics; + this.text = text; + + //COLORS + this.bgColor = bgColor; + this.fgColor = fgColor; + this.blink = blink; + this.color = { + black: 0, + red: 1, + green: 2, + yellow: 3, + magenta: 4, + blue: 5, + cyan: 6, + white: 7 + }; + + //CURSOR + this.locate = locate; + this.showCursor = showCursor; + + //TEXT + this.print = mprint; + this.println = println; + this.write = write; + this.font = font; + this.font.regular = 76; + this.font.doubleHeight = 77; + this.font.doubleWidth = 78; + this.font.double = 79; + this.font.double = 79; + this.font.normal = 92; + this.font.inverted = 93; + this.font.transparent = 94; + this.font.mask = 88; + this.font.demask = 95; + this.repeat = repeat; + + // INPUT + this.keyCode = keyCode; + + // GRAPHICS + this.graph = graph; + this.frame = frame; + this.fill = fill; + //EVENTS + this.onInput = undefined; +} + +module.exports = Minitel; \ No newline at end of file