/*
* 作成日: 2008/05/05
*/
package jp.ac.fit.asura.nao.glue;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.vecmath.Vector3f;
import jp.ac.fit.asura.nao.Image;
import jp.ac.fit.asura.nao.Joint;
import jp.ac.fit.asura.nao.RobotContext;
import jp.ac.fit.asura.nao.VisualCycle;
import jp.ac.fit.asura.nao.VisualFrameContext;
import jp.ac.fit.asura.nao.Camera.CameraID;
import jp.ac.fit.asura.nao.Camera.CameraParam;
import jp.ac.fit.asura.nao.Camera.PixelFormat;
import jp.ac.fit.asura.nao.misc.MathUtils;
import jp.ac.fit.asura.nao.misc.Pixmap;
import jp.ac.fit.asura.nao.misc.TeeOutputStream;
import jp.ac.fit.asura.nao.motion.Motion;
import jp.ac.fit.asura.nao.motion.MotionParam.CircleTurnParam.Side;
import jp.ac.fit.asura.nao.motion.Motions;
import jp.ac.fit.asura.nao.motion.MotorCortex;
import jp.ac.fit.asura.nao.motion.motions.CartesianMotion;
import jp.ac.fit.asura.nao.motion.motions.CompatibleMotion;
import jp.ac.fit.asura.nao.motion.motions.ForwardMotion;
import jp.ac.fit.asura.nao.motion.motions.LinerMotion;
import jp.ac.fit.asura.nao.motion.motions.RawMotion;
import jp.ac.fit.asura.nao.motion.motions.TimedMotion;
import jp.ac.fit.asura.nao.motion.motions.CartesianMotion.ChainFrame;
import jp.ac.fit.asura.nao.motion.motions.CartesianMotion.DataFrame;
import jp.ac.fit.asura.nao.physical.Robot;
import jp.ac.fit.asura.nao.physical.RobotFrame;
import jp.ac.fit.asura.nao.physical.Robot.Frames;
import jp.ac.fit.asura.nao.strategy.Role;
import jp.ac.fit.asura.nao.strategy.Task;
import jp.ac.fit.asura.nao.strategy.Team;
import jp.ac.fit.asura.nao.strategy.schedulers.Scheduler;
import jp.ac.fit.asura.nao.strategy.schedulers.WalkConfigScheduler;
import jp.ac.fit.asura.nao.strategy.schedulers.WalkConfigScheduler.ConfigMode;
import jp.ac.fit.asura.nao.vision.GCD;
import jp.ac.fit.asura.nao.vision.VisualContext;
import jp.ac.fit.asura.nao.vision.VisualParam;
import jscheme.JScheme;
import jsint.BacktraceException;
import jsint.Pair;
import jsint.U;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.net.TelnetAppender;
/**
* @author sey
*
* @version $Id: SchemeGlue.java 717 2008-12-31 18:16:20Z sey $
*
*/
public class SchemeGlue implements VisualCycle {
private static final Logger log = Logger.getLogger(SchemeGlue.class);
public enum InterpolationType {
Raw(1), Liner(2), Compatible(3), Timed(4), TimedDeg(5), Cartesian(6);
private final int id;
InterpolationType(int id) {
this.id = id;
}
public static InterpolationType valueOf(int id) {
for (InterpolationType t : InterpolationType.values()) {
if (t.id == id)
return t;
}
return null;
}
}
private JScheme js;
private MotorCortex motor;
private TinyHttpd httpd;
private RobotContext rctx;
private int saveImageInterval;
private TeeOutputStream outputStreams;
private TeeOutputStream errorStreams;
/**
*
*/
public SchemeGlue() {
js = new JScheme();
httpd = new TinyHttpd();
outputStreams = new TeeOutputStream(System.out);
errorStreams = new TeeOutputStream(System.err);
try {
outputStreams.addStream(new FileOutputStream("output.log"));
} catch (IOException e) {
log.error("Can't open output.log");
}
try {
errorStreams.addStream(new FileOutputStream("error.log"));
} catch (IOException e) {
log.error("Can't open error.log");
}
js.getEvaluator().setOutput(new PrintWriter(outputStreams));
js.getEvaluator().setError(new PrintWriter(errorStreams));
}
@Override
public void init(RobotContext context) {
log.info("Init SchemeGlue.");
this.rctx = context;
motor = context.getMotor();
// Declare global values
js.setGlobalValue("glue", this);
js.setGlobalValue("robot-context", rctx);
// Declare joint definition
for (Frames frame : Frames.values()) {
js.setGlobalValue(frame.name(), frame.ordinal());
}
saveImageInterval = 0;
httpd.init(rctx);
}
@Override
public void start() {
log.info("Start SchemeGlue.");
try {
log.debug(Charset.defaultCharset());
ClassLoader cl = getClass().getClassLoader();
InputStream is = cl.getResourceAsStream("init.scm");
if (is == null)
throw new FileNotFoundException("Can't get resource init.scm");
Reader reader = new InputStreamReader(is, Charset.forName("UTF-8"));
js.load(reader);
} catch (BacktraceException e) {
log.fatal("", e.getBaseException());
} catch (IOException e) {
log.fatal("", e);
}
log.info("SchemeGlue started.");
}
@Override
public void step(VisualFrameContext context) {
if (saveImageInterval != 0
&& context.getFrame() % saveImageInterval == 0) {
log.debug("save image.");
VisualContext ctx = context.getVisualContext();
Image image = ctx.image;
byte[] yvuPlane = new byte[image.getWidth() * image.getHeight() * 3];
if (image.getPixelFormat() == PixelFormat.RGB444) {
GCD.rgb2yvu(image.getIntBuffer(), yvuPlane);
} else if (image.getPixelFormat() == PixelFormat.YUYV) {
GCD.yuyv2yvu(image.getByteBuffer(), yvuPlane);
} else {
assert false;
yvuPlane = null;
}
Pixmap ppm = new Pixmap(yvuPlane, image.getWidth(), image
.getHeight(), 255);
try {
ppm.write("snapshot/image" + context.getFrame() + ".ppm");
} catch (Exception e) {
log.error("", e);
}
}
}
@Override
public void stop() {
httpd.stop();
}
public Object getValue(String key) {
return js.getGlobalValue(key);
}
public <T> T getValue(Class<T> clazz, String key) {
Object o = js.getGlobalValue(key);
return clazz.isInstance(o) ? (T) o : null;
}
public void setValue(String key, Object obj) {
js.setGlobalValue(key, obj);
}
public Object eval(String expression) {
return js.eval(expression);
}
public boolean load(String expression) {
return js.load(expression) == U.TRUE;
}
public boolean load(Reader reader) {
return js.load(reader) == U.TRUE;
}
public void glueStartHttpd(int port) {
assert port > 0;
if (httpd.isRunning()) {
log.error("httpd is already running");
return;
}
httpd.start(port);
}
public void glueStopHttpd() {
if (!httpd.isRunning()) {
log.error("httpd isn't running");
return;
}
httpd.stop();
}
public void glueStartLogd(int port) {
TelnetAppender tel = (TelnetAppender) Logger.getRootLogger()
.getAppender("telnet");
if (tel != null) {
tel.close();
} else {
tel = new TelnetAppender();
tel.setName("telnet");
tel.setLayout(new PatternLayout("%d %5p %c{1} - %m%n"));
}
tel.setPort(port);
tel.activateOptions();
Logger.getRootLogger().addAppender(tel);
log.info("logd started");
}
public void glueStopLogd() {
log.info("logd is going to stop");
TelnetAppender tel = (TelnetAppender) Logger.getRootLogger()
.getAppender("telnet");
if (tel != null)
tel.close();
}
public void glueSetSaveImageInterval(int interval) {
saveImageInterval = interval;
}
public void glueSetLogLevel(String loggerName, String levelType) {
Level level = Level.toLevel(levelType);
if (level == null) {
log.error("Illegal level:" + levelType);
return;
}
Logger logger = Logger.getLogger(loggerName);
if (logger == null) {
log.error("Logger not found. :" + loggerName);
return;
}
logger.setLevel(level);
}
public void glueSetParam(VisualParam.Boolean key, boolean value) {
log.info("glueSetParam key: " + key + " value: " + value);
rctx.getVision().setParam(key, value);
}
public void glueSetParam(VisualParam.Float key, float value) {
log.info("glueSetParam key: " + key + " value: " + value);
rctx.getVision().setParam(key, value);
}
public void glueSetParam(VisualParam.Int key, int value) {
log.info("glueSetParam key: " + key + " value: " + value);
rctx.getVision().setParam(key, value);
}
public void mcRegistmotion(int id, Object obj) {
if (obj instanceof Motion) {
Motion motion = (Motion) obj;
motion.setId(id);
motor.registMotion(motion);
}
}
public void mcRegistmotion(int id, String name, int interpolationType,
Object[] scmArgs) {
try {
InterpolationType type = InterpolationType
.valueOf(interpolationType);
log.trace("mcRegistMotion" + id + ", " + name + "," + type + "("
+ interpolationType + ")" + Arrays.toString(scmArgs));
if (id < 0)
throw new IllegalArgumentException(
"id must be positive or zero.");
if (type == null)
throw new IllegalArgumentException("invalid interpolationType.");
Motion motion;
// 引数の型を変換してモーションを作成
switch (type) {
case Raw: {
motion = new RawMotion(matrix2float(scmArgs));
break;
}
case Compatible:
case Liner:
case Timed:
case TimedDeg: {
if (scmArgs.length != 2)
throw new IllegalArgumentException("args must be 2.");
Object[] frames = (Object[]) scmArgs[0];
Object[] frameStep = (Object[]) scmArgs[1];
if (frames.length != frameStep.length)
throw new IllegalArgumentException(
"args length must be equal. but " + frames.length
+ " and " + frameStep.length);
float[] a1 = matrix2float(frames);
int[] a2 = array2int(frameStep);
switch (type) {
case Compatible:
if (id == Motions.MOTION_YY_FORWARD) {
motion = new ForwardMotion(a1, a2);
} else {
motion = new CompatibleMotion(a1, a2);
}
break;
case TimedDeg:
toRad(a1);
case Timed:
motion = new TimedMotion(a1, a2);
break;
case Liner:
default:
motion = new LinerMotion(a1, a2);
}
log.debug("new motion " + name + " id: " + id
+ " is registered. frames: " + frames.length);
break;
}
case Cartesian: {
if (scmArgs.length != 2)
throw new IllegalArgumentException("args must be 2.");
Object[] frames = (Object[]) scmArgs[0];
Object[] frameStep = (Object[]) scmArgs[1];
// Object[] options = (Object[]) scmArgs[2];
if (frames.length != frameStep.length)
throw new IllegalArgumentException(
"args length must be equal. but " + frames.length
+ " and " + frameStep.length);
List<DataFrame> args = new ArrayList<DataFrame>();
int[] a2 = array2int(frameStep);
for (int i = 0; i < frames.length; i++) {
DataFrame data = new DataFrame();
data.time = a2[i];
data.chains = new ArrayList<ChainFrame>();
Object[] frame = (Object[]) frames[i];
for (Object chainFrameObj : frame) {
ChainFrame e = new ChainFrame();
Object[] chainFrame = (Object[]) chainFrameObj;
if (chainFrame.length < 2)
throw new IllegalArgumentException(
"chainFrame length must be 2 or more.");
Object[] pos = (Object[]) chainFrame[1];
if (pos.length != 6)
throw new IllegalArgumentException(
"posture length must be 6.");
Frames chain = Frames.valueOf(chainFrame[0].toString());
Vector3f v1 = new Vector3f();
v1.x = Float.parseFloat(pos[0].toString());
v1.y = Float.parseFloat(pos[1].toString());
v1.z = Float.parseFloat(pos[2].toString());
Vector3f v2 = new Vector3f();
v2.x = Float.parseFloat(pos[3].toString());
v2.y = Float.parseFloat(pos[4].toString());
v2.z = Float.parseFloat(pos[5].toString());
v2.scale(1 / (180.0f / MathUtils.PIf));
e.chainId = chain;
e.position = v1;
e.postureYpr = v2;
if (chainFrame.length == 3) {
Object[] w = (Object[]) chainFrame[2];
if (w.length != 6)
throw new IllegalArgumentException(
"weight length must be 6.");
v1 = new Vector3f();
v1.x = Float.parseFloat(w[0].toString());
v1.y = Float.parseFloat(w[1].toString());
v1.z = Float.parseFloat(w[2].toString());
v2 = new Vector3f();
v2.x = Float.parseFloat(w[3].toString());
v2.y = Float.parseFloat(w[4].toString());
v2.z = Float.parseFloat(w[5].toString());
e.positionWeight = v1;
e.postureWeight = v1;
} else {
e.positionWeight = new Vector3f(1, 1, 1);
e.postureWeight = new Vector3f(1, 1, 1);
}
data.chains.add(e);
}
args.add(data);
}
motion = new CartesianMotion(
rctx.getSensoryCortex().getRobot(), args);
log.debug("new motion " + name + " id: " + id
+ " is registered. frames: " + frames.length);
break;
}
default:
assert false;
motion = null;
}
motion.setName(name);
motion.setId(id);
motor.registMotion(motion);
} catch (NumberFormatException e) {
log.error("", e);
} catch (ClassCastException e) {
log.error("", e);
} catch (IllegalArgumentException e) {
log.error("", e);
} catch (Exception e) {
log.fatal("", e);
assert false;
}
}
public void mcMakemotion(int id) {
if (!motor.hasMotion(id)) {
log.error("Motion " + id + " notfound.");
return;
}
log.info("makemotion:" + id);
motor.makemotion(id);
}
public void mcMotorPower(float power) {
log.info("Motor Power " + power * 100 + "%");
rctx.getEffector().setPower(power);
}
public void mcJointPower(String joint, float power) {
log.info(joint + " Power " + power * 100 + "%");
Joint j = Joint.valueOf(joint);
if (j == null) {
log.error("mcJointPower: Invalid Joint " + j);
return;
}
rctx.getEffector().setPower(j, power);
}
public RobotFrame scCreateFrame(int frameId, Pair list) {
Frames frame = (Frames.values())[frameId];
RobotFrame rf = new RobotFrame(frame);
assert U.isList(list.getFirst());
log.debug("create new frame " + frame);
for (Object o : U.listToVector(list.getFirst())) {
assert o instanceof Pair;
Pair pair = (Pair) o;
String str = pair.getFirst().toString();
if (str.equals("translation")) {
Object[] vec = U.listToVector(pair.getRest());
rf.getTranslation().x = Float.parseFloat(vec[0].toString());
rf.getTranslation().y = Float.parseFloat(vec[1].toString());
rf.getTranslation().z = Float.parseFloat(vec[2].toString());
log.trace("set " + frame + " translation "
+ rf.getTranslation());
} else if (str.equals("axis")) {
Object[] vec = U.listToVector(pair.getRest());
rf.getAxis().x = Float.parseFloat(vec[0].toString());
rf.getAxis().y = Float.parseFloat(vec[1].toString());
rf.getAxis().z = Float.parseFloat(vec[2].toString());
log.trace("set " + frame + " axis " + rf.getAxis());
} else if (str.equals("max")) {
float max = Float.parseFloat(pair.getRest().toString());
rf.setMaxAngle(max);
log.trace("set " + frame + " max angle " + max);
} else if (str.equals("min")) {
float min = Float.parseFloat(pair.getRest().toString());
rf.setMinAngle(min);
log.trace("set " + frame + " min angle " + min);
} else if (str.equals("mass")) {
float mass = Float.parseFloat(pair.getRest().toString());
rf.setMass(mass);
log.trace("set " + frame + " mass " + mass);
} else if (str.equals("centerOfMass")) {
Object[] vec = U.listToVector(pair.getRest());
rf.getCenterOfMass().x = Float.parseFloat(vec[0].toString());
rf.getCenterOfMass().y = Float.parseFloat(vec[1].toString());
rf.getCenterOfMass().z = Float.parseFloat(vec[2].toString());
log.trace("set " + frame + " com " + rf.getCenterOfMass());
} else if (str.equals("angle")) {
float angle = Float.parseFloat(pair.getRest().toString());
rf.getAxis().angle = angle;
log.trace("set " + frame + " angle " + angle);
} else {
log.warn("unknown parameter " + str);
assert false : "unknown parameter " + str;
}
}
return rf;
}
public Robot scCreateRobot(Pair args) {
RobotFrame root = setRobotRecur(args);
log.info("root:" + root.getId());
root.calculateGrossMass();
return new Robot(root);
}
public void scSetRobot(Robot robot) {
rctx.getSensoryCortex().updateRobot(robot);
}
private RobotFrame setRobotRecur(Pair list) {
RobotFrame parent = null;
assert U.isList(list);
assert U.isList(list.getRest());
Object first = list.getFirst();
Pair rest = (Pair) list.getRest();
if (U.isList(first)) {
parent = setRobotRecur((Pair) first);
} else if (first instanceof RobotFrame) {
parent = (RobotFrame) first;
} else {
assert false;
throw new IllegalArgumentException("must be List or RobotFrame");
}
if (rest != Pair.EMPTY) {
RobotFrame child = setRobotRecur(rest);
child.setParent(parent);
log.debug("add child " + child.getId() + " to " + parent.getId());
RobotFrame[] children = Arrays.copyOf(parent.getChildren(), parent
.getChildren().length + 1);
children[children.length - 1] = child;
parent.setChildren(children);
}
return parent;
}
public void ssSetScheduler(String schedulerName) {
Task task = rctx.getStrategy().getTaskManager().find(schedulerName);
if (task == null) {
log.error("task not found:" + schedulerName);
} else if (task instanceof Scheduler) {
log.info("set scheduler " + schedulerName);
rctx.getStrategy().setNextScheduler((Scheduler) task);
} else {
log.error("task is not scheduler:" + schedulerName);
}
}
public void ssSetRole(String roleId) {
Role role = Role.valueOf(roleId);
if (role == null) {
log.error("Invalid role:" + roleId);
return;
}
rctx.getStrategy().setRole(role);
}
public void ssSetTeam(String teamId) {
Team team = Team.valueOf(teamId);
if (team == null) {
log.error("Invalid team:" + teamId);
return;
}
rctx.getStrategy().setTeam(team);
}
public void ssAbortTask() {
rctx.getStrategy().getScheduler().abort();
}
public void ssPushQueue(String taskName) {
Task task = rctx.getStrategy().getTaskManager().find(taskName);
if (task == null) {
log.error("Invalid task name:" + taskName);
return;
}
rctx.getStrategy().getScheduler().pushQueue(task);
}
@Deprecated
public int vcGetParam(int controlId) {
log.warn("vcGetParam without CameraID is not recommended.");
return vcGetParam2(rctx.getCamera().getSelectedId().name(), controlId);
}
public int vcGetParam2(String camera, int controlId) {
if (controlId < 0 || controlId >= CameraParam.values().length) {
log.error("vcSetParam: Invalid Control:" + controlId);
return 0;
}
CameraParam cp = CameraParam.values()[controlId];
if (!rctx.getCamera().isSupported(cp)) {
log.error("vcSetParam: Unsupported Control:" + cp);
return 0;
}
CameraID id = CameraID.valueOf(camera);
if (id == null) {
log.error("Invalid CameraId:" + camera);
return 0;
}
return rctx.getCamera().getParam(id, cp);
}
@Deprecated
public void vcSetParam(int controlId, int value) {
log.warn("vcSetParam without CameraID is not recommended.");
vcSetParam2(CameraID.TOP.name(), controlId, value);
vcSetParam2(CameraID.BOTTOM.name(), controlId, value);
}
public void vcSetParam2(String camera, int controlId, int value) {
if (controlId < 0 || controlId >= CameraParam.values().length) {
log.error("vcSetParam: Invalid Control:" + controlId);
return;
}
CameraParam cp = CameraParam.values()[controlId];
if (!rctx.getCamera().isSupported(cp)) {
log.error("vcSetParam: Unsupported Control:" + cp);
return;
}
CameraID id = CameraID.valueOf(camera);
if (id == null) {
log.error("Invalid CameraId:" + camera);
return;
}
rctx.getCamera().setParam(id, cp, value);
}
public void vcSelectCamera(String camera) {
// FIXME VCのスレッドをロックしないと切り替え処理がおかしくなる.
CameraID id = CameraID.valueOf(camera);
if (id == null) {
log.error("Invalid CameraId:" + camera);
return;
}
rctx.getCamera().selectCamera(id);
}
public void vcLoadTMap(String fileName) {
GCD gcd = new GCD();
try {
gcd.loadTMap(fileName);
rctx.getVision().setGCD(gcd);
} catch (IOException e) {
log.error("vcLoadTMap failed.", e);
}
}
private float[] matrix2float(Object[] matrix) {
assert matrix[0].getClass().isArray();
int rows = matrix.length;
int cols = ((Object[]) matrix[0]).length;
float[] a1 = new float[rows * cols];
for (int i = 0; i < rows; i++) {
assert matrix[i].getClass().isArray();
Object[] row = (Object[]) matrix[i];
for (int j = 0; j < cols; j++) {
try {
a1[i * cols + j] = Float.parseFloat(row[j].toString());
} catch (NumberFormatException nfe) {
log.error("", nfe);
}
}
}
return a1;
}
private float[] array2float(Object[] array) {
float[] floatArray = new float[array.length];
for (int i = 0; i < array.length; i++) {
try {
floatArray[i] = Float.parseFloat(array[i].toString());
} catch (NumberFormatException nfe) {
log.error("", nfe);
}
}
return floatArray;
}
private int[] array2int(Object[] array) {
int[] floatArray = new int[array.length];
for (int i = 0; i < array.length; i++) {
try {
floatArray[i] = Integer.parseInt(array[i].toString());
} catch (NumberFormatException nfe) {
log.error("", nfe);
}
}
return floatArray;
}
private void toRad(float[] a) {
for (int i = 0; i < a.length; i++) {
a[i] = MathUtils.toRadians(a[i]);
}
}
/**
* 歩き調整用スケジューラのモード設定(NaojiWalker)
*/
public void setWalkConfigMode(String mode) {
WalkConfigScheduler.ConfigMode newMode = ConfigMode.valueOf(mode);
WalkConfigScheduler wfs = (WalkConfigScheduler)rctx.getStrategy().getTaskManager().find("WalkConfigScheduler");
wfs.setMode(newMode);
}
/**
* 歩き調整用スケジューラのモード設定(NaojiCircleTurn)
* @param mode
* @param side
*/
public void setWalkConfigMode(String mode, String side) {
WalkConfigScheduler.ConfigMode newMode = ConfigMode.valueOf(mode);
Side newSide = Side.valueOf(side);
WalkConfigScheduler wfs = (WalkConfigScheduler)rctx.getStrategy().getTaskManager().find("WalkConfigScheduler");
wfs.setMode(newMode, newSide);
}
}