org.goko.controller.grbl.v08.GrblCommunicator.java Source code

Java tutorial

Introduction

Here is the source code for org.goko.controller.grbl.v08.GrblCommunicator.java

Source

/*
 *
 *   Goko
 *   Copyright (C) 2013, 2016  PsyKo
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package org.goko.controller.grbl.v08;

import java.math.BigDecimal;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.goko.controller.grbl.v08.bean.StatusReport;
import org.goko.core.common.GkUtils;
import org.goko.core.common.buffer.ByteCommandBuffer;
import org.goko.core.common.exception.GkException;
import org.goko.core.common.exception.GkFunctionalException;
import org.goko.core.common.measure.quantity.Length;
import org.goko.core.common.measure.units.Unit;
import org.goko.core.connection.DataPriority;
import org.goko.core.connection.EnumConnectionEvent;
import org.goko.core.connection.IConnectionDataListener;
import org.goko.core.connection.IConnectionListener;
import org.goko.core.connection.IConnectionService;
import org.goko.core.log.GkLog;
import org.goko.core.math.Tuple6b;

public class GrblCommunicator implements IConnectionDataListener, IConnectionListener {
    /** LOG */
    private static final GkLog LOG = GkLog.getLogger(GrblCommunicator.class);
    /** The target Grbl service */
    private GrblControllerService grbl;
    /** Grbl end line delimiter */
    private char endLineCharDelimiter;
    /** Buffer for incoming data    */
    private ByteCommandBuffer incomingBuffer;
    /** The connection service */
    private IConnectionService connectionService;

    /**
     * Constructor
     */
    protected GrblCommunicator(GrblControllerService grbl) {
        this.grbl = grbl;
        endLineCharDelimiter = '\n';
        incomingBuffer = new ByteCommandBuffer((byte) endLineCharDelimiter);
    }

    /** (inheritDoc)
     * @see org.goko.core.connection.IConnectionDataListener#onDataReceived(java.util.List)
     */
    @Override
    public void onDataReceived(List<Byte> data) throws GkException {
        incomingBuffer.addAll(data);
        while (incomingBuffer.hasNext()) {
            handleIncomingData(GkUtils.toString(incomingBuffer.unstackNextCommand()));
        }
    }

    /** (inheritDoc)
     * @see org.goko.core.connection.IConnectionDataListener#onDataSent(java.util.List)
     */
    @Override
    public void onDataSent(List<Byte> data) throws GkException {
        // TODO Auto-generated method stub

    }

    @Override
    public void onConnectionEvent(EnumConnectionEvent event) throws GkException {
        if (event == EnumConnectionEvent.CONNECTED) {
            incomingBuffer.clear();
            getConnectionService().addInputDataListener(this);
            grbl.startStatusPolling();
        } else if (event == EnumConnectionEvent.DISCONNECTED) {
            getConnectionService().removeInputDataListener(this);
            grbl.stopStatusPolling();
            grbl.setState(GrblMachineState.UNDEFINED);
            incomingBuffer.clear();
        }
    }

    /**
     * Handling of incoming data
     * @param data the received data
     * @throws GkException GkException
     */
    protected void handleIncomingData(String data) throws GkException {
        String trimmedData = StringUtils.trim(data);
        if (StringUtils.isNotEmpty(trimmedData)) {
            /* Received OK response */
            if (StringUtils.equals(trimmedData, Grbl.OK_RESPONSE)) {
                grbl.handleOkResponse();

                /* Received error  */
            } else if (StringUtils.startsWith(trimmedData, "error:")) {
                grbl.handleError(trimmedData);

                /* Received status report  */
            } else if (StringUtils.startsWith(trimmedData, "<") && StringUtils.endsWith(trimmedData, ">")) {
                grbl.handleStatusReport(parseStatusReport(trimmedData));

                /* Received Grbl header */
            } else if (StringUtils.startsWith(trimmedData, "Grbl")) {
                handleHeader(trimmedData);
                grbl.initialiseConnectedState();
                //   refreshStatus();

                /* Received a configuration confirmation */
            } else if (StringUtils.defaultString(trimmedData).matches("\\$[0-9]*=.*")) {
                grbl.handleConfigurationReading(trimmedData);

                /* Received a work position report */
            } else if (StringUtils.defaultString(trimmedData).matches("\\[G5.*\\]")) {
                Tuple6b targetPoint = new Tuple6b().setNull();
                String offsetName = parseCoordinateSystem(trimmedData, targetPoint);
                grbl.setOffsetCoordinate(offsetName, targetPoint);

                /* Received an offset position report */
            } else if (StringUtils.defaultString(trimmedData).matches("\\[(G92|G28|G30).*\\]")) {
                //            Tuple6b targetPoint = new Tuple6b().setNull();
                //            String coordinateSystemName = parseCoordinateSystem(trimmedData, targetPoint);
                //            grbl.setOffsetCoordinate(coordinateSystemName, targetPoint);
                // TODO Handle G92
                /* Parser state report */
            } else if (StringUtils.defaultString(trimmedData).matches("\\[(G0|G1|G2|G3).*\\]")) {
                grbl.receiveParserState(StringUtils.substringBetween(trimmedData, "[", "]"));
                /* Unkown format received */
            } else {
                LOG.error("Ignoring received data " + trimmedData);
                grbl.getApplicativeLogService().warning("Ignoring received data " + trimmedData,
                        GrblControllerService.SERVICE_ID);
            }
        }
    }

    /**
     * Parse Grbl header
     * @param grblHeader the received header
     */
    private void handleHeader(String grblHeader) {
        String[] tokens = StringUtils.split(grblHeader, " ");
        if (tokens != null && tokens.length >= 2) {
            LOG.info("Grbl version is " + tokens[1]);
        }
    }

    /**
     * Create a status report from the given string
     * @param strStatusReport the String representing the status report
     * @return {@link StatusReport}
     * @throws GkException
     */
    private StatusReport parseStatusReport(String strStatusReport) throws GkException {
        StatusReport result = new StatusReport();
        int comma = StringUtils.indexOf(strStatusReport, ",");
        String state = StringUtils.substring(strStatusReport, 1, comma);
        GrblMachineState grblState = grbl.getGrblStateFromString(state);
        result.setState(grblState);

        // Looking for MPosition
        String mpos = StringUtils.substringBetween(strStatusReport, "MPos:", ",WPos");
        String wpos = StringUtils.substringBetween(strStatusReport, "WPos:", ">");
        Tuple6b machinePosition = new Tuple6b().setNull();
        Tuple6b workPosition = new Tuple6b().setNull();
        String[] machineCoordinates = StringUtils.split(mpos, ",");

        parseTuple(machineCoordinates, machinePosition);
        result.setMachinePosition(machinePosition);

        String[] workCoordinates = StringUtils.split(wpos, ",");
        parseTuple(workCoordinates, workPosition);
        result.setWorkPosition(workPosition);

        return result;
    }

    private void parseTuple(String[] values, Tuple6b target) throws GkException {
        if (values != null && values.length >= 3) {
            Unit<Length> unit = grbl.getConfiguration().getReportUnit();
            if (NumberUtils.isNumber(values[0])) {
                target.setX(Length.valueOf(new BigDecimal(values[0]), unit));
            }
            if (NumberUtils.isNumber(values[1])) {
                target.setY(Length.valueOf(new BigDecimal(values[1]), unit));
            }
            if (NumberUtils.isNumber(values[2])) {
                target.setZ(Length.valueOf(new BigDecimal(values[2]), unit));
            }
        }
    }

    private String parseCoordinateSystem(String strOrigin, Tuple6b targetPoint) throws GkException {
        String identifier = StringUtils.substringBetween(strOrigin, "[", ":");
        String valuesGroup = StringUtils.substringBetween(strOrigin, ":", "]");
        String[] values = StringUtils.split(valuesGroup, ",");
        Unit<Length> unit = grbl.getConfiguration().getReportUnit();
        if (values == null || values.length < 3) {
            throw new GkFunctionalException("Received incomplete offset report " + strOrigin + ". Ignoring...");
        }

        BigDecimal x = new BigDecimal(values[0]);
        BigDecimal y = new BigDecimal(values[1]);
        BigDecimal z = new BigDecimal(values[2]);
        targetPoint.setX(Length.valueOf(x, unit));
        targetPoint.setY(Length.valueOf(y, unit));
        targetPoint.setZ(Length.valueOf(z, unit));

        return identifier;
    }

    /**
     * Add the end line character at the end of the given list
     * @param list the list
     */
    private void addEndLineCharacter(List<Byte> list) {
        //command.add(new Byte((byte) endLineCharDelimiter));
        list.add(new Byte((byte) endLineCharDelimiter));
    }

    /**
     * @return the connectionService
     */
    protected IConnectionService getConnectionService() {
        return connectionService;
    }

    /**
     * @param connectionService the connectionService to set
     * @throws GkException GkException
     */
    protected void setConnectionService(IConnectionService connectionService) throws GkException {
        this.connectionService = connectionService;
        this.connectionService.addConnectionListener(this);
    }

    protected void send(List<Byte> lstByte) throws GkException {
        addEndLineCharacter(lstByte);
        getConnectionService().send(lstByte);
    }

    protected void sendWithoutEndLineCharacter(List<Byte> lstByte) throws GkException {
        getConnectionService().send(lstByte);
    }

    protected void sendImmediately(List<Byte> lstByte) throws GkException {
        getConnectionService().send(lstByte, DataPriority.IMPORTANT);
    }
}