/** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2017 bLAB ** $CREATED: 02-10-16 by sbosse. ** $VERSION: 1.3.8 ** ** $INFO: ** ** SEJAM: JavaScript AIOS JAM Agent Simluator ** - Terminal (curses) GUI ** ** $ENDOFINFO */ var Io = Require('com/io'); var Comp = Require('com/compat'); var Db = Require('db/db'); var blessed = Require('term/blessed'); var current=none; var Aios = none; var Papa = Require('parser/papaparse.js'); var Esprima = Require('parser/esprima'); /** Create a simulation world with visualization * * options: {x:number,y:number,id:string?,terminal:string,gui='term'?, * nolimits:boolean is a disable flag for agent check poininting, * connection:{random:number?}, * classes:{node:{..},ac1:{..},..}} * */ var simuTerm = function (options) { var self=this; var node1,node2,row,row1,row2,i,j,p; this.options=options||{}; if (this.options.x==_) this.options.x=6; if (this.options.y==_) this.options.y=6; if (this.options.id==_) this.options.id='Dark Universe World'; if (this.options.connections==_) this.options.connections={}; if (this.options.markings) this.options.nodebar=true; if (this.options.nolimits) Aios.config({nolimits:true}); this.classes=this.options.classes||{}; delete this.options.classes; this.run=false; this.steps=0; this.loop=none; this.time=0; this.time0=0; this.log=[]; this.events=[]; this.err=function (msg,err) { Aios.aios.log('Error: '+msg); throw (err||'[SIM] Error'); } this.warn=function (msg) { Aios.aios.log('Warning: '+msg); } this.out=function (msg) { Aios.aios.log(msg); } // PRINT: Smart print function var print = function (msg,header,depth,nolog) { var lines=[]; var line=''; if (depth==_) depth=1; function isvec(obj) {return(Comp.obj.isArray(obj) && (obj.length == 0 || !Comp.obj.isArray(obj[0])))} function ismat(obj) {return(Comp.obj.isArray(obj) && obj.length > 0 && Comp.obj.isArray(obj[0]))} function mat(o,depth) { // matrix var lines=[]; var line = ''; if (header) {line=header; header=_}; for (var j in o) { var row=o[j]; line += Comp.printf.list(row,function (v) { return (Comp.obj.isArray(v)?(depth>0?'['+vec(v,depth-1)+']':'[..]'): Comp.obj.isObj(v)?(depth>0?obj(v,depth-1):'{..}'):v); }); lines.push(line); line=''; } return lines; } function vec(v,depth) { // vector var lines=[]; var line = ''; if (header) {line=header; header=_}; if (v.length==0) return(line+'[]'); else { // can still contain matrix elements that must bes separated var sep='',sepi=''; for (var p in v) { if (ismat(v[p])) { //self.log.log(line); line=' '; if (depth>0) { lines = mat(v[p],depth-1); line += sep+'['; sepi=''; Comp.array.iter(lines,function (line2) { line += sepi+'['+line2+']'; sepi=','; }); line += ']'; sep=','; } else { line += sep+'[[..]]'; sep=','; } } else if (isvec(v[p])) { //self.log.log(line); line=' '; line += sep+vec(v[p],depth-1); sep=','; } else { line += sep+(Comp.obj.isArray(v[p])?(depth>0?vec(v[p],depth-1):'[..]'): Comp.obj.isObj(v[p])?(depth>0?obj(v[p],depth-1):'{..}'):v[p]); sep=','; } } if (line!='') return line; } } function obj(o,depth) { var line=''; var sep=''; if (header) {line=header; header=_}; line += '{'; for (var p in o) { if (!Comp.obj.isFunction(o[p])) { line += sep + p+':'+ (Comp.obj.isArray(o[p])?(depth>0?vec(o[p],depth-1):'[..]'): Comp.obj.isObj(o[p])?(depth>0?obj(o[p],depth-1):'{..}'):o[p]); sep=','; } else { line += sep + p+':'+'function()'; sep=','; } } return line+'}'; } function str(s) { var line=''; var lines=[]; var lines2 = Comp.string.split('\n',msg); if (header) {line=header; header=_}; if (lines2.length==1) lines.push(line+msg); else { Comp.array.iter(lines2,function (line2,i) { if (i==0) lines.push(line+line2); else lines.push(line2); }); } return lines; } if (ismat(msg)) lines = Comp.array.concat(lines, Comp.array.map(mat(msg,depth-1),function (line){ return ' '+line})); else if (Comp.obj.isString(msg)) lines = Comp.array.concat(lines,str(msg)); else if (isvec(msg)) lines.push(vec(msg,depth-1)); else if (Comp.obj.isObj(msg)) lines.push(obj(msg,depth-1)); else { if (header) {line=header; header=_}; line += msg; lines.push(line); } if (nolog) return lines; else Comp.array.iter(lines,function (line) {self.log.log(line)}); }; var log = function(){ if (!current.node || !current.process) { print(arguments[0]); } else if (arguments.length==1) print(arguments[0],'['+current.node.id+':'+current.process.agent.id+':'+current.process.pid+':'+current.process.agent.ac+'] '); else { for (var i in arguments) { if (i==0) print(arguments[i],'['+current.node.id+':'+current.process.agent.id+':'+current.process.pid+':'+current.process.agent.ac+'] '); else print(arguments[i],_,2); } } }; // Extended sandbox environment available for all agents Aios.aios0.log=log; Aios.aios1.log=log; Aios.aios2.log=log; Aios.aios0.print=function(msg,depth) {return print(msg,_,depth,false)}; Aios.aios1.print=function(msg,depth) {return print(msg,_,depth,false)}; Aios.aios2.print=function(msg,depth) {return print(msg,_,depth,false)}; Aios.aios0.sprint=function(msg,depth) {return print(msg,_,depth,true)}; Aios.aios1.sprint=function(msg,depth) {return print(msg,_,depth,true)}; Aios.aios2.sprint=function(msg,depth) {return print(msg,_,depth,true)}; Aios.config({TIMESCHED:1000}); this.world = Aios.World.World([],{id:this.options.id,classes:this.classes}); this.screen = blessed.screen({ smartCSR: false, terminal: self.options.terminal||'xterm-color' }); this.screen.title = 'SEJAM (c) Stefan Bosse - '+(this.options.id||'JAM Dark Universe Simulation World'); this.screen.cursor.color='red'; // Information bar this.info = blessed.textbox({ top: 0, left: 4*9+1, width: self.screen.width-4*9-1, height: 3, label: 'Information', focus:false, //draggable:true, border: { type: 'line' }, style: { fg:'blue' } }); this.screen.append(this.info); // Buttons function but(options) { var obj = blessed.button({ width: options.width||8, left: options.left||0, top: options.top||0, height: 3, align: 'center', content: options.content||'?', mouse:true, focus:true, border: { type: 'line' }, style: { fg: 'white', bg: 'blue', bold:true, border: { fg: 'black' }, hover: { border: { fg: 'red' } } } }); self.screen.append(obj); return obj; } // Bt QUIT this.but1 = but({left:1,content:'QUIT'}); this.but1.on('press', function(data) { return process.exit(0); }); // Bt RUN this.but2 = but({left:9,content:'RUN'}); this.but2.on('press', function(data) { if (self.run) return; var curtime=Aios.time(); var lasttime=curtime; if (self.time>0) current.world.lag=current.world.lag+(lasttime-self.time); if (self.time0==0) self.time0=curtime; self.time=curtime; self.log.log('Running simulation at '+self.steps+ ' .. '); self.but2.setStyle({bg:'red'}); Aios.config({iterations:1}); self.run=true; var loop = function () { var nexttime=Aios.scheduler(); self.steps++; curtime=Aios.time(); if (!self.run) return; //self.log.log(nexttime); if (nexttime>0) self.loop=setTimeout(loop,nexttime); else if (nexttime<0) self.loop=setTimeout(loop,0); else { self.but2.setStyle({bg:'blue'}); self.log.log('Stopping simulation at '+self.steps+ ' .. (d '+(Aios.time()-self.time)+' ms / t '+(Aios.time()-current.world.lag)+' ms)'); self.update(true); self.time=curtime; } if ((curtime-lasttime)>100) { self.update(true); lasttime=curtime; } else self.update(false); } self.loop = setTimeout(loop,1); }); // Bt STOP this.but3 = but({left:17,content:'STOP'}); this.but3.on('press', function(data) { if (!self.run) return; self.log.log('Stopping simulation at '+self.steps+ ' .. (d '+(Aios.time()-self.time)+' ms / t '+(Aios.time()-current.world.lag)+' ms)'); self.time=Aios.time(); self.run=false; if (self.loop!=none) clearTimeout(self.loop); self.but2.setStyle({bg:'blue'}); self.update(true); }); // Bt STEP this.but4 = but({left:25,content:'STEP'}); this.but4.on('press', function(data) { var start,stop; var curtime=Aios.time(); var lasttime=curtime; start=curtime; if (self.time>0) current.world.lag=current.world.lag+(start-self.time); if (self.time0==0) self.time0=start; self.time=start; self.run=true; self.log.log('Stepping simulation ('+self.but5.steps+') at '+self.steps+ ' .. (t '+(Aios.time()-current.world.lag)+' ms)'); self.but2.setStyle({bg:'red'}); Aios.config({iterations:1}); var stepped=self.but5.steps; var loop = function () { var nexttime=Aios.scheduler(); self.steps++; stepped--; curtime=Aios.time(); if (!self.run) return; if (stepped==0) { stop=Aios.time(); self.run=false; self.but2.setStyle({bg:'blue'}); self.log.log('Stopping simulation at '+self.steps+ ' .. (d '+(Aios.time()-self.time)+' ms / t '+(Aios.time()-current.world.lag)+' ms)'); self.time=stop; self.update(true); return; } if (nexttime>0) self.loop=setTimeout(loop,nexttime); else if (nexttime<0) self.loop=setTimeout(loop,0); else { self.but2.setStyle({bg:'blue'}); self.log.log('Stopping simulation at '+self.steps+ ' .. (d '+(Aios.time()-self.time)+' ms / t '+(Aios.time()-current.world.lag)+' ms)'); self.update(true); self.time=curtime; } if ((curtime-lasttime)>100) { self.update(true); lasttime=curtime; } else self.update(false); } self.loop = setTimeout(loop,0); }); // Bt STEPS this.but5 = blessed.button({ width:3, top:1, left:33, height:1, align: 'center', mouse:true, content: '1', style: { bold:true, bg:'blue', fg:'white', hover: { bg: 'red' } } }); this.but5.steps=1; var stepsall = [1,5,10,50,100,500]; this.but5.on('press', function(data) { self.but5.steps=Comp.array.next(stepsall,self.but5.steps); self.but5.setContent(Comp.pervasives.string_of_int(self.but5.steps)); self.screen.render(); }); self.screen.append(this.but5); // GUI Update this.update = function (full) { var mem = Io.mem(); var info = self.world.info(); var curtime=Aios.time(); if (self.nodelast!=none) self.info.setValue(Comp.printf.sprintf('%s / %d stp. / %d ag. / C %d kB / M %d MB / t %d / d %d', self.node(self.nodelast.i,self.nodelast.j).id, self.steps, info.agents, div(info.transferred,1024), div(mem.data+mem.heap,1024), curtime-current.world.lag, self.run?curtime-current.world.lag-self.time0:self.time-current.world.lag-self.time0)); else self.info.setValue(Comp.printf.sprintf('%d stp. / %d ag. / C %d kB / M %d MB / t %d / d %d', self.steps, info.agents, div(info.transferred,1024), div(mem.data+mem.heap,1024), curtime-current.world.lag, self.run?curtime-current.world.lag-self.time0:self.time-current.world.lag-self.time0)); if (full) for (var j=0;j99) tsn='>H'; procn=node.processes.used; if (procn>999) procn='>k'; nodebut.setContent(Comp.printf.sprintf('%s\n%3s|%2s',self.node(i,j).id, Comp.pervasives.string_of_int(procn), Comp.pervasives.string_of_int(tsn))); // Node Markings if (self.options.markings) { var marked=Comp.array.create(6,false); for (var m in self.options.markings) { // class:[index(0..),color,optional text] // class.property:[index(0..),color,text mapping function] var mark = self.options.markings[m]; var index=mark[0]; var color=mark[1]; var property = Comp.string.postfix(m); var ac=Comp.string.prefix(m); for (var p in node.processes.table) { if (node.processes.table[p]!=_){ var agent=node.processes.table[p].agent; if (agent.ac==ac) { // Agent property marking if (agent[property]!=_) { var val=mark[2](agent[property]); if (val!=none) { marked[index]=true; nodebut.markbuts[index].setStyle({bg:color}); if (Comp.obj.isString(val) && val!='') nodebut.markbuts[index].setContent(Comp.string.get(val,0)); } } else if (ac==property) { var text=mark[2]; marked[index]=true; nodebut.markbuts[index].setStyle({bg:color}); if (text) nodebut.markbuts[index].setContent(text); } } } } } for (m in marked) { if (!marked[m]) nodebut.markbuts[m].setStyle({bg:'blue'}); if (!marked[m]) nodebut.markbuts[m].setContent(''); } } } } //if (!self.run) if (full) self.screen.render(); }; // Simulation World this.worldframe = blessed.textbox({ top: 3, left: 1, width: self.options.x*8, height: self.options.y*(self.options.nodebar?4:3)+1, label: 'Simulation World', focus:false, border: { type: 'line' }, style: { fg:'blue' } }); this.screen.append(this.worldframe); this.nodebuts=[]; this.nodelast=none; this.nodes=[]; // Get a node object this.node = function (i,j) { return this.nodes[j][i]; }; // Build the world for (var j=0;j self.options.connections.random) return; if (dx==1 && dy==0) dir=Aios.DIR.EAST; if (dx==-1 && dy==0) dir=Aios.DIR.WEST; if (dx==0 && dy==1) dir=Aios.DIR.SOUTH; if (dx==0 && dy==0) dir=Aios.DIR.NORTH; self.world.connect(dir,n1,n2,self.options.connections); var line = blessed.line({ orientation:dx!=0?'horizontal':'vertical', top:dx==0?(dy<0?n1.position.y*(self.options.nodebar?4:3)+(self.options.nodebar?4:3): n1.position.y*(self.options.nodebar?4:3)+(self.options.nodebar?4:3)+3): n1.position.y*(self.options.nodebar?4:3)+(self.options.nodebar?5:4), left:dy==0?(dx<0?n1.position.x*8:n1.position.x*8+8):(n1.position.x*8+5), width:dy==0?2:0, height:dx==0?1:0 }); self.screen.append(line); } // Create connections for (j=0;j= self.options.x || y<0 || y>=self.options.y) throw 'setOn: Invalid Argument'; var node=self.nodes[y][x]; var agent=node.processes.process(agentid); if (agent) { for (var p in properties) { if (agent.agent[p]!=_) agent.agent[p]=properties[p]; } } else self.log.log('Cannot find agent '+agentid+' on node ['+x+','+y+']'); }, setOnNode:function (x,y,properties) { if (x<0 || x >= self.options.x || y<0 || y>=self.options.y) throw 'setOnNode: Invalid Argument'; var node=self.nodes[y][x]; if (node) { for (var p in properties) { if (node[p]!=_) node[p]=properties[p]; if (p=='id') { var nodebut=self.nodebuts[y][x]; nodebut.setContent(properties[p]); } } } else self.log.log('Cannot find agent '+agentid+' on node ['+x+','+y+']'); }, stop: function () { if (!self.run) return; self.log.log('Stopping simulation at '+self.steps+ ' .. (d '+(Aios.time()-self.time)+' ms / t '+(Aios.time()-current.world.lag)+' ms)'); self.time=Aios.time(); self.run=false; if (self.loop!=none) clearTimeout(self.loop); self.but2.setStyle({bg:'blue'}); self.update(true); } }; // Extended sandbox environment available for all agents in simulation Aios.aios0.simu=this.aiosX; Aios.aios1.simu=this.aiosX; Aios.aios2.simu=this.aiosX; if (options.aios) { // Extend AIOS environment(s) for (p in options.aios) { if (!Aios.aios0[p]) Aios.aios0[p]=options.aios[p]; if (!Aios.aios1[p]) Aios.aios1[p]=options.aios[p]; if (!Aios.aios2[p]) Aios.aios2[p]=options.aios[p]; } } if (options.aios0) { // Extend AIOS environment(s) for (p in options.aios0) { if (!Aios.aios0[p]) Aios.aios0[p]=options.aios[p]; } } if (options.aios1) { // Extend AIOS environment(s) for (p in options.aios1) { if (!Aios.aios1[p]) Aios.aios1[p]=options.aios[p]; } } if (options.aios2) { // Extend AIOS environment(s) for (p in options.aios2) { if (!Aios.aios2[p]) Aios.aios2[p]=options.aios[p]; } } // Start world agent - if world class is specified if (self.classes['world']) { // A virtual world node must be created. this.worldnode=Aios.Node.Node({id:'World0'}); this.worldagent=Aios.Code.createOn(this.worldnode,self.classes['world'],[],2); this.worldagent.agent.ac='world'; // Add additional attributes to world agent //this.worldagent.agent.options=self.options; //for (var p in this.agent) { // this.worldagent.agent[p]=this.agent[p]; //} this.world.addNode(this.worldnode); this.worldbut = blessed.button({ width:1, top:3, left:self.options.x*8, height:1, align: 'left', mouse:true, content: 'W', object:self.worldnode, style: { bold:true, bg:'blue', fg:'white', hover: { bg: 'red' } } }); this.screen.append(this.worldbut); (function (node) { self.worldbut.on('press', function(data) { //self.info.setValue(' World '); if (self.nodelast!=none) self.nodebuts[self.nodelast.j][self.nodelast.i].setStyle({fg:'white'}); self.nodelast=none; self.worldbut.setStyle({fg:'yellow'}); self.treeview.setLabel(' World '); var data = node.info(); for (var p in data) { var element=data[p]; var content=self.maketree(element,data); if (content) self.treeview.DATA.children[p]=content; } self.treeview.setData(self.treeview.DATA); self.update(); })})(this.worldnode); } // Start node agents - if node class is specified else if (self.classes['node']) { Comp.array.iter(self.world.nodes, function (node,i) { var agent = Aios.Code.createOn(node,self.classes['node'],[node.position.x,node.position.y]); agent.agent.ac='node'; }); } } simuTerm.prototype.start = function () { this.screen.render(); this.out('Initializing ...'); // AC COMPILING for(var c in this.classes) { this.out('Compiling agent class '+c+'...'); this.compile(this.classes[c],c); } // CONNECTION LINKS if(this.options.connections.link) this.out('Setting up physical link(s) ...'); for(var c in this.options.connections.link) { var link=this.options.connections.link[c]; if (!link.from || !link.to || !link.from.url || !link.to.url || link.from.x==undefined || link.from.y==undefined || !link.dir) this.err('Invalid link: '+Io.inspect(link)); this.out(link.from.url+' ['+link.from.x+','+link.from.y+'] -> '+link.to.url+(link.to.x!=_?' ['+link.to.x+','+link.to.y+']':'')); var row = this.nodes[link.from.y]; var node = row?row[link.from.x]:_; if (!node) this.err('Invalid node reference in link: '+Io.inspect(link)); this.world.connectPhy(link.dir,node,{rcv:link.from.url,snd:link.to.url,verbose:1,out:this.out}); } this.out('Ready.'); } var simu = simuTerm; var JamAnal = Require('jam/analyzer'); simu.prototype.analyze=JamAnal.jamc.prototype.analyze; simu.prototype.syntax=JamAnal.jamc.prototype.syntax; simu.prototype.compile = function (ac,name) { var syntax, modu, content, off; try { off=this.syntax.find(this.options.ast,'VariableDeclarator',name); if (off && off.loc) this.syntax.offset=off.loc.start.line-1; content = 'var ac = '+ac; syntax = Esprima.parse(content, { tolerant: true, loc:true }); this.analyze(syntax,{classname:name,level:2,verbose:1}); if (this.options.verbose>=0) this.out('Agent class template '+name+' compiled successfully.'); this.syntax.offset=0; } catch (e) { this.out('Agent class template '+name+' not compiled successfully: '+e); } }; var Simu = function(options) { var obj=none; if (!options || (options && (options.gui=='term' || !options.gui))) obj = new simuTerm(options); return obj; } module.exports = { simu:simu, Simu:Simu, current:function (module) { current=module.current; Aios=module; JamAnal.current(module)} }