/**
*
*/
package org.goko.controller.g2core.controller;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.goko.controller.g2core.configuration.G2CoreConfiguration;
import org.goko.controller.g2core.preferences.G2CorePreferences;
import org.goko.controller.tinyg.commons.AbstractTinyGCommunicator;
import org.goko.controller.tinyg.commons.ITinyGStatus;
import org.goko.controller.tinyg.commons.TinyG;
import org.goko.core.common.GkUtils;
import org.goko.core.common.exception.GkException;
import org.goko.core.common.measure.quantity.Angle;
import org.goko.core.common.measure.quantity.AngleUnit;
import org.goko.core.common.measure.quantity.Length;
import org.goko.core.common.measure.quantity.Speed;
import org.goko.core.controller.bean.MachineState;
import org.goko.core.gcode.rs274ngcv3.context.CoordinateSystem;
import org.goko.core.gcode.rs274ngcv3.context.CoordinateSystemFactory;
import org.goko.core.gcode.rs274ngcv3.context.EnumDistanceMode;
import org.goko.core.gcode.rs274ngcv3.context.EnumMotionMode;
import org.goko.core.gcode.rs274ngcv3.context.EnumPlane;
import org.goko.core.gcode.rs274ngcv3.context.EnumUnit;
import org.goko.core.gcode.rs274ngcv3.context.GCodeContext;
import org.goko.core.log.GkLog;
import org.goko.core.math.Tuple6b;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
/**
* G2 Core communicator
*
* @author Psyko
* @date 8 janv. 2017
*/
public class G2CoreCommunicator extends AbstractTinyGCommunicator<G2CoreConfiguration, G2CoreControllerService> {
/** LOG */
private static final GkLog LOG = GkLog.getLogger(G2CoreCommunicator.class);
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#handleNonJsonData(java.lang.String)
*/
@Override
protected void handleNonJsonData(String data) throws GkException {
LOG.info("Received non json data ["+data+"]");
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#handleMalformedJson(java.lang.String)
*/
@Override
protected void handleMalformedJson(String data) {
LOG.info("Received malformed json data ["+data+"]");
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#onConnected()
*/
@Override
protected void onConnected() throws GkException {
getIncomingBuffer().clear();
getConnectionService().addInputDataListener(this);
forceJsonMode();
forceStatusReportFormat();
requestStatusReport();
requestQueueReport();
requestConfigurationUpdate();
requestCoordinateSystemUpdate();
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#onDisconnected()
*/
@Override
protected void onDisconnected() throws GkException {
getIncomingBuffer().clear();
getConnectionService().removeInputDataListener(this);
getControllerService().setState(G2Core.State.UNDEFINED);
getControllerService().resetConfiguration();
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#getResponseFooter(org.goko.controller.tinyg.commons.JsonValue)
*/
@Override
protected ITinyGStatus getResponseFooter(JsonValue footerBody) throws GkException {
JsonArray footerArray = footerBody.asArray();
int statusCodeIntValue = footerArray.get(TinyG.FOOTER_STATUS_CODE_INDEX).asInt();
ITinyGStatus status = G2CoreStatusCode.findEnum(statusCodeIntValue);
return status;
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#handleResponse(org.goko.controller.tinyg.commons.JsonObject, org.goko.controller.tinyg.commons.ITinyGStatus)
*/
@Override
protected void handleResponse(JsonObject responseBody, ITinyGStatus status) throws GkException {
if(CollectionUtils.isEmpty(responseBody.names())){
// Response is empty, it's a confirmation of a GCode line
handleGCodeResponse(null, status);
}else{
for(String name : responseBody.names()){
if(StringUtils.equals(name, G2Core.STATUS_REPORT)){
handleStatusReport((JsonObject)responseBody.get(G2Core.STATUS_REPORT));
}else if(StringUtils.equals(name, G2Core.QUEUE_REPORT)){
handleQueueReport(responseBody.get(G2Core.QUEUE_REPORT));
}else if(StringUtils.equals(name, G2Core.GCODE_COMMAND)){
handleGCodeResponse(responseBody.get(G2Core.GCODE_COMMAND).asString(), status);
}else if(StringUtils.equals(name, G2Core.LINE_REPORT)){
// LOG.info("Skipping line report "+String.valueOf(responseEnvelope.get(name)));
}else if(StringUtils.equals(name, G2Core.PROBE_REPORT)){
handleProbeReport(responseBody.get(G2Core.PROBE_REPORT));
}else if(StringUtils.equals(name, G2Core.MESSAGE_REPORT)){
handleMessage(responseBody.get(G2Core.MESSAGE_REPORT));
}else if(StringUtils.defaultString(name).matches("(g|G)5(4|5|6|7|8|9)")){
handleCoordinateSystemOffsetReport(name, responseBody.get(name));
}else{
handleConfigurationModification(responseBody);
}
}
}
}
/**
* Handle the reception of a message
* @param jsonValue
* @throws GkException GkException
*/
private void handleMessage(JsonValue message) throws GkException {
getApplicativeLogService().info(message.asString(), "G2 Core");
getControllerService().setMessage(message.asString());
}
/**
* Receives configuration data
* @param responseBody the JSON containing the configurations
* @throws GkException GkException
*/
private void handleConfigurationModification(JsonObject responseBody) throws GkException {
G2CoreConfiguration cfg = getControllerService().getConfiguration().getCopy();
cfg.setFromJson(responseBody);
getControllerService().setConfiguration(cfg);
}
/**
* @param status
* @throws GkException
*/
private void handleGCodeResponse(String command, ITinyGStatus status) throws GkException {
getControllerService().handleGCodeResponse(command, status);
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#handleStatusReport(org.goko.controller.tinyg.commons.JsonObject)
*/
@Override
protected void handleStatusReport(JsonObject statusReportBody) throws GkException {
if(statusReportBody.isObject()){
JsonObject statusReportObject = (JsonObject) statusReportBody;
EnumUnit units = findUnits(statusReportObject);
Tuple6b workPosition = findWorkPosition(statusReportObject, units);
Tuple6b machinePosition= findMachinePosition(statusReportObject, units);
MachineState state = findState(statusReportObject);
EnumDistanceMode distanceMode = findDistanceMode(statusReportObject);
Speed velocity = findVelocity(statusReportObject, units);
Speed feedrate = findFeedrate(statusReportObject, units);
CoordinateSystem cs = findCoordinateSystem(statusReportObject);
EnumPlane plane = findPlane(statusReportObject);
EnumMotionMode motionMode = findMotionMode(statusReportObject);
GCodeContext gcodeContext = new GCodeContext(getControllerService().getGCodeContext());
gcodeContext.setPosition(workPosition);
gcodeContext.setMachinePosition(machinePosition);
gcodeContext.setDistanceMode(distanceMode);
gcodeContext.setUnit(units);
gcodeContext.setCoordinateSystem(cs);
gcodeContext.setFeedrate(feedrate);
gcodeContext.setMachinePosition(machinePosition);
gcodeContext.setPlane(plane);
gcodeContext.setMotionMode(motionMode);
if(state != null){
getControllerService().setState(state);
}
if(velocity != null){
getControllerService().setVelocity(velocity);
}
getControllerService().updateGCodeContext(gcodeContext);
}
}
/**
* Extract state from status report
* @param statusReport
* @return
*/
private MachineState findState(JsonObject statusReport){
JsonValue statReport = statusReport.get(G2Core.STATUS_REPORT_STATE);
if(statReport != null){
switch(statReport.asInt()){
case 0: return MachineState.INITIALIZING;
case 1: return MachineState.READY;
case 2: return MachineState.ALARM;
case 3: return MachineState.PROGRAM_STOP;
case 4: return MachineState.PROGRAM_END;
case 5: return MachineState.MOTION_RUNNING;
case 6: return MachineState.MOTION_HOLDING;
case 7: return MachineState.PROBE_CYCLE;
//case 8: return MachineState.CYCLE;
case 9: return MachineState.HOMING;
default: return MachineState.UNDEFINED;
}
}
return null;
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#handleQueueReport(org.goko.controller.tinyg.commons.JsonValue)
*/
@Override
protected void handleQueueReport(JsonValue queueReportBody) throws GkException {
getControllerService().setAvailablePlannerBuffer(queueReportBody.asInt());
}
/** (inheritDoc)
* @see org.goko.controller.tinyg.commons.AbstractTinyGCommunicator#handleErrorReport(org.goko.controller.tinyg.commons.JsonObject)
*/
@Override
protected void handleErrorReport(JsonObject errorReportBody) throws GkException {
//{"fb":100.17,"st":94,"msg":"Planner assertion failure - mp_exec_aline() zero length move"}
JsonValue errorMessage = errorReportBody.get(G2Core.MESSAGE_REPORT);
getApplicativeLogService().error(errorMessage.asString(), "G2 Core");
}
protected void handleProbeReport(JsonValue probeReport) throws GkException {
if(probeReport.isObject()){
Tuple6b probePosition = null;
JsonObject probeReportObject = (JsonObject) probeReport;
JsonValue eProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_SUCCESS);
boolean probeSuccess = (eProbeResult.asInt() == 1);
if(probeSuccess){
JsonValue xProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_POSITION_X);
JsonValue yProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_POSITION_Y);
JsonValue zProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_POSITION_Z);
JsonValue aProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_POSITION_A);
JsonValue bProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_POSITION_B);
JsonValue cProbeResult = probeReportObject.get(G2Core.PROBE_REPORT_POSITION_C);
probePosition = new Tuple6b();
if(xProbeResult != null){
probePosition.setX( Length.valueOf(xProbeResult.asBigDecimal(), getControllerService().getCurrentUnit()) );
}
if(yProbeResult != null){
probePosition.setY( Length.valueOf(yProbeResult.asBigDecimal(), getControllerService().getCurrentUnit()) );
}
if(zProbeResult != null){
probePosition.setZ( Length.valueOf(zProbeResult.asBigDecimal(), getControllerService().getCurrentUnit()) );
}
if(aProbeResult != null){
probePosition.setA( Angle.valueOf(aProbeResult.asBigDecimal(), AngleUnit.DEGREE_ANGLE) );
}
if(bProbeResult != null){
probePosition.setB( Angle.valueOf(bProbeResult.asBigDecimal(), AngleUnit.DEGREE_ANGLE) );
}
if(cProbeResult != null){
probePosition.setC( Angle.valueOf(cProbeResult.asBigDecimal(), AngleUnit.DEGREE_ANGLE) );
}
}
getControllerService().handleProbeResult(probeSuccess, probePosition);
}
}
/**
* Handle the reception of a coordinate offset report
* @param offsetName the name of the offset
* @param jsonOffset the JSON description
* @throws GkException GkException
*/
private void handleCoordinateSystemOffsetReport(String offsetName, JsonValue jsonOffset) throws GkException{
CoordinateSystem cs = new CoordinateSystemFactory().get(StringUtils.upperCase(offsetName));
JsonObject offsetObj = (JsonObject) jsonOffset;
JsonValue xOffset = offsetObj.get("x");
JsonValue yOffset = offsetObj.get("y");
JsonValue zOffset = offsetObj.get("z");
JsonValue aOffset = offsetObj.get("a");
JsonValue bOffset = offsetObj.get("b");
JsonValue cOffset = offsetObj.get("c");
Tuple6b offset = new Tuple6b().setZero();
offset.setX( Length.valueOf(xOffset.asBigDecimal(), getControllerService().getCurrentUnit() ));
offset.setY( Length.valueOf(yOffset.asBigDecimal(), getControllerService().getCurrentUnit() ) );
offset.setZ( Length.valueOf(zOffset.asBigDecimal(), getControllerService().getCurrentUnit() ) );
if(aOffset != null){
offset.setA( Angle.valueOf(aOffset.asBigDecimal(), AngleUnit.DEGREE_ANGLE ) );
}
if(bOffset != null){
offset.setB( Angle.valueOf(bOffset.asBigDecimal(), AngleUnit.DEGREE_ANGLE ) );
}
if(cOffset != null){
offset.setC( Angle.valueOf(cOffset.asBigDecimal(), AngleUnit.DEGREE_ANGLE ) );
}
getControllerService().setCoordinateSystemOffset(cs, offset);
}
/**
* Entry point for Kill Alarm action
* @throws GkException GkException
*/
public void killAlarm() throws GkException{
send(buildJsonQuery(G2Core.KILL_ALARM_HEADER), true);
}
/**
* Entry point for Stop Motion action
* @throws GkException GkException
*/
public void stopMotion() throws GkException{
sendImmediately(G2Core.FEED_HOLD_COMMAND, true);
sendImmediately(G2Core.QUEUE_FLUSH_COMMAND, true);
}
/**
* Entry point for Pause Motion action
* @throws GkException GkException
*/
public void pauseMotion() throws GkException{
sendImmediately(G2Core.FEED_HOLD_COMMAND, false);
}
/**
* Entry point for Resume motion action
* @throws GkException GkException
*/
public void resumeMotion() throws GkException{
sendImmediately(G2Core.CYCLE_START_COMMAND, false);
}
/**
* Entry point for Start Motion action
* @throws GkException GkException
*/
public void startMotion() throws GkException{
sendImmediately(G2Core.CYCLE_START_COMMAND, false);
}
/**
* Entry point for Reset action
* @throws GkException GkException
*/
public void resetG2Core() throws GkException{
sendImmediately(G2Core.RESET_COMMAND, false);
}
/**
* Entry point for Turn Spindle On action
* @throws GkException GkException
*/
public void turnSpindleOn() throws GkException{
send(G2Core.TURN_SPINDLE_ON_GCODE, true);
}
/**
* Entry point for Turn Spindle Off action
* @throws GkException GkException
*/
public void turnSpindleOff() throws GkException{
send(G2Core.TURN_SPINDLE_OFF_GCODE, true);
}
/**
* Entry point for Reset Zero action
* @throws GkException GkException
*/
public void resetZero(List<String> axes) throws GkException{
List<Byte> lstBytes = GkUtils.toBytesList("G28.3");
if(CollectionUtils.isNotEmpty(axes)){
for (String axe : axes) {
lstBytes.addAll(GkUtils.toBytesList(axe+"0"));
}
}else{
lstBytes.addAll( GkUtils.toBytesList("X0Y0Z0"));
}
send(lstBytes, true);
}
/**
* Sends a JSon command to set the format of the returned status report
* @throws GkException GkException
*/
public void forceStatusReportFormat() throws GkException{
JsonObject statusReportFormat = new JsonObject();
G2CorePreferences prefs = G2CorePreferences.getInstance();
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_MACHINE_STATE)){
statusReportFormat.add(G2Core.STATUS_REPORT_STATE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_VELOCITY)){
statusReportFormat.add(G2Core.STATUS_REPORT_VELOCITY, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_FEEDRATE)){
statusReportFormat.add(G2Core.STATUS_REPORT_FEEDRATE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_UNITS)){
statusReportFormat.add(G2Core.STATUS_REPORT_UNITS, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_COORDINATE_SYSTEM)){
statusReportFormat.add(G2Core.STATUS_REPORT_COORDINATES, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_MOTION_MODE)){
statusReportFormat.add(G2Core.STATUS_REPORT_MOTION_MODE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_PLANE)){
statusReportFormat.add(G2Core.STATUS_REPORT_PLANE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_PATH_CONTROL)){
statusReportFormat.add(G2Core.STATUS_REPORT_PATH_CONTROL, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_DISTANCE_MODE)){
statusReportFormat.add(G2Core.STATUS_REPORT_DISTANCE_MODE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_ARC_DISTANCE_MODE)){
statusReportFormat.add(G2Core.STATUS_REPORT_ARC_DISTANCE_MODE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_FEEDRATE_MODE)){
statusReportFormat.add(G2Core.STATUS_REPORT_FEEDRATE_MODE, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_TOOL)){
statusReportFormat.add(G2Core.STATUS_REPORT_TOOL, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_G92)){
statusReportFormat.add(G2Core.STATUS_REPORT_G92, true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_MPOS)){
addPositionReport(G2Core.STATUS_REPORT_MACHINE_POSITION, statusReportFormat);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_WPOS)){
addPositionReport(G2Core.STATUS_REPORT_WORK_POSITION, statusReportFormat);
}
JsonObject statusReportEnveloppe= new JsonObject();
statusReportEnveloppe.add("sr", statusReportFormat);
send(statusReportEnveloppe, true);
}
/**
* Builds the position report mask based on G2Core preferences
* @param positionPrefix the prefix for the position report (mpos, pos, ...)
* @param statusReportFormat the target json object for SR format
*/
private void addPositionReport(String positionPrefix, JsonObject statusReportFormat){
G2CorePreferences prefs = G2CorePreferences.getInstance();
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_POS_X)){
statusReportFormat.add(positionPrefix+"x", true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_POS_Y)){
statusReportFormat.add(positionPrefix+"y", true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_POS_Z)){
statusReportFormat.add(positionPrefix+"z", true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_POS_A)){
statusReportFormat.add(positionPrefix+"a", true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_POS_B)){
statusReportFormat.add(positionPrefix+"b", true);
}
if(prefs.getBoolean(G2CorePreferences.STATUS_REPORT_POS_C)){
statusReportFormat.add(positionPrefix+"c", true);
}
}
}