Usage
Installation
Install with the Loupe Package Manager:
lpm install databuffer
DataBuffer depends only on sys_lib, which ships with Automation Studio.
Declaration
Declare one datbufBuffer_typ variable per buffer, as a task or global variable. The examples on this page use the following variables; other application variables are left undeclared.
C
datbufBuffer_typ myBuf;
UINT status;
ST
VAR
myBuf : datbufBuffer_typ;
status : UINT;
END_VAR
Initialization
A buffer must be initialized with datbufInitBuffer before any other function is called on it. Initialization allocates maxLength bytes of memory for the buffer’s data and clears it to zero.
Call datbufInitBuffer once, in the _INIT routine. It allocates the memory with TMP_alloc, which B&R allows only in the _INIT and _EXIT routines because the operating system locks it takes can cause cycle time violations. Do not call it from a cyclic routine. To empty a buffer for reuse, call datbufClearBuffer instead.
The library has no function to release the memory. The block stays allocated until the controller restarts, so call datbufInitBuffer only once per buffer.
C
void _INIT ProgramInit(void)
{
status = datbufInitBuffer((UDINT)&myBuf, 1000);
}
ST
PROGRAM _INIT
status := datbufInitBuffer(ADR(myBuf), 1000);
END_PROGRAM
A non-zero status means the buffer is not usable. See Types and Enums for the error codes.
Appending Data
datbufAppendToBuffer copies dataLength bytes from pData to the end of the buffer and advances myBuf.currentLength. After appending, myBuf.pData points to the combined data and myBuf.currentLength is its length in bytes.
To rebuild the contents from scratch, call datbufClearBuffer first. Clearing resets currentLength to 0 and zeroes the whole buffer.
The following example builds one string out of an array of messages whenever a new message arrives.
C
if (newMessage) {
newMessage = 0;
status = datbufClearBuffer((UDINT)&myBuf);
for (i = 0; i < numMessages; i++) {
status = datbufAppendToBuffer((UDINT)&myBuf, (UDINT)&message[i], strlen(message[i]));
if (status != 0) {
// Handle error, e.g. DATBUF_ERR_BUFFERFULL
break;
}
}
}
ST
IF newMessage THEN
newMessage := FALSE;
status := datbufClearBuffer(ADR(myBuf));
FOR i := 0 TO numMessages - 1 DO
status := datbufAppendToBuffer(ADR(myBuf), ADR(message[i]), brsstrlen(ADR(message[i])));
IF status <> 0 THEN
// Handle error, e.g. DATBUF_ERR_BUFFERFULL
EXIT;
END_IF
END_FOR
END_IF
Appended data is not limited to strings. Any variable can be appended by passing its address and size, for example datbufAppendToBuffer(ADR(myBuf), ADR(myStruct), SIZEOF(myStruct)).
Working with Strings
The buffer does not add a null terminator after appended data. A string built in the buffer is still null-terminated as long as the buffer is not full, because the buffer is zeroed on initialization and on every clear, so the byte after the data is 0. When the buffer is completely full, currentLength equals maxLength and there is no byte left for the terminator.
To guarantee a terminated string, either:
- Allocate one byte more than the longest string the buffer should hold, and treat
currentLength = maxLengthas an overflow, or - Use
myBuf.currentLengthas the string length instead of relying on a terminator, for example withbrsmemcpyor a length-aware send function.
Handling a Full Buffer
When the data to append does not fit, datbufAppendToBuffer copies as many bytes as fit, sets currentLength to maxLength, and returns DATBUF_ERR_BUFFERFULL. The data in the buffer is therefore truncated, not left unchanged. Further appends of any non-zero length copy nothing and return DATBUF_ERR_BUFFERFULL until the buffer is cleared.
An append that fills the buffer exactly returns 0. A 0 status therefore does not mean there is space left; compare currentLength with maxLength if that matters.
If a truncated result is not acceptable, check status after each append and discard or clear the buffer on DATBUF_ERR_BUFFERFULL. To size the buffer, maxLength should be at least the largest total amount of data that will be appended between clears.