/* * Copyright 2014 Bevbot LLC <info@bevbot.com> * * This file is part of the Kegtab package from the Kegbot project. For * more information on Kegtab or Kegbot, see <http://kegbot.org/>. * * Kegtab 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, version 2. * * Kegtab 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 Kegtab. If not, see <http://www.gnu.org/licenses/>. */ package org.kegbot.kegboard; import com.google.common.base.Preconditions; import com.google.common.primitives.Shorts; import java.util.Arrays; import javax.annotation.concurrent.GuardedBy; public class KegboardMessageFactory { private static final boolean DEBUG = true; private static final byte[] KBSP_PREFIX = "KBSP v1:".getBytes(); private static final byte[] KBSP_TRAILER = "\r\n".getBytes(); private static final int KBSP_HEADER_LENGTH = 12; private static final int KBSP_CRC_LENGTH = 2; private static final int KBSP_TRAILER_LENGTH = KBSP_CRC_LENGTH + KBSP_TRAILER.length; private static final int KBSP_MIN_PACKET_SIZE = KBSP_HEADER_LENGTH + KBSP_TRAILER_LENGTH; @GuardedBy("this") private final byte[] mBuffer = new byte[2048]; private int mAppendPosition = 0; public synchronized void addBytes(byte[] newBytes, int length) { for (int i = 0; i < length; i++) { mBuffer[mAppendPosition++] = newBytes[i]; } } public synchronized KegboardMessage getMessage() { // Consume from mBuffer until done while (mAppendPosition > 0) { final int available = mAppendPosition; if (available < KBSP_MIN_PACKET_SIZE) { return null; } boolean framingError = false; for (int i = 0; i < KBSP_PREFIX.length; i++) { if (mBuffer[i] != KBSP_PREFIX[i]) { compact(i + 1); framingError = true; break; } } if (framingError) { //debug("Framing error: " + HexDump.dumpHexString(mBuffer, 0, 12)); continue; } int payloadLength = Shorts.fromBytes(mBuffer[11], mBuffer[10]); if (payloadLength > 240) { compact(KBSP_HEADER_LENGTH); framingError("Illegal payload length"); return null; } int totalLength = KBSP_HEADER_LENGTH + payloadLength + KBSP_TRAILER_LENGTH; if (available < totalLength) { return null; } final KegboardMessage message; try { message = KegboardMessage.fromBytes(Arrays.copyOf(mBuffer, totalLength)); return message; } catch (KegboardMessageException e) { debug("Error building message: " + e); /* Don't return, keep trying. */ } finally { compact(totalLength); } } return null; } private void compact(int length) { mAppendPosition -= length; Preconditions.checkState(mAppendPosition >= 0); System.arraycopy(mBuffer, length, mBuffer, 0, mBuffer.length - length); } private void debug(String message) { if (DEBUG) { System.out.println("[mAppendPosition=" + mAppendPosition + "]: " + message); } } private void framingError(String reason) { debug("Framing error: " + reason); } }