1151 lines
35 KiB
JavaScript
1151 lines
35 KiB
JavaScript
/**
|
|
** ==============================
|
|
** 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;j<self.options.y;j++) {
|
|
var row=self.nodebuts[j];
|
|
for (var i=0;i<self.options.x;i++) {
|
|
var nodebut=row[i];
|
|
var node=nodebut.object;
|
|
var tsn=0,procn=0;
|
|
// Node Stats
|
|
for (var t in node.ts.db) {
|
|
if (node.ts.db[t]) tsn += Aios.Ts.count(node.ts.db[t]);
|
|
}
|
|
if (tsn>99) 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<this.options.y;j++) {
|
|
var nodebutrow=[];
|
|
var noderow=[];
|
|
for (var i=0;i<this.options.x;i++) {
|
|
var id = Comp.printf.sprintf('N%2d.%2d',i,j);
|
|
var node = Aios.Node.Node({id:id,position:{x:i,y:j}});
|
|
var nodebut = blessed.button({
|
|
width:6,
|
|
top:j*(self.options.nodebar?4:3)+4,
|
|
left:i*8+2,
|
|
height:self.options.nodebar?3:2,
|
|
align: 'left',
|
|
mouse:true,
|
|
content: id+'\n0',
|
|
object:node
|
|
});
|
|
nodebut.options.style.bg='blue';
|
|
this.screen.append(nodebut);
|
|
if (self.options.nodebar) {
|
|
nodebut.markbuts=[];
|
|
for (var m=0;m<6;m++) {
|
|
var markbut = blessed.button({
|
|
width:1,
|
|
top:j*4+6,
|
|
left:i*8+2+m,
|
|
height:1,
|
|
align: 'center',
|
|
mouse:true,
|
|
content: '',
|
|
object:node
|
|
});
|
|
markbut.options.style.bg='blue';
|
|
nodebut.markbuts.push(markbut);
|
|
this.screen.append(markbut);
|
|
}
|
|
}
|
|
self.world.addNode(node);
|
|
nodebutrow.push(nodebut);
|
|
noderow.push(node);
|
|
(function (i,j,node) {
|
|
nodebut.on('press', function(data) {
|
|
var id = self.node(i,j).id;
|
|
// self.info.setValue(Comp.printf.sprintf(' N%2d.%2d ',i,j));
|
|
self.info.setValue(Comp.printf.sprintf(' %s ',id));
|
|
//self.nodebuts[j][i].setContent('xxx');
|
|
//self.nodebuts[j][i].setStyle({bg:'red'});
|
|
if (self.nodelast!=none) self.nodebuts[self.nodelast.j][self.nodelast.i].setStyle({fg:'white'});
|
|
if (self.worldbut) self.worldbut.setStyle({fg:'white'})
|
|
self.nodelast={i:i,j:j};
|
|
self.nodebuts[j][i].setStyle({fg:'yellow'});
|
|
self.treeview.setLabel(Comp.printf.sprintf(' N%2d.%2d ',i,j));
|
|
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();
|
|
})})(i,j,node);
|
|
}
|
|
this.nodebuts.push(nodebutrow);
|
|
this.nodes.push(noderow);
|
|
}
|
|
|
|
function link(n1,n2) {
|
|
var dx=n2.position.x-n1.position.x;
|
|
var dy=n2.position.y-n1.position.y;
|
|
var dir=Aios.DIR.ORIGIN;
|
|
if (self.options.connections.random &&
|
|
Comp.random.float(1.0) > 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.y;j++) {
|
|
row1=self.nodebuts[j];
|
|
for (i=0;i<self.options.x;i++) {
|
|
if (i<self.options.x-1) {
|
|
node1=row1[i].object;
|
|
node2=row1[i+1].object;
|
|
link(node1,node2);
|
|
}
|
|
if (j<self.options.y-1) {
|
|
row2=self.nodebuts[j+1];
|
|
node1=row1[i].object;
|
|
node2=row2[i].object;
|
|
link(node1,node2);
|
|
}
|
|
}
|
|
}
|
|
// Tree Viewer
|
|
|
|
this.maketree = function (element,reference) {
|
|
var content,children;
|
|
children={};
|
|
if (Comp.obj.isObject(element) || Comp.obj.isArray(element)) {
|
|
if (element && element != null && element._update) element._update(element);
|
|
for (var p in element) {
|
|
if (p != '_update')
|
|
children[p]={};
|
|
}
|
|
content={
|
|
children : children,
|
|
data : element
|
|
}
|
|
} else if (element != undefined) {
|
|
var name = element.toString();
|
|
var funpat = /function[\s0-9a-zA-Z_$]*\(/i;
|
|
var isfun=Comp.obj.isFunction(element)||funpat.test(name);
|
|
if (isfun) {
|
|
element=Comp.string.sub(name,0,name.indexOf('{'));
|
|
}
|
|
if (!isfun || (isfun && self.options.showfun)) {
|
|
children[element]={};
|
|
content={children : children,reference:reference};
|
|
}
|
|
} else {
|
|
children[element]={};
|
|
content={children : children};
|
|
}
|
|
return content;
|
|
};
|
|
|
|
this.treeview = blessed.tree({
|
|
top: 3,
|
|
left: self.options.x*8+2,
|
|
width: self.screen.width-self.options.x*8-2,
|
|
height: self.options.y*(self.options.nodebar?4:3)+1,
|
|
label: self.options.info||'Info',
|
|
focus:true,
|
|
border: {
|
|
type: 'line'
|
|
},
|
|
style: {
|
|
border: {
|
|
fg: 'black'
|
|
},
|
|
hover: {
|
|
border: {
|
|
fg: 'red'
|
|
}
|
|
},
|
|
focus : {
|
|
border: {
|
|
fg: 'red'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
this.treeviewbut1 = blessed.button({
|
|
width:7,
|
|
top:3,
|
|
left:self.screen.width-26,
|
|
height:1,
|
|
align: 'center',
|
|
mouse:true,
|
|
content: 'SIZE',
|
|
style: {
|
|
bold:true,
|
|
bg:'blue',
|
|
fg:'white',
|
|
hover: {
|
|
bg: 'red'
|
|
}
|
|
}
|
|
});
|
|
this.treeviewbut2 = blessed.button({
|
|
width:7,
|
|
top:3,
|
|
left:self.screen.width-18,
|
|
height:1,
|
|
align: 'center',
|
|
mouse:true,
|
|
content: 'PRINT',
|
|
style: {
|
|
bold:true,
|
|
bg:'blue',
|
|
fg:'white',
|
|
hover: {
|
|
bg: 'red'
|
|
}
|
|
}
|
|
});
|
|
this.treeviewbut3 = blessed.button({
|
|
width:7,
|
|
top:3,
|
|
left:self.screen.width-10,
|
|
height:1,
|
|
align: 'center',
|
|
mouse:true,
|
|
content: 'D1',
|
|
style: {
|
|
bold:true,
|
|
bg:'blue',
|
|
fg:'white',
|
|
hover: {
|
|
bg: 'red'
|
|
}
|
|
}
|
|
});
|
|
this.treeviewbut3.depth=1;
|
|
|
|
this.treeviewbut1.on('press', function(data) {
|
|
var item=self.treeview.nodeLines[self.treeview.list.getItemIndex(self.treeview.list.selected)];
|
|
try {
|
|
if (item.data) print(Aios.Code.size(item.data));
|
|
} catch (e) { print(0); }
|
|
});
|
|
this.treeviewbut2.on('press', function(data) {
|
|
var item=self.treeview.nodeLines[self.treeview.list.getItemIndex(self.treeview.list.selected)];
|
|
if (item.data) print(item.data,_,self.treeviewbut3.depth); else if (item.children) {
|
|
for (var p in item.children) {
|
|
print(p,_,self.treeviewbut3.depth);
|
|
}
|
|
} else print(item.name);
|
|
});
|
|
this.treeviewbut3.on('press', function(data) {
|
|
if (self.treeviewbut3.depth<3) self.treeviewbut3.depth++;
|
|
else self.treeviewbut3.depth=1;
|
|
self.treeviewbut3.setContent('D'+self.treeviewbut3.depth);
|
|
self.screen.render();
|
|
});
|
|
|
|
// Create sub-trees
|
|
this.treeview.on('preselect',function(node){
|
|
var content,children,element,data,name;
|
|
if (node.name != '/' && !node.extended) {
|
|
// Io.out(node.extended);
|
|
data = node.data;
|
|
if (data != none && (Comp.obj.isObject(data) || Comp.obj.isArray(data))) {
|
|
node.children = {};
|
|
if (Comp.obj.isArray(data) && Comp.array.empty(data) && Comp.hashtbl.empty(data)) {
|
|
node.children={'[]' : {}};
|
|
} else {
|
|
if (data._update) data._update(data);
|
|
for (var p in data) {
|
|
if (p != '_update') {
|
|
element = data[p];
|
|
content=self.maketree(element,data);
|
|
if (content) node.children[p]=content;
|
|
}
|
|
}
|
|
}
|
|
} else if (data == none && node.reference) {
|
|
node.children = {};
|
|
element=node.reference[node.name];
|
|
name=element.toString();
|
|
var funpat = /function[\s0-9a-zA-Z_$]*\(/i;
|
|
var isfun=Comp.obj.isFunction(element)||funpat.test(name);
|
|
if (isfun) {
|
|
element=Comp.string.sub(name,0,name.indexOf('{'));
|
|
}
|
|
node.children[element]={};
|
|
}
|
|
}
|
|
});
|
|
// Update preview
|
|
this.treeview.on('selected',function(node){
|
|
// self.screen.render();
|
|
});
|
|
this.screen.append(this.treeview);
|
|
this.screen.append(this.treeviewbut1);
|
|
this.screen.append(this.treeviewbut2);
|
|
this.screen.append(this.treeviewbut3);
|
|
|
|
this.treeview.DATA = {
|
|
name:'/',
|
|
extended:true,
|
|
children: {}
|
|
};
|
|
this.treeview.setData(this.treeview.DATA);
|
|
|
|
// this.tree.focus();
|
|
|
|
|
|
// Log window
|
|
|
|
this.log = blessed.Log({
|
|
top: 3+self.options.y*(self.options.nodebar?4:3)+1,
|
|
left: 1,
|
|
width: this.screen.width-1,
|
|
height: this.screen.height-self.options.y*(self.options.nodebar?4:3)-4,
|
|
label: 'Log',
|
|
mouse:true,
|
|
keys:true,
|
|
scrollback:100,
|
|
border: {
|
|
type: 'line'
|
|
},
|
|
scrollbar: {
|
|
ch: ' ',
|
|
track: {
|
|
bg: 'yellow'
|
|
},
|
|
style: {
|
|
fg: 'cyan',
|
|
inverse: true
|
|
}
|
|
},
|
|
alwaysScroll:true,
|
|
scrollOnInput:true,
|
|
style: {
|
|
fg: 'white',
|
|
bg: 'black',
|
|
border: {
|
|
fg: 'green'
|
|
},
|
|
focus: {
|
|
border: {
|
|
fg: 'red'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
this.screen.append(this.log);
|
|
|
|
this.log.key('enter',function (ch,key) {
|
|
self.log.clear();
|
|
self.screen.render();
|
|
});
|
|
this.screen.key(['escape', 'q', 'C-c'], function(ch, key) {
|
|
return process.exit(0);
|
|
});
|
|
|
|
// DB connection?
|
|
if (this.options.db) {
|
|
if (this.options.db.path==_ || this.options.db.channel==_) Io.err('SIMU.DB: invalid options');
|
|
this.db=Db.Sqlc(this.options.db.path,this.options.db.channel);
|
|
if (this.db) {
|
|
this.db.setLog(function (msg) {self.log.log(msg)});
|
|
this.db.init();
|
|
}
|
|
}
|
|
|
|
this.csv = {
|
|
read: function (file,callback,verbose) {
|
|
var data;
|
|
var proc;
|
|
Aios.aios.B([
|
|
function () {
|
|
if (verbose) Aios.aios.log('Reading '+file);
|
|
data=Io.read_file(file);
|
|
if (!data) throw 'File read error: '+file;
|
|
},
|
|
function () {
|
|
var done=false,run=true;
|
|
if (verbose) Aios.aios.log('Parsing '+file);
|
|
Papa.parse(data,{
|
|
complete: function(results) {
|
|
if (verbose) Aios.aios.log('DEL="'+results.meta.delimiter+'" TRUNC='+results.meta.truncated+
|
|
' ABORT='+results.meta.aborted);
|
|
callback.call(current.process.agent,results.data);
|
|
if (!run) Aios.aios.wakeup();
|
|
done=true;
|
|
}
|
|
});
|
|
if (!done) {
|
|
run=false;
|
|
Aios.aios.sleep();
|
|
}
|
|
},
|
|
function () {
|
|
|
|
}
|
|
]);
|
|
}
|
|
};
|
|
|
|
// Agent AIOS simulation extensions
|
|
this.aiosX = {
|
|
csv:this.csv,
|
|
db:this.db,
|
|
createOn:function (x,y,ac,args,level) {
|
|
var node=self.nodes[y][x];
|
|
var agent=none;
|
|
if (level==undefined) level=2;
|
|
if (self.classes[ac])
|
|
agent = Aios.Code.createOn(node,self.classes[ac],args,level);
|
|
if (agent) {
|
|
agent.agent.ac=ac;
|
|
return agent.agent.id;
|
|
} else return none;
|
|
},
|
|
event: {
|
|
add: function (ev) {
|
|
self.events.push({step:self.steps,node:current.node.id,event:ev});
|
|
},
|
|
get: function () {
|
|
var evl = self.events;
|
|
self.events=[];
|
|
return evl;
|
|
}
|
|
},
|
|
getOn:function (x,y,agentid,property) {
|
|
var node=self.nodes[y][x];
|
|
var agent=node.processes.process(agentid);
|
|
if (agent) {
|
|
return agent.agent[property];
|
|
} else self.log.log('Cannot find agent '+agentid+' on node ['+x+','+y+']');
|
|
},
|
|
getNode:function () {return current.node.id},
|
|
getStats: function () {
|
|
var i,j;
|
|
var stats={steps:self.steps};
|
|
for (i in self.world.nodes) {
|
|
var node=self.world.nodes[i];
|
|
for(j in node.processes.table) {
|
|
var proc=node.processes.table[j];
|
|
if (proc && proc.agent.ac) {
|
|
if (stats[proc.agent.ac]==undefined) stats[proc.agent.ac]=1;
|
|
else stats[proc.agent.ac]++;
|
|
}
|
|
}
|
|
}
|
|
return stats;
|
|
},
|
|
getSteps:function () {return self.steps},
|
|
network: {
|
|
x:this.options.x,
|
|
y:this.options.y
|
|
},
|
|
options:this.options,
|
|
print:print,
|
|
setOn:function (x,y,agentid,properties) {
|
|
if (x<0 || x >= 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)}
|
|
}
|