diff --git a/src/jvmTest/kotlin/PacketReaderTests.kt b/src/jvmTest/kotlin/PacketReaderTests.kt
new file mode 100644
index 0000000..7a06a4d
--- /dev/null
+++ b/src/jvmTest/kotlin/PacketReaderTests.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2019. Open JumpCO
+ *
+ * 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 .
+ */
+
+package io.jumpco.open.kfsm
+
+import java.io.ByteArrayOutputStream
+
+class Block {
+ val byteArrayOutputStream = ByteArrayOutputStream(32)
+ fun addByte(byte: Int) {
+ byteArrayOutputStream.write(byte)
+ }
+}
+
+class Packet {
+ val fields = mutableListOf()
+ var currentField: Block? = null
+ var checksumValid: Boolean = false
+ private set
+ public get
+ val checkSum = Block()
+ fun addField() {
+ currentField = Block()
+ }
+
+ fun endField() {
+ val field = currentField
+ require(field != null) { "expected currentField to have a value" }
+ fields.add(field.byteArrayOutputStream.toByteArray())
+ currentField = null
+ }
+
+ fun addByte(byte: Int) {
+ val field = currentField
+ require(field != null) { "expected currentField to have a value" }
+ field.addByte(byte)
+ }
+
+ fun addChecksum(byte: Int) {
+ checkSum.addByte(byte)
+ }
+
+ fun checksum() {
+ require(checkSum.byteArrayOutputStream.size() > 0)
+ val checksumBytes = checkSum.byteArrayOutputStream.toByteArray()
+ // TODO calc checksum using all fields and verify with checksumBytes and update checksumValid
+ }
+
+ fun sendNACK() {
+ println("NACK")
+ }
+
+ fun sendACK() {
+ println("ACK")
+ }
+}
+
+enum class ReaderEvents(val code: Int) {
+ BYTE(0x00),
+ SOH(0x01),
+ STX(0x02),
+ ETX(0x03),
+ EOT(0x04),
+ ESC(0x1b)
+}
+
+enum class ReaderStates {
+ START,
+ RCVPCKT,
+ RCVDATA,
+ RCVESC,
+ RCVCHK,
+ RCVCHKESC,
+ CHKSUM,
+ END
+}
+
+class PacketReaderFSM(private val packet: Packet) {
+ companion object {
+ private val definition = stateMachine(ReaderStates.values().toSet(), ReaderEvents::class, Packet::class) {
+ default {
+ transition(ReaderEvents.BYTE to ReaderStates.END) {
+ sendNACK()
+ }
+ transition(ReaderEvents.SOH to ReaderStates.END) {
+ sendNACK()
+ }
+ transition(ReaderEvents.STX to ReaderStates.END) {
+ sendNACK()
+ }
+ transition(ReaderEvents.ETX to ReaderStates.END) {
+ sendNACK()
+ }
+ transition(ReaderEvents.EOT to ReaderStates.END) {
+ sendNACK()
+ }
+ transition(ReaderEvents.ESC to ReaderStates.END) {
+ sendNACK()
+ }
+ }
+ state(ReaderStates.START) {
+ transition(ReaderEvents.SOH to ReaderStates.RCVPCKT) {}
+ }
+ state(ReaderStates.RCVPCKT) {
+ transition(ReaderEvents.STX to ReaderStates.RCVDATA) {
+ addField()
+ }
+ transition(ReaderEvents.BYTE to ReaderStates.RCVCHK) { args ->
+ require(args.size == 1)
+ args as Array
+ addChecksum(args[0])
+ }
+ }
+ state(ReaderStates.RCVDATA) {
+ transition(ReaderEvents.BYTE) { args ->
+ require(args.size == 1)
+ args as Array
+ addByte(args[0])
+ }
+ transition(ReaderEvents.ETX to ReaderStates.RCVPCKT) {
+ endField()
+ }
+ transition(ReaderEvents.ESC to ReaderStates.RCVESC) {}
+ }
+ state(ReaderStates.RCVESC) {
+ transition(ReaderEvents.ESC to ReaderStates.RCVDATA) {
+ addByte(ReaderEvents.ESC.code)
+ }
+ transition(ReaderEvents.STX to ReaderStates.RCVDATA) {
+ addByte(ReaderEvents.STX.code)
+ }
+ transition(ReaderEvents.SOH to ReaderStates.RCVDATA) {
+ addByte(ReaderEvents.SOH.code)
+ }
+ transition(ReaderEvents.ETX to ReaderStates.RCVDATA) {
+ addByte(ReaderEvents.ETX.code)
+ }
+ transition(ReaderEvents.EOT to ReaderStates.RCVDATA) {
+ addByte(ReaderEvents.EOT.code)
+ }
+ }
+ state(ReaderStates.RCVCHK) {
+ transition(ReaderEvents.BYTE) { args ->
+ require(args.size == 1)
+ args as Array
+ addChecksum(args[0])
+ }
+ transition(ReaderEvents.ESC to ReaderStates.RCVCHKESC) {}
+ transition(ReaderEvents.EOT to ReaderStates.CHKSUM) {
+ checksum()
+ }
+ }
+ state(ReaderStates.CHKSUM) {
+ automatic(ReaderStates.END, guard = { !checksumValid }) {
+ sendNACK()
+ }
+ automatic(ReaderStates.END, guard = { checksumValid }) {
+ sendACK()
+ }
+ }
+ state(ReaderStates.RCVCHKESC) {
+ transition(ReaderEvents.ESC to ReaderStates.RCVCHK) {
+ addChecksum(ReaderEvents.ESC.code)
+ }
+ transition(ReaderEvents.SOH to ReaderStates.RCVCHK) {
+ addChecksum(ReaderEvents.SOH.code)
+ }
+ transition(ReaderEvents.EOT to ReaderStates.RCVCHK) {
+ addChecksum(ReaderEvents.EOT.code)
+ }
+ transition(ReaderEvents.STX to ReaderStates.RCVCHK) {
+ addChecksum(ReaderEvents.STX.code)
+ }
+ transition(ReaderEvents.ETX to ReaderStates.RCVCHK) {
+ addChecksum(ReaderEvents.ETX.code)
+ }
+ }
+ }.build()
+ }
+
+ private val fsm = definition.create(packet)
+ fun receiveByte(byte: Int) {
+ when (byte) {
+ ReaderEvents.SOH.code -> fsm.sendEvent(ReaderEvents.SOH)
+ ReaderEvents.STX.code -> fsm.sendEvent(ReaderEvents.STX)
+ ReaderEvents.ETX.code -> fsm.sendEvent(ReaderEvents.ETX)
+ ReaderEvents.EOT.code -> fsm.sendEvent(ReaderEvents.EOT)
+ ReaderEvents.ESC.code -> fsm.sendEvent(ReaderEvents.ESC)
+ ReaderEvents.BYTE.code -> fsm.sendEvent(ReaderEvents.BYTE, byte)
+ else -> fsm.sendEvent(ReaderEvents.BYTE, byte)
+ }
+ }
+}