All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cl.transbank.pos.utils.Serial Maven / Gradle / Ivy

package cl.transbank.pos.utils;

import cl.transbank.pos.exceptions.common.TransbankException;
import cl.transbank.pos.responses.common.IntermediateResponse;
import com.fazecast.jSerialComm.SerialPort;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import java.nio.charset.StandardCharsets;
import java.util.*;

@Log4j2
public class Serial {
    protected static final byte ACK = 0x06;
    public static final int DEFAULT_TIMEOUT = 150000;
    public static final int DEFAULT_BAUDRATE = 115200;
    private static final char STX = '\u0002';
    private static final char ETX = '\u0003';

    @Getter @Setter
    private int timeout = DEFAULT_TIMEOUT;
    protected String currentResponse;
    protected SerialPort port;
    protected List saleDetailResponse;

    private Serial.OnIntermediateMessageReceivedListener onIntermediateMessageReceivedListener;

    @SuppressWarnings({"unused", "UnusedReturnValue"})
    public void setOnIntermediateMessageReceivedListener(OnIntermediateMessageReceivedListener listener) {
        onIntermediateMessageReceivedListener = listener;
    }

    private void setCurrentResponse(String response) {
        currentResponse = response;

        if(checkIntermediateMessage(currentResponse)
                && onIntermediateMessageReceivedListener != null) {
            onIntermediateMessageReceivedListener.onReceived(new IntermediateResponse(currentResponse));
        }
    }

    @SuppressWarnings({"unused", "UnusedReturnValue"})
    public List listPorts() {
        List serialPorts = new ArrayList<>();
        SerialPort[] ports =  SerialPort.getCommPorts();

        for (SerialPort serialPort : ports) {
            serialPorts.add(serialPort.getSystemPortName());
        }

        return serialPorts;
    }

    @SuppressWarnings({"unused", "UnusedReturnValue"})
    public boolean openPort(String portName) {
        log.debug(String.format("Opening port %s", portName));
        return openPort(portName, DEFAULT_BAUDRATE);
    }

    @SuppressWarnings({"unused", "UnusedReturnValue"})
    public boolean openPort(String portName, int baudRate) {
        port = SerialPort.getCommPort(portName);
        port.setBaudRate(baudRate);
        return port.openPort();
    }

    @SuppressWarnings({"unused", "UnusedReturnValue"})
    public boolean closePort() {
        return port.closePort();
    }

    protected void checkCanWrite() throws TransbankException {
        if(port == null || !port.isOpen()) {
            throw new TransbankException("Can't write to port, the port is null or not open");
        }
    }

    protected String createCommand(String payload) {
        String fullCommand = STX+payload+ETX;
        return fullCommand + lrc(fullCommand);
    }

    private char lrc(String command) {
        char lrc = (char)0;

        for (int i = 1; i < command.length(); i++)
        {
            lrc ^= command.charAt(i);
        }
        return lrc;
    }

    protected void write(String payload) throws TransbankException { write(payload, false); }

    protected void write(String payload, boolean intermediateMessages) throws TransbankException { write(payload, intermediateMessages, false, false); }

    protected void write(String payload, boolean intermediateMessages, boolean saleDetail, boolean printOnPOS) throws TransbankException {
        currentResponse = "";

        checkCanWrite();

        String command = createCommand(payload);
        byte[] hexCommand = command.getBytes();

        log.debug(String.format("Request [Hex]: %s", toHexString(hexCommand)));
        log.debug(String.format("Request [ASCII]: %s", command));

        port.writeBytes(hexCommand, hexCommand.length);

        if(!checkAck()) {
            throw new TransbankException("NACK received, check the message sent to the POS");
        }
        log.debug("Read ACK Ok");

        if(intermediateMessages) {
            readResponse();
            while(checkIntermediateMessage(currentResponse)) {
                readResponse();
            }
            return;
        }

        if(saleDetail) {
            saleDetailResponse = new ArrayList<>();
            String authorizationCode = "Start";

            while (!authorizationCode.trim().isEmpty() && !printOnPOS) {
                readResponse();
                try {
                    authorizationCode = getAuthorizationCode(currentResponse);
                    if (!authorizationCode.trim().isEmpty()) {
                        saleDetailResponse.add(currentResponse);
                    }
                } catch (IndexOutOfBoundsException e) {
                    authorizationCode = "";
                }
            }
            return;
        }

        readResponse();
    }

    private void readResponse() throws TransbankException {
        waitResponse();

        int bytesAvailable = port.bytesAvailable();
        byte[] response = new byte[bytesAvailable];
        port.readBytes(response, bytesAvailable);

        if(response[response.length-2] != ETX) {
            waitResponse();
            int fullResponseLength = bytesAvailable;
            bytesAvailable = port.bytesAvailable();
            fullResponseLength += bytesAvailable;
            byte[] fullResponse = Arrays.copyOf(response, fullResponseLength);
            port.readBytes(fullResponse, bytesAvailable, response.length);
            response = fullResponse;
        }

        setCurrentResponse(new String(response, StandardCharsets.UTF_8));

        log.debug(String.format("Response [Hex]: %s", toHexString(response)));
        log.debug(String.format("Response [ASCII]: %s", currentResponse));

        writeAck();
    }

    protected boolean checkAck() throws TransbankException {
        byte[] response = new byte[1];

        waitResponse();

        port.readBytes(response, port.bytesAvailable());
        log.debug(String.format("Checking ACK [Hex]: %02X", response[0]));
        return response[0] == ACK;
    }

    private void waitResponse() throws TransbankException {
        final boolean[] isTimeoutCompleted = {false};
        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {isTimeoutCompleted[0] = true;}
        };

        timer.schedule(timerTask, timeout);

        //noinspection StatementWithEmptyBody
        while (port.bytesAvailable() <= 0 && !isTimeoutCompleted[0]) {
            //waiting for response
        }

        if(isTimeoutCompleted[0]) {
            timer.cancel();
            throw new TransbankException("Read operation Timeout");
        }
        timer.cancel();
    }

    private void writeAck() {
        byte[] ack = {ACK};
        log.debug(String.format("Send ACK [Hex]: %02X", ack[0]));
        port.writeBytes(ack, ack.length);
    }

    protected String toHexString(byte[] data) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < data.length; i++) {
            sb.append(String.format("%02X%s", data[i], (i < data.length - 1) ? "-" : ""));
        }

        return sb.toString();
    }

    private String getFunctionCode(String response) {
        return response.substring(1, response.length()-2).split("\\|")[0];
    }

    private String getAuthorizationCode(String response) {
        return response.substring(1, response.length()-2).split("\\|")[5];
    }

    private boolean checkIntermediateMessage(String response)
    {
        return response.length() >= 1 && getFunctionCode(response).equals("0900");
    }

    public interface OnIntermediateMessageReceivedListener {
        void onReceived(IntermediateResponse intermediateMessage);
    }

    @SuppressWarnings({"unused", "UnusedReturnValue"})
    public boolean isPortOpen() {
        return port.isOpen();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy