diff --git a/.gitignore b/.gitignore
index fc63cb95..09748589 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@
tmp
logs
build
-htdocs
*.o
*.a
test_fjage
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..e3227ef3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+language: java
+install: true
+notifications:
+ slack:
+ secure: QAm3ehwD1y1CQatjEB1aQ/oGJhLGMnsmlqehbT7V+KVq15hBeDZMKZocsmPMs+CwfXwSbYOeGSOtMigKTDVJXy+YmHaU4btO6JJxZwKB+HQBofgG2KO9/Ak/WPhBhqA9eJJzowjzHv/dVYvtWRRPriD9yJ8vv+2ZDC56cY06OZdWJxau3hTX0XAAJNPOXeVHXWmfORwIOQR5r5nGlYNhwbbuuPDlKLbvIaZ+5X3XHyNNgi+S+4nB8utrgEYumq8stgXDeZL8lS8rGeqCLt4RNrOBTw3UUIlPCZMnyq75BDtBRPs9bWTIFv3XqTWqBmvWY/OVQKRe6tc66dleRRc0+6xd5w5yNV44t8zsUbgpFs02+4mHbEGrrlXpJ0j0OZzBa5xWX5THhSgvdhw/rl/YAP57sLC6amsccLKoO/sBaEu73pXxafJ0C1un8gNAgVfIISsyplikSBzS9lVrUZLNnrAuESpC/kwPH4SGj0m3MxitVDtGgmM4+MINA0XAT8IJqiMnEagshbwKHBqWzQAJbMjlgI8rxlMZr1BXCfxf9mffxPpgsLKlM9jbG/JVmeQg8xcxedfkxsDm0FZ4egMiaHKv8kbdgldx8jruuIDcjWn7115hXpE2q1U4dcUnzTVGX1rG3gcTOqBVv+80+mGG8tmzX54iOwkB2XszUWIWMbg=
diff --git a/README.md b/README.md
index 6d396dda..06b17749 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
+[![Build Status](https://travis-ci.org/org-arl/fjage.svg?branch=1.5)](https://travis-ci.org/org-arl/fjage)
+
fjåge
=====
**Framework for Java and Groovy Agents**
-developed by [Mandar Chitre](http://www.chitre.net), [Acoustic Research Laboratory](http://www.arl.nus.edu.sg)
-
Introduction
------------
@@ -17,42 +17,39 @@ Key Features
* Agent development in Java or Groovy
* Interactive Groovy shell and scripting
* Easy switching between realtime operation and discrete event simulation
+* APIs for access from Javascript, Python, C
Documentation
-------------
-* [Getting Started](http://org-arl.github.io/fjage/doc/html/quickstart.html)
-* [Developer's Guide](http://org-arl.github.com/fjage/doc/html/)
-* [API documentation](http://org-arl.github.com/fjage/javadoc/)
+* [Getting Started](https://fjage.readthedocs.io/en/latest/quickstart.html)
+* [Developer's Guide](https://fjage.readthedocs.io/en/latest/)
+* [API documentation](http://org-arl.github.io/fjage/javadoc/)
Support
-------
* [Project Home](http://github.com/org-arl/fjage)
-* [Discussion Forum](http://groups.google.com/forum/#!forum/fjage-users)
* [Issue Tracking](http://github.com/org-arl/fjage/issues)
-Binary Releases
----------------
-
-* [fjage-1.4.2.jar](http://repo1.maven.org/maven2/com/github/org-arl/fjage/1.4.2/fjage-1.4.2.jar), released Apr 19, 2018 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/t1-rN2i7lyU))
-* [fjage-1.4.1.jar](http://repo1.maven.org/maven2/com/github/org-arl/fjage/1.4.1/fjage-1.4.1.jar), released Dec 5, 2017 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/frim1f-5ZK8))
-* [fjage-1.4.jar](http://repo1.maven.org/maven2/com/github/org-arl/fjage/1.4/fjage-1.4.jar), released Mar 4, 2016 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/9yZuVZWQSfY))
-* [fjage-1.3.4.jar](https://repo1.maven.org/maven2/com/github/org-arl/fjage/1.3.4/fjage-1.3.4.jar), released Jul 23, 2015 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/7d5CZB82QCc))
-* [fjage-1.3.3.jar](https://repo1.maven.org/maven2/com/github/org-arl/fjage/1.3.3/fjage-1.3.3.jar), released Jul 21, 2014 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/_9p7w8eES9Q))
-* [fjage-1.3.2.jar](https://repo1.maven.org/maven2/com/github/org-arl/fjage/1.3.2/fjage-1.3.2.jar), released Oct 21, 2013 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/RdAidaBDlNQ))
-* [fjage-1.3.1.jar](https://repo1.maven.org/maven2/com/github/org-arl/fjage/1.3.1/fjage-1.3.1.jar), released Sep 1, 2013 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/2M_FtV2zzRY))
-* [fjage-1.3.jar](https://repo1.maven.org/maven2/com/github/org-arl/fjage/1.3/fjage-1.3.jar), released Aug 26, 2013 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/dibfe8w2Ijk))
-* [fjage-1.2.1.jar](https://repo1.maven.org/maven2/com/github/org-arl/fjage/1.2.1/fjage-1.2.1.jar), released May 29, 2013 ([release notes](https://groups.google.com/forum/#!topic/fjage-users/tw5Zf-7hil4))
-
-**Maven Central dependency:**
+Maven Central dependency
+------------------------
com.github.org-arl
fjage
- 1.4.2
+ 1.5
+Contributing
+------------
+
+Contributions are always welcome! Clone, develop and do a pull request!
+
+Try to stick to the coding style already in use in the repository. Additionally, some guidelines:
+
+* [Commit message style](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits)
+
License
-------
diff --git a/VERSION b/VERSION
index 62a4b90c..8df077e0 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.4.3-SNAPSHOT
+1.5-SNAPSHOT
diff --git a/build.gradle b/build.gradle
index df8b3bcf..e0b39d0f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ defaultTasks 'jars'
archivesBaseName = 'fjage'
group = 'com.github.org-arl'
version = new File('VERSION').text.trim()
-docsDirName = '../htdocs'
+docsDirName = '../docs'
targetCompatibility = 1.8
sourceCompatibility = 1.8
@@ -19,12 +19,12 @@ repositories {
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.4'
- compile 'jline:jline:2.12'
+ compile 'org.jline:jline:3.9.0'
compile 'org.apache.commons:commons-lang3:3.1'
compile 'uk.com.robust-it:cloning:1.9.0'
- compile 'org.eclipse.jetty:jetty-server:8.0.4.v20111024'
- compile 'org.eclipse.jetty:jetty-servlet:8.0.4.v20111024'
- compile 'org.eclipse.jetty:jetty-eventsource-servlet:1.0.0'
+ compile 'org.eclipse.jetty:jetty-server:9.4.12.v20180830'
+ compile 'org.eclipse.jetty:jetty-servlet:9.4.12.v20180830'
+ compile 'org.eclipse.jetty.websocket:websocket-server:9.4.12.v20180830'
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.fazecast:jSerialComm:2.1.1'
testCompile 'junit:junit:4.11'
@@ -55,6 +55,13 @@ jar {
}
}
+test {
+ testLogging {
+ events "passed", "skipped", "failed"
+ exceptionFormat "full"
+ }
+}
+
task jars(dependsOn: jar, type: Copy) {
into "$buildDir/libs"
from configurations.runtime
@@ -62,14 +69,6 @@ task jars(dependsOn: jar, type: Copy) {
jars.outputs.upToDateWhen { false }
-task doc {
- doLast {
- ant.exec(executable: 'make', dir: '.', failonerror: true) {
- arg(line: 'html')
- }
- }
-}
-
////// tasks for MavenCentral deployment
task javadocJar(type: Jar, dependsOn: javadoc) {
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 00000000..fce9501f
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,10 @@
+
+
+fjåge documentation
+
+
+
+Documentation
+API Javadocs
+
+
diff --git a/etc/initrc-rconsole.groovy b/etc/initrc-rconsole.groovy
index 5ee9ff94..17d87233 100644
--- a/etc/initrc-rconsole.groovy
+++ b/etc/initrc-rconsole.groovy
@@ -26,5 +26,6 @@ if (devname != null) {
container = new SlaveContainer(platform, hostname, port)
}
shell = new ShellAgent(new ConsoleShell(), new GroovyScriptEngine())
+shell.addInitrc("cls://org.arl.fjage.shell.fshrc");
container.add 'rshell', shell
platform.start()
diff --git a/etc/initrc.groovy b/etc/initrc.groovy
index 3b06281e..801a3e6b 100644
--- a/etc/initrc.groovy
+++ b/etc/initrc.groovy
@@ -1,8 +1,9 @@
import org.arl.fjage.*
import org.arl.fjage.remote.*
import org.arl.fjage.shell.*
+import org.arl.fjage.connectors.*
-boolean gui = System.properties.getProperty('fjage.gui') == 'true'
+boolean web = System.properties.getProperty('fjage.web') == 'true'
int port = 5081
try {
port = Integer.parseInt(System.properties.getProperty('fjage.port'))
@@ -20,9 +21,16 @@ if (devname != null) {
}
platform = new RealTimePlatform()
-if (devname == null) container = new MasterContainer(platform, port)
-else container = new MasterContainer(platform, port, devname, baud, 'N81')
-if (gui) shell = new ShellAgent(new SwingShell(), new GroovyScriptEngine())
-else shell = new ShellAgent(new ConsoleShell(), new GroovyScriptEngine())
+container = new MasterContainer(platform, port)
+if (devname != null) container.addConnector(new SerialPortConnector(devname, baud, 'N81'))
+if (web) {
+ WebServer.getInstance(8080).add("/", "/org/arl/fjage/web")
+ Connector conn = new WebSocketConnector(8080, "/shell/ws")
+ shell = new ShellAgent(new ConsoleShell(conn), new GroovyScriptEngine())
+ container.addConnector(new WebSocketConnector(8080, "/ws", true))
+} else {
+ shell = new ShellAgent(new ConsoleShell(), new GroovyScriptEngine())
+}
+shell.addInitrc("cls://org.arl.fjage.shell.fshrc");
container.add 'shell', shell
platform.start()
diff --git a/fjage.sh b/fjage.sh
index e6eb29aa..63301abd 100755
--- a/fjage.sh
+++ b/fjage.sh
@@ -1,7 +1,7 @@
#!/bin/bash
#
# Usage:
-# ./fjage.sh [-gui] [-port port] [-baud baud] [-rs232 devname]
+# ./fjage.sh [-web] [-port port] [-baud baud] [-rs232 devname]
CLASSPATH=.`find build/libs -name *.jar -exec /bin/echo -n :'{}' \;`
export CLASSPATH=$CLASSPATH:samples
@@ -9,17 +9,16 @@ export CLASSPATH=$CLASSPATH:samples
# Cygwin/Windows uses a ";" classpath separator
if [ $(expr "$(uname -s)" : 'CYGWIN.*') -gt 0 ];then
CLASSPATH=`echo "$CLASSPATH" | sed 's/:/;/g'`
- TERMOPT="-Djline.terminal=jline.UnixTerminal"
fi
# process command line options
-GUI=false
+WEB=false
OPT1=
while [[ $1 == -* ]]
do
OPT=$1
- if [ $OPT = "-gui" ]; then
- GUI=true
+ if [ $OPT = "-web" ]; then
+ WEB=true
elif [ $OPT = "-port" ]; then
shift
OPT1="$OPT1 -Dfjage.port=$1"
@@ -37,4 +36,4 @@ do
done
mkdir -p logs
-java -cp "$CLASSPATH" -Dfjage.gui=$GUI $TERMOPT $OPT1 org.arl.fjage.shell.GroovyBoot $@ etc/initrc.groovy
+java -cp "$CLASSPATH" -Dfjage.web=$WEB $OPT1 org.arl.fjage.shell.GroovyBoot $@ etc/initrc.groovy
diff --git a/src/main/groovy/org/arl/fjage/shell/BaseGroovyScript.groovy b/src/main/groovy/org/arl/fjage/shell/BaseGroovyScript.groovy
index 83490533..b438d978 100644
--- a/src/main/groovy/org/arl/fjage/shell/BaseGroovyScript.groovy
+++ b/src/main/groovy/org/arl/fjage/shell/BaseGroovyScript.groovy
@@ -12,7 +12,7 @@ package org.arl.fjage.shell;
import java.util.logging.*;
import org.arl.fjage.*;
-import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.codehaus.groovy.control.customizers.ImportCustomizer;
/**
* Methods and attributes available to Groovy scripts.
@@ -50,20 +50,39 @@ abstract class BaseGroovyScript extends Script {
*
* @param name name of class or package to import.
*/
- def shellImport(String name) {
+ def export(String name) {
Binding binding = getBinding();
if (binding.hasVariable('imports')) {
ImportCustomizer imports = binding.getVariable('imports');
- if (name.endsWith('.*')) imports.addStarImport(name[0..-3]);
- else imports.addImport(name);
+ name = name.trim();
+ if (name.startsWith("static ")) {
+ name = name.substring(7);
+ if (name.endsWith('.*')) imports.addStaticStars(name[0..-3]);
+ else {
+ int n = name.lastIndexOf('.');
+ if (n < 0) throw new FjageError('Bad static import');
+ imports.addStaticImport(name.substring(0, n), name.substring(n+1));
+ }
+ } else {
+ if (name.endsWith('.*')) imports.addStarImport(name[0..-3]);
+ else imports.addImport(name);
+ }
+ return null;
}
}
/**
- * Do not use include(), use shellImport() instead.
+ * Do not use shellImport(), use export() instead.
+ */
+ def shellImport(String name) {
+ throw new FjageError('shellImport() has been superceded by export()');
+ }
+
+ /**
+ * Do not use include(), use export() instead.
*/
def include(String name) {
- throw new FjageError('include() has been superceded by shellImport()');
+ throw new FjageError('include() has been superceded by export()');
}
/**
@@ -154,6 +173,18 @@ abstract class BaseGroovyScript extends Script {
return new AgentID(name);
}
+ /**
+ * Gets the container in which the shell is running.
+ */
+ Container getContainer() {
+ Binding binding = getBinding();
+ if (binding.hasVariable('agent')) {
+ Agent a = binding.getVariable('agent');
+ return a.getContainer();
+ }
+ return null;
+ }
+
/**
* Lists all the services, along with a list of agents that provide them.
*
@@ -305,40 +336,25 @@ abstract class BaseGroovyScript extends Script {
return who();
}
- /**
- * Display on console. This method clears the current line
- * and displays output on it, followed by a newline.
- *
- * @param s object to display.
- */
- void println(def x) {
- Binding binding = getBinding();
- if (binding.hasVariable('out')) {
- def out = binding.getVariable('out');
- if (out != null) out.println(x.toString(), OutputType.OUTPUT);
- }
+ @Override
+ void println() {
+ println('');
}
- /**
- * Display on console. This method clears the current line
- * and displays output on it, followed by a newline.
- *
- * @param s object to display.
- * @param type type of output to display.
- */
- void println(def x, OutputType type) {
- Binding binding = getBinding();
- if (binding.hasVariable('out')) {
- def out = binding.getVariable('out');
- if (out != null) out.println(x.toString(), type);
- }
+ @Override
+ void print(def x) {
+ println(x);
}
- /**
- * Do not use print(), use println() only.
- */
- void print(def x) {
- throw new FjageError("print() not supported, use println() instead");
+ @Override
+ void printf(String format, Object value) {
+ println(String.format(format, value));
+ }
+
+ @Override
+ @SuppressWarnings("overrides")
+ void printf(String format, Object[] value) {
+ println(String.format(format, value));
}
/**
@@ -387,6 +403,35 @@ abstract class BaseGroovyScript extends Script {
}
}
+ /**
+ * Run a nested Groovy script.
+ *
+ * @param file script to run.
+ * @param args arguments to pass to the script.
+ */
+ @Override
+ @SuppressWarnings("overrides")
+ void run(File file, String... args) {
+ Binding binding = getBinding();
+ def oldScript = binding.getVariable('script');
+ def oldArgs = binding.getVariable('args');
+ try {
+ if (binding.hasVariable('groovy')) {
+ GroovyShell groovy = binding.getVariable('groovy');
+ groovy.getClassLoader().clearCache();
+ List> arglist = new ArrayList>();
+ if (args != null && args.length > 0)
+ for (a in args)
+ arglist.add(a.toString());
+ binding.setVariable('script', file.getAbsoluteFile());
+ groovy.run(file, arglist);
+ }
+ } finally {
+ binding.setVariable('script', oldScript);
+ binding.setVariable('args', oldArgs);
+ }
+ }
+
/**
* Run a nested Groovy script.
*
diff --git a/src/main/groovy/org/arl/fjage/shell/GroovyBoot.java b/src/main/groovy/org/arl/fjage/shell/GroovyBoot.java
index 25a909ee..86609462 100644
--- a/src/main/groovy/org/arl/fjage/shell/GroovyBoot.java
+++ b/src/main/groovy/org/arl/fjage/shell/GroovyBoot.java
@@ -1,6 +1,6 @@
/******************************************************************************
-Copyright (c) 2013, Mandar Chitre
+Copyright (c) 2018, Mandar Chitre
This file is part of fjage which is released under Simplified BSD License.
See file LICENSE.txt or go to http://www.opensource.org/licenses/BSD-3-Clause
@@ -14,14 +14,14 @@
import java.util.*;
import java.util.logging.*;
import groovy.lang.ExpandoMetaClass;
-import org.arl.fjage.* ;
+import org.arl.fjage.*;
/**
* fjage bootloader.
*
* Usage:
*
- * java [-Djava.util.logging.config.file=logging.properties] org.arl.fjage.shell.GroovyBoot [-nocolor] [-debug:package-name] [[-arg:arg]... script-file]...
+ * java [-Djava.util.logging.config.file=logging.properties] org.arl.fjage.shell.GroovyBoot [-debug:package-name] [[-arg:arg]... script-file]...
*
*
* @author Mandar Chitre
@@ -55,13 +55,9 @@ public static void main(String[] args) {
// parse command line and execute scripts
GroovyScriptEngine engine = new GroovyScriptEngine();
- OutputShell out = new OutputShell(System.out);
List arglist = new ArrayList();
for (String a: args) {
- if (a.equals("-nocolor")) {
- Term.setDefaultState(false);
- out.disableTerm();
- } else if (a.equals("-debug")) {
+ if (a.equals("-debug")) {
log.info("Setting root logger level to ALL");
Logger logger = Logger.getLogger("");
logger.setLevel(Level.ALL);
@@ -81,18 +77,17 @@ public static void main(String[] args) {
// execute script from resource file
InputStream inp = GroovyBoot.class.getResourceAsStream(a.substring(5));
if (inp == null) throw new FileNotFoundException(a+" not found");
- engine.exec(new InputStreamReader(inp), a, arglist, out);
+ engine.exec(new InputStreamReader(inp), a, arglist);
if (arglist.size() > 0) arglist = new ArrayList();
} else if (a.startsWith("cls://")) {
// execute pre-compiled script from class file
Class> cls = Class.forName(a.substring(6));
- engine.exec(cls, arglist, out);
+ engine.exec(cls, arglist);
} else {
// execute script from file
- engine.exec(new File(a), arglist, out);
+ engine.exec(new File(a), arglist);
if (arglist.size() > 0) arglist = new ArrayList();
}
- engine.waitUntilCompletion();
}
}
engine.shutdown();
diff --git a/src/main/groovy/org/arl/fjage/shell/GroovyScriptEngine.java b/src/main/groovy/org/arl/fjage/shell/GroovyScriptEngine.java
index 13950ab0..ad9c5253 100644
--- a/src/main/groovy/org/arl/fjage/shell/GroovyScriptEngine.java
+++ b/src/main/groovy/org/arl/fjage/shell/GroovyScriptEngine.java
@@ -19,19 +19,19 @@
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.*;
import org.codehaus.groovy.GroovyBugError;
-import org.arl.fjage.Message;
+import org.arl.fjage.*;
/**
* Groovy scripting engine.
*/
-public class GroovyScriptEngine extends Thread implements ScriptEngine {
+public class GroovyScriptEngine implements ScriptEngine {
////// private attributes
private GroovyShell groovy;
private Binding binding;
- private ExecutorService executor = Executors.newSingleThreadExecutor();
- private Future> last = null;
+ private Shell out = null;
+ private Thread busy = null;
private Logger log = Logger.getLogger(getClass().getName());
////// constructor
@@ -64,174 +64,202 @@ private void init() {
////// script engine methods
@Override
- public boolean exec(final String cmd1, final Shell out) {
- if (isBusy()) return false;
+ public void bind(Shell shell) {
+ out = shell;
+ }
+
+ @Override
+ public String getPrompt(boolean cont) {
+ return cont ? "- " : "> ";
+ }
+
+ @Override
+ public boolean isComplete(String cmd) {
+ if (cmd == null || cmd.trim().length() == 0) return true;
try {
- last = executor.submit(new Callable