0

I am facing a problem while building an integration between ECR (electronic cash register) and an android POS. I'm working on the android POS and i want to calculate the message size and add it in "2 bytes, binary".

I have this block of code to calculate and generate the complete message

fun  generateMessage(message: String): String {
    
            return buildString {
                append(Constants.DIRECTION)
                append(AppData.getProtocolVariant())
                append(AppData.getProtocolVersion())
                append(message)
    
                val buffer = ByteBuffer.allocate(2)
                buffer.putShort(message.length.toShort())
                insert(0, buffer.array().decodeToString())
            }
        }

But this code only works for really small messages, because in the logs i can see that for larger messages this code adds 3 digits and communication fails. The complete message is in the form of <MSG SIZE(2 bytes, binary)><DIRECTION INDICATOR(3 bytes, ascii)><PROTOCOL VARIANT(2 bytes, ascii)><PROTOCOL VERSION(2 bytes, ascii)><Actual messsage (MSG SIZE-7)>

When I send a simple message "E/000" (hex dump 45 2f 30 30 30) the code works and the ECR accepts it and in the logs i see "��POS0110E/000" when i have a bigger message i see something like this "���POS0110R/S852484/RDLF****16/T93/M0/C00/DMastercard:00:0000***1234:200:200:0:0:0:1:803:2:133030119089:193439:497853:20230926115814:0"

which has 3 digits for the message size and it fails.

So how can i create this message and follow the protocol <MSG SIZE(2 bytes, binary)>?

override fun sendMessage(message: String) { 
      val finalMessage = Utils.generateMessage(message) 
      server.sendMessage(finalMessage.encodeToByteArray()) }

`

changed the code to

 fun sendMessage(bytes: ByteArray) { 
   coroutineScope.launch(Dispatchers.IO) { 
    val outputStream = socket?.getOutputStream() 
    outputStream?.write(bytes) 
    outputStream?.flush() 
  } 
}` 

and still is not accepted

example from the guide enter image description here

20
  • What are the length of the messages? Tell us the two and three leading values in hexadecimal notation as then we know the length. You only need three bytes for lengths bigger then 16575 or 34784 so tell. Commented Sep 26, 2023 at 9:37
  • Further you cannot pack -two- byte values in a string variable. You have to sent them as bytes. Not as strings. Commented Sep 26, 2023 at 9:38
  • 1
    Thats wrong as you cannot send bytes with strings. Send/write the bytes directly. Commented Sep 26, 2023 at 9:51
  • 1
    You must write bytes to the OutputStream. Your approach is wrong. Instead of trying to but the binary bytes into a String along with the rest of your data, you need to convert your String data into bytes and create a byte array containing your message. You then write these bytes to the OutputStream. Commented Sep 26, 2023 at 9:54
  • 1
    After the update you do pretty much the same - you try to create a String with a binary header. You can't do that. Strings can't hold binary data. generateMessage() should return ByteArray and you should never try to write the size header to any string. Commented Sep 26, 2023 at 10:34

2 Answers 2

1

You need to write bytes to the OutputStream, not a String. You should change your generateMessage() method to return a ByteArray instead of a String, like this:

fun generateMessage(message: String): ByteArray {
    val x = buildString {
        append(Constants.DIRECTION)
        append(AppData.getProtocolVariant())
        append(AppData.getProtocolVersion())
        append(message)
    }
    // Create byte array containing 2-byte message length (binary) followed
    //  by the actual message
    val buffer = ByteBuffer.allocate(2 + x.length)
    buffer.putShort(x.length.toShort())
    buffer.put(x.getBytes())
    return buffer
}

Also, please note that the message length (according to the example from the guide you pasted) is the length of the entire message (including the header: direction, protocol version, etc.). In your code you only used the length of the actual message (not including the header data).

Sign up to request clarification or add additional context in comments.

1 Comment

cant stress enough of how much i THANK YOU
1

As said in comments, we can't use strings to construct binary messages. Strings are for texts and they can't hold binary data correctly. To process binary data we have to use ByteArray or other similar buffer.

I may not be able to replicate your exact case, but it should be something along lines:

@OptIn(ExperimentalStdlibApi::class)
fun main() {
    val msg = generateMessage("Hello world!")
    println(msg.decodeToString()) // ??POS0110Hello world!
    println(msg.toHexString()) // 0013504f533031313048656c6c6f20776f726c6421
}

fun generateMessage(message: String): ByteArray {
    val baos = ByteArrayOutputStream()
    val out = DataOutputStream(baos)

    val data = message.toByteArray(Charsets.US_ASCII)
    out.writeShort(data.size + 7)
    out.write(Constants.DIRECTION)
    out.write(AppData.getProtocolVariant())
    out.write(AppData.getProtocolVersion())
    out.write(data)

    return baos.toByteArray()
}

object Constants {
    val DIRECTION = "POS".toByteArray(Charsets.US_ASCII)
}

object AppData {
    fun getProtocolVariant() = "01".toByteArray(Charsets.US_ASCII)
    fun getProtocolVersion() = "10".toByteArray(Charsets.US_ASCII)
}

Size header is 0013 in hex, so 19. This is correct, as POS0110Hello world! is 19 bytes.

Please note we use big endian here. That matches the format provided in the example image.

If we like hardcode Kotlin programming, we could alternatively use this:

fun generateMessage(message: String) = ByteArrayOutputStream().also {
    with (DataOutputStream(it)) {
        val data = message.toByteArray(Charsets.US_ASCII)
        writeShort(data.size + 7)
        write(Constants.DIRECTION)
        write(AppData.getProtocolVariant())
        write(AppData.getProtocolVersion())
        write(data)
    }
}.toByteArray()

1 Comment

thanks a lot guys, you teach me something today

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.