Jan Breuer
2012-11-16 f5850892befc908f7deeae7b7d39f44e4f5ae293
Initial implementation of SCPI parser library
2个文件已修改
13个文件已添加
2102 ■■■■■ 已修改文件
.gitignore 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LICENSE 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi.c 633 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi.h 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_constants.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_error.c 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_error.h 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_ieee488.c 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_ieee488.h 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_minimal.c 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_minimal.h 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_utils.c 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scpi/scpi_utils.h 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-parser.c 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -15,3 +15,6 @@
*.exe
*.out
*.app
# Backup files
*~
LICENSE
New file
@@ -0,0 +1,24 @@
Copyright (c) 2012-2013 Jan Breuer,
All Rights Reserved
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md
@@ -1,4 +1,73 @@
scpi-parser
SCPI parser library
===========
SCPI parser library
[SCPI](http://en.wikipedia.org/wiki/Standard_Commands_for_Programmable_Instruments) Parser library aims to provide parsing ability of SCPI commands on instrument side. All commands are defined by its patterns eg: "STATus:QUEStionable:EVENt?".
Source codes are devided into few files to provide better portability to other systems.
- *scpi.c* - provides the core parser library
- *scpi_error.c* - provides basic error handling (error queue of the instrument)
- *scpi_ieee488.c* - provides basic implementation of IEEE488.2 mandatory commands
- *scpi_minimal.c* - provides basic implementation of SCPI mandatory commands
- *scpi_utils.c* - provides string handling routines and conversion routines
- *test-parser.c* - is the basic non-interactive demo of the parser
Implementation to your instrument
-------------
First of all you need to fill structure of SCPI command definitions
    scpi_command_t scpi_commands[] = {
        { .pattern = "*IDN?", .callback = SCPI_CoreIdnQ,},
        { .pattern = "*RST", .callback = SCPI_CoreRst,},
        SCPI_CMD_LIST_END
    };
Than you need to initialize interface callbacks structure. If you don't want to provide some callbacks, just initialize it as NULL. write callback is mandatory and is used to output data from the library.
    scpi_interface_t scpi_interface = {
        .write = myWrite,
        .error = NULL,
        .reset = NULL,
        .test = NULL,
    };
Important thing is command buffer. Maximum size is up to you and it should be larger than any possible largest command.
    #define SCPI_BUFFER_LENGTH 256
    char myBuffer[SCPI_BUFFER_LENGTH];
    scpi_buffer_t scpi_buffer = {
        .length = SCPI_BUFFER_LENGTH,
        .data = myBuffer,
    };
The last structure is scpi context used in parser library.
    scpi_context_t scpi_context;
All these structures should be global variables of the c file or allocated by function like malloc. It is common mistake to create these structures inside a function as local variables of this function. This will not work. If you don't know why, you should read something about [function stack.](http://stackoverflow.com/questions/4824342/returning-a-local-variable-from-function-in-c).
Now we are ready to connect everything together
    SCPI_Init(&scpi_context, scpi_commands, &scpi_buffer, &scpi_interface);
Test implementation of function myWrite, which outputs everything to stdout, can be
    size_t myWrite(scpi_context_t * context, const char * data, size_t len) {
        (void) context;
        return fwrite(data, 1, len, stdout);
    }
Interactive demo can beimplemented using this loop
    #define SMALL_BUFFER_LEN
    char smbuffer[SMALL_BUFFER_LEN];
    while(1) {
        fgets(smbuffer, SMALL_BUFFER_LEN, stdin);
        SCPI_Input(&scpi_context, smbuffer, strlen(smbuffer));
    }
scpi/scpi.c
New file
@@ -0,0 +1,633 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi.c
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  SCPI parser implementation
 *
 *
 */
#include "scpi.h"
#include "scpi_utils.h"
#include "scpi_error.h"
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
static size_t patternSeparatorPos(const char * pattern, size_t len);
static size_t patternSeparatorShortPos(const char * pattern, size_t len);
static size_t cmdSeparatorPos(const char * cmd, size_t len);
static size_t cmdTerminatorPos(const char * cmd, size_t len);
static size_t cmdlineSeparatorPos(const char * cmd, size_t len);
static char * cmdlineSeparator(const char * cmd, size_t len);
static char * cmdlineTerminator(const char * cmd, size_t len);
static const char * cmdlineNext(const char * cmd, size_t len);
static bool_t cmdMatch(const char * pattern, const char * cmd, size_t len);
static size_t skipWhitespace(const char * cmd, size_t len);
static void paramSkipBytes(scpi_context_t * context, size_t num);
static void paramSkipWhitespace(scpi_context_t * context);
static bool_t paramNext(scpi_context_t * context, bool_t mandatory);
#define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })
#define min(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a < _b ? _a : _b; })
/*
int _strnicmp(const char* s1, const char* s2, size_t len) {
    int result = 0;
    int i;
    for (i = 0; i < len && s1[i] && s2[i]; i++) {
        char c1 = tolower(s1[i]);
        char c2 = tolower(s2[i]);
        if (c1 != c2) {
            result = (int) c1 - (int) c2;
            break;
        }
    }
    return result;
}
 */
/**
 * Find pattern separator position
 * @param pattern
 * @param len - max search length
 * @return position of separator or len
 */
size_t patternSeparatorPos(const char * pattern, size_t len) {
    char * separator = strnpbrk(pattern, len, "?:[]");
    if (separator == NULL) {
        return len;
    } else {
        return separator - pattern;
    }
}
/**
 * Pattern is composed from upper case an lower case letters. This function
 * search the first lowercase letter
 * @param pattern
 * @param len - max search length
 * @return position of separator or len
 */
size_t patternSeparatorShortPos(const char * pattern, size_t len) {
    size_t i;
    for (i = 0; (i < len) && pattern[i]; i++) {
        if (islower(pattern[i])) {
            return i;
        }
    }
    return i;
}
/**
 * Find command separator position
 * @param cmd - input command
 * @param len - max search length
 * @return position of separator or len
 */
size_t cmdSeparatorPos(const char * cmd, size_t len) {
    char * separator = strnpbrk(cmd, len, ":?");
    size_t result;
    if (separator == NULL) {
        result = len;
    } else {
        result = separator - cmd;
    }
    return result;
}
/**
 * Find command termination character
 * @param cmd - input command
 * @param len - max search length
 * @return position of terminator or len
 */
size_t cmdTerminatorPos(const char * cmd, size_t len) {
    char * terminator = strnpbrk(cmd, len, "; \r\n\t");
    if (terminator == NULL) {
        return len;
    } else {
        return terminator - cmd;
    }
}
/**
 * Find command line separator
 * @param cmd - input command
 * @param len - max search length
 * @return pointer to line separator or NULL
 */
char * cmdlineSeparator(const char * cmd, size_t len) {
    return strnpbrk(cmd, len, ";\r\n");
}
/**
 * Find command line terminator
 * @param cmd - input command
 * @param len - max search length
 * @return pointer to command line terminator or NULL
 */
char * cmdlineTerminator(const char * cmd, size_t len) {
    return strnpbrk(cmd, len, "\r\n");
}
/**
 * Find command line separator position
 * @param cmd - input command
 * @param len - max search length
 * @return position of line separator or len
 */
size_t cmdlineSeparatorPos(const char * cmd, size_t len) {
    char * separator = cmdlineSeparator(cmd, len);
    if (separator == NULL) {
        return len;
    } else {
        return separator - cmd;
    }
}
/**
 * Find next part of command
 * @param cmd - input command
 * @param len - max search length
 * @return Pointer to next part of command
 */
const char * cmdlineNext(const char * cmd, size_t len) {
    const char * separator = cmdlineSeparator(cmd, len);
    if (separator == NULL) {
        return cmd + len;
    } else {
        return separator + 1;
    }
}
/**
 * Compare pattern and command
 * @param pattern
 * @param cmd - command
 * @param len - max search length
 * @return TRUE if pattern matches, FALSE otherwise
 */
bool_t cmdMatch(const char * pattern, const char * cmd, size_t len) {
    int result = FALSE;
    const char * pattern_ptr = pattern;
    int pattern_len = strlen(pattern);
    const char * pattern_end = pattern + pattern_len;
    const char * cmd_ptr = cmd;
    size_t cmd_len = strnlen(cmd, len);
    const char * cmd_end = cmd + cmd_len;
    while (1) {
        int pattern_sep_pos = patternSeparatorPos(pattern_ptr, pattern_end - pattern_ptr);
        int cmd_sep_pos = cmdSeparatorPos(cmd_ptr, cmd_end - cmd_ptr);
        int pattern_sep_pos_short = patternSeparatorShortPos(pattern_ptr, pattern_sep_pos);
        if (compareStr(pattern_ptr, pattern_sep_pos, cmd_ptr, cmd_sep_pos) ||
                compareStr(pattern_ptr, pattern_sep_pos_short, cmd_ptr, cmd_sep_pos)) {
            pattern_ptr = pattern_ptr + pattern_sep_pos;
            cmd_ptr = cmd_ptr + cmd_sep_pos;
            result = TRUE;
            // command is complete
            if ((pattern_ptr == pattern_end) && (cmd_ptr >= cmd_end)) {
                break;
            }
            // pattern complete, but command not
            if ((pattern_ptr == pattern_end) && (cmd_ptr < cmd_end)) {
                result = FALSE;
                break;
            }
            // command complete, but pattern not
            if (cmd_ptr >= cmd_end) {
                result = FALSE;
                break;
            }
            // both command and patter contains command separator at this position
            if ((pattern_ptr[0] == cmd_ptr[0]) && ((pattern_ptr[0] == ':') || (pattern_ptr[0] == '?'))) {
                pattern_ptr = pattern_ptr + 1;
                cmd_ptr = cmd_ptr + 1;
            } else {
                result = FALSE;
                break;
            }
        } else {
            result = FALSE;
            break;
        }
    }
    return result;
}
/**
 * Count white spaces from the beggining
 * @param cmd - command
 * @param len - max search length
 * @return number of white spaces
 */
size_t skipWhitespace(const char * cmd, size_t len) {
    size_t i;
    for (i = 0; i < len; i++) {
        if (!isspace(cmd[i])) {
            return i;
        }
    }
    return len;
}
/**
 * Write data to SCPI output
 * @param context
 * @param data
 * @param len - lenght of data to be written
 * @return number of bytes written
 */
static inline size_t writeData(scpi_context_t * context, const char * data, size_t len) {
    return context->interface->write(context, data, len);
}
/**
 * Write result delimiter to output
 * @param context
 * @return number of bytes written
 */
static inline size_t writeDelimiter(scpi_context_t * context) {
    if (context->output_count > 0) {
        return writeData(context, ", ", 2);
    } else {
        return 0;
    }
}
/**
 * Zapis nove radky na SCPI vystup
 * @param context
 * @return pocet zapsanych znaku
 */
static inline size_t writeNewLine(scpi_context_t * context) {
    if (context->output_count > 0) {
        return writeData(context, "\r\n", 2);
    } else {
        return 0;
    }
}
/**
 * Parse one command line
 * @param context
 * @param data - complete command line
 * @param len - command line length
 * @return 1 if the last evaluated command was found
 */
int SCPI_Parse(scpi_context_t * context, const char * data, size_t len) {
    int32_t i;
    int result = 0;
    const char * cmdline_end = data + len;
    const char * cmdline_ptr = data;
    size_t cmd_len;
    if (context == NULL) {
        return -1;
    }
    while (cmdline_ptr < cmdline_end) {
        result = 0;
        cmd_len = cmdTerminatorPos(cmdline_ptr, cmdline_end - cmdline_ptr);
        if (cmd_len > 0) {
            for (i = 0; context->cmdlist[i].pattern != NULL; i++) {
                if (cmdMatch(context->cmdlist[i].pattern, cmdline_ptr, cmd_len)) {
                    if (context->cmdlist[i].callback != NULL) {
                        context->paramlist.cmd = &context->cmdlist[i];
                        context->paramlist.parameters = cmdline_ptr + cmd_len;
                        context->paramlist.length = cmdlineSeparatorPos(context->paramlist.parameters, cmdline_end - context->paramlist.parameters);
                        context->output_count = 0;
                        context->input_count = 0;
                        SCPI_DEBUG_COMMAND(context);
                        context->cmdlist[i].callback(context);
                        writeNewLine(context); // conditionaly write new line
                        paramSkipWhitespace(context);
                        if (context->paramlist.length != 0) {
                            SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED);
                        }
                        result = 1;
                        break;
                    }
                }
            }
            if (result == 0) {
                SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER);
            }
        }
        cmdline_ptr = cmdlineNext(cmdline_ptr, cmdline_end - cmdline_ptr);
    }
    return result;
}
/**
 * Initialize SCPI context structure
 * @param context
 * @param command_list
 * @param buffer
 * @param interface
 */
void SCPI_Init(scpi_context_t * context, scpi_command_t * command_list, scpi_buffer_t * buffer, scpi_interface_t * interface) {
    context->cmdlist = command_list;
    context->buffer.data = buffer->data;
    context->buffer.length = buffer->length;
    context->buffer.position = 0;
    context->interface = interface;
}
/**
 * Interface to the application. Adds data to system buffer and try to search
 * command line termination. If the termination is found or if len=0, command
 * parser is called.
 *
 * @param context
 * @param data - data to process
 * @param len - length of data
 * @return
 */
int SCPI_Input(scpi_context_t * context, const char * data, size_t len) {
    int result = 0;
    size_t buffer_free;
    char * cmd_term;
    int ws;
    if (len == 0) {
        context->buffer.data[context->buffer.position] = 0;
        result = SCPI_Parse(context, context->buffer.data, context->buffer.position);
        context->buffer.position = 0;
    } else {
        buffer_free = context->buffer.length - context->buffer.position;
        if (len > (buffer_free - 1)) {
            return -1;
        }
        memcpy(&context->buffer.data[context->buffer.position], data, len);
        context->buffer.position += len;
        context->buffer.data[context->buffer.position] = 0;
        ws = skipWhitespace(context->buffer.data, context->buffer.position);
        cmd_term = cmdlineTerminator(context->buffer.data + ws, context->buffer.position - ws);
        if (cmd_term != NULL) {
            int curr_len = cmd_term - context->buffer.data;
            result = SCPI_Parse(context, context->buffer.data + ws, curr_len - ws);
            memmove(context->buffer.data, cmd_term, context->buffer.position - curr_len);
            context->buffer.position -= curr_len;
        }
    }
    return result;
}
/**
 * Debug function: show current command and its parameters
 * @param context
 * @return
 */
bool_t SCPI_DebugCommand(scpi_context_t * context) {
    (void) context;
    printf("**DEBUG: %s (\"", context->paramlist.cmd->pattern);
    fwrite(context->paramlist.parameters, 1, context->paramlist.length, stdout);
    printf("\" - %ld)\r\n", context->paramlist.length);
    return TRUE;
}
/* writing results */
/**
 * Write raw string result to the output
 * @param context
 * @param data
 * @return
 */
size_t SCPI_ResultString(scpi_context_t * context, const char * data) {
    size_t len = strlen(data);
    size_t result = 0;
    result += writeDelimiter(context);
    result += writeData(context, data, len);
    context->output_count++;
    return result;
}
/**
 * Write integer value to the result
 * @param context
 * @param val
 * @return
 */
size_t SCPI_ResultInt(scpi_context_t * context, int32_t val) {
    char buffer[12];
    size_t result = 0;
    size_t len = longToStr(val, buffer, sizeof (buffer));
    result += writeDelimiter(context);
    result += writeData(context, buffer, len);
    context->output_count++;
    return result;
}
/**
 * Write double walue to the result
 * @param context
 * @param val
 * @return
 */
size_t SCPI_ResultDouble(scpi_context_t * context, double val) {
    char buffer[32];
    size_t result = 0;
    size_t len = doubleToStr(val, buffer, sizeof (buffer));
    result += writeDelimiter(context);
    result += writeData(context, buffer, len);
    context->output_count++;
    return result;
}
/**
 * Write string withn " to the result
 * @param context
 * @param data
 * @return
 */
size_t SCPI_ResultText(scpi_context_t * context, const char * data) {
    size_t result = 0;
    result += writeDelimiter(context);
    result += writeData(context, "\"", 1);
    result += writeData(context, data, strlen(data));
    result += writeData(context, "\"", 1);
    context->output_count++;
    return result;
}
/* parsing parameters */
/**
 * Skip num bytes from the begginig of parameters
 * @param context
 * @param num
 */
void paramSkipBytes(scpi_context_t * context, size_t num) {
    if (context->paramlist.length < num) {
        num = context->paramlist.length;
    }
    context->paramlist.parameters += num;
    context->paramlist.length -= num;
}
/**
 * Skip white spaces from the beggining of parameters
 * @param context
 */
void paramSkipWhitespace(scpi_context_t * context) {
    size_t ws = skipWhitespace(context->paramlist.parameters, context->paramlist.length);
    paramSkipBytes(context, ws);
}
/**
 * Find next parameter
 * @param context
 * @param mandatory
 * @return
 */
bool_t paramNext(scpi_context_t * context, bool_t mandatory) {
    paramSkipWhitespace(context);
    if (context->paramlist.length == 0) {
        if (mandatory) {
            SCPI_ErrorPush(context, SCPI_ERROR_MISSING_PARAMETER);
        }
        return FALSE;
    }
    if (context->input_count != 0) {
        if (context->paramlist.parameters[0] == ',') {
            paramSkipBytes(context, 1);
            paramSkipWhitespace(context);
        } else {
            SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SEPARATOR);
            return FALSE;
        }
    }
    context->input_count++;
    return TRUE;
}
/**
 * Parse integer parameter
 * @param context
 * @param value
 * @param mandatory
 * @return
 */
bool_t SCPI_ParamInt(scpi_context_t * context, int32_t * value, bool_t mandatory) {
    size_t len;
    if (!paramNext(context, mandatory)) {
        return FALSE;
    }
    len = strToLong(context->paramlist.parameters, value);
    if (len == 0) {
        if (mandatory) {
            SCPI_ErrorPush(context, SCPI_ERROR_SYNTAX);
        }
        return FALSE;
    } else {
        paramSkipBytes(context, len);
    }
    return TRUE;
}
/**
 * Parse double parameter
 * @param context
 * @param value
 * @param mandatory
 * @return
 */
bool_t SCPI_ParamDouble(scpi_context_t * context, double * value, bool_t mandatory) {
    size_t len;
    if (!paramNext(context, mandatory)) {
        return FALSE;
    }
    len = strToDouble(context->paramlist.parameters, value);
    if (len == 0) {
        if (mandatory) {
            SCPI_ErrorPush(context, SCPI_ERROR_SYNTAX);
        }
        return FALSE;
    } else {
        paramSkipBytes(context, len);
    }
    return TRUE;
}
/**
 * Parse string parameter
 * @param context
 * @param value
 * @param len
 * @param mandatory
 * @return
 */
bool_t SCPI_ParamString(scpi_context_t * context, char ** value, size_t * len, bool_t mandatory) {
    (void)context;
    (void)value;
    (void)len;
    (void)mandatory;
    return FALSE;
}
scpi/scpi.h
New file
@@ -0,0 +1,126 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi.h
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  SCPI parser implementation
 *
 *
 */
#ifndef SCPI_H
#define    SCPI_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef    __cplusplus
extern "C" {
#endif
#define FALSE false
#define TRUE true
    typedef bool bool_t;
    //typedef enum { FALSE = 0, TRUE } bool_t;
    typedef struct _scpi_context_t scpi_context_t;
    typedef struct _scpi_param_list_t scpi_param_list_t;
    typedef struct _scpi_command_t scpi_command_t;
    typedef struct _scpi_buffer_t scpi_buffer_t;
    typedef struct _scpi_interface_t scpi_interface_t;
    typedef int (*scpi_command_callback_t)(scpi_context_t *);
    typedef size_t(*scpi_write_t)(scpi_context_t * context, const char * data, size_t len);
    typedef int (*scpi_error_callback_t)(scpi_context_t * context, int_fast16_t error);
    struct _scpi_param_list_t {
        const scpi_command_t * cmd;
        const char * parameters;
        size_t length;
    };
    struct _scpi_command_t {
        const char * pattern;
        scpi_command_callback_t callback;
    };
    struct _scpi_buffer_t {
        size_t length;
        size_t position;
        char * data;
    };
    struct _scpi_interface_t {
        scpi_error_callback_t error;
        scpi_write_t write;
        scpi_command_callback_t reset;
        scpi_command_callback_t test;
    };
    struct _scpi_context_t {
        const scpi_command_t * cmdlist;
        scpi_buffer_t buffer;
        scpi_param_list_t paramlist;
        scpi_interface_t * interface;
        int_fast16_t output_count;
        int_fast16_t input_count;
    };
#define SCPI_CMD_LIST_END       {.pattern = NULL, .callback = NULL, }
    void SCPI_Init(scpi_context_t * context, scpi_command_t * command_list, scpi_buffer_t * buffer, scpi_interface_t * interface);
    int SCPI_Input(scpi_context_t * context, const char * data, size_t len);
    int SCPI_Parse(scpi_context_t * context, const char * data, size_t len);
    size_t SCPI_ResultString(scpi_context_t * context, const char * data);
    size_t SCPI_ResultInt(scpi_context_t * context, int32_t val);
    size_t SCPI_ResultDouble(scpi_context_t * context, double val);
    size_t SCPI_ResultText(scpi_context_t * context, const char * data);
    bool_t SCPI_ParamInt(scpi_context_t * context, int32_t * value, bool_t mandatory);
    bool_t SCPI_ParamDouble(scpi_context_t * context, double * value, bool_t mandatory);
    bool_t SCPI_ParamString(scpi_context_t * context, char ** value, size_t * len, bool_t mandatory);
    bool_t SCPI_DebugCommand(scpi_context_t * context);
    //#define SCPI_DEBUG_COMMAND(a)   scpi_debug_command(a)
#define SCPI_DEBUG_COMMAND(a)
#ifdef    __cplusplus
}
#endif
#endif    /* SCPI_H */
scpi/scpi_constants.h
New file
@@ -0,0 +1,54 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_constants.h
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  SCPI Device constants
 *
 *
 */
#ifndef SCPI_CONSTANTS_H
#define    SCPI_CONSTANTS_H
#ifdef    __cplusplus
extern "C" {
#endif
#define SCPI_MANUFACTURE "CTU FEE"
#define SCPI_DEV_NAME "TEST SCPI INSTRUMENT TSI3225"
#define SCPI_DEV_VERSION "v1.0"
#ifdef    __cplusplus
}
#endif
#endif    /* SCPI_CONSTANTS_H */
scpi/scpi_error.c
New file
@@ -0,0 +1,108 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_error.c
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  Error handling and storing routines
 *
 *
 */
#include "scpi.h"
#include "scpi_ieee488.h"
#include "scpi_error.h"
#include <stdint.h>
static int16_t scpi_err = 0;
/**
 * Clear error queue
 * @param context - scpi context
 */
void SCPI_ErrorClear(scpi_context_t * context) {
    (void)context;
    scpi_err = 0;
}
/**
 * Push error to queue
 * @param context - scpi context
 * @param err - error number
 */
void SCPI_ErrorPush(scpi_context_t * context, int16_t err) {
    scpi_err = err;
     // Command error (e.g. syntax error)
    if ((err < -101) && (err > -158)) {
        SCPI_RegSetBits(SCPI_REG_ESR, ESR_CER);
    }
    // Execution Error (e.g. range error)
    if ((err < -211) && (err > -230)) {
        SCPI_RegSetBits(SCPI_REG_ESR, ESR_EER);
    }
    // Device Dependent Error
    if ((err < -501) && (err > -748)) {
        SCPI_RegSetBits(SCPI_REG_ESR, ESR_DER);
    }
    if (context && context->interface && context->interface->error) {
        context->interface->error(context, err);
    }
}
/**
 * Pop error from queue
 * @param context - scpi context
 * @return error number
 */
int16_t SCPI_ErrorPop(scpi_context_t * context) {
    (void)context;
    int16_t result = scpi_err;
    scpi_err = 0;
    return result;
}
/**
 * Translate error number to string
 * @param err - error number
 * @return Error string representation
 */
const char * SCPI_ErrorTranslate(int16_t err) {
    switch(err) {
        case 0: return "No error";
        case SCPI_ERROR_SYNTAX: return "Syntax error";
        case SCPI_ERROR_INVALID_SEPARATOR: return "Invalid separator";
        case SCPI_ERROR_UNDEFINED_HEADER: return "Undefined header";
        case SCPI_ERROR_PARAMETER_NOT_ALLOWED: return "Parameter not allowed";
        case SCPI_ERROR_MISSING_PARAMETER: return "Missing parameter";
        default: return "Unknown error";
    }
}
scpi/scpi_error.h
New file
@@ -0,0 +1,60 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_error.h
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  Error handling and storing routines
 *
 *
 */
#ifndef SCPI_ERROR_H
#define    SCPI_ERROR_H
#ifdef    __cplusplus
extern "C" {
#endif
    void SCPI_ErrorClear(scpi_context_t * context);
    int16_t SCPI_ErrorPop(scpi_context_t * context);
    void SCPI_ErrorPush(scpi_context_t * context, int16_t err);
    const char * SCPI_ErrorTranslate(int16_t err);
#define SCPI_ERROR_SYNTAX               -102
#define SCPI_ERROR_INVALID_SEPARATOR    -103
#define SCPI_ERROR_UNDEFINED_HEADER     -113
#define SCPI_ERROR_PARAMETER_NOT_ALLOWED        -108
#define SCPI_ERROR_MISSING_PARAMETER    -109
#ifdef    __cplusplus
}
#endif
#endif    /* SCPI_ERROR_H */
scpi/scpi_ieee488.c
New file
@@ -0,0 +1,317 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_ieee488.c
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  Implementation of IEEE488.2 commands and state model
 *
 *
 */
#include "scpi.h"
#include "scpi_ieee488.h"
#include "scpi_error.h"
#include "scpi_constants.h"
#include <string.h>
#include <stdint.h>
/* register array */
static scpi_reg_val_t regs[SCPI_REG_COUNT];
/**
 * Update register value
 * @param name - register name
 */
static void SCPI_RegUpdate(scpi_reg_name_t name) {
    SCPI_RegSet(name, SCPI_RegGet(name));
}
/**
 * Get register value
 * @param name - register name
 * @return register value
 */
scpi_reg_val_t SCPI_RegGet(scpi_reg_name_t name) {
    if (name < SCPI_REG_COUNT) {
        return regs[name];
    } else {
        return 0;
    }
}
/**
 * Set register value
 * @param name - register name
 * @param val - new value
 */
void SCPI_RegSet(scpi_reg_name_t name, scpi_reg_val_t val) {
    if (name >= SCPI_REG_COUNT) {
        return;
    }
    // set register value
    regs[name] = val;
    switch (name) {
        case SCPI_REG_STB:
            if (val & (SCPI_RegGet(SCPI_REG_SRE) &~STB_SRQ)) {
                val |= STB_SRQ;
            } else {
                val &= ~STB_SRQ;
            }
            break;
        case SCPI_REG_SRE:
            SCPI_RegUpdate(SCPI_REG_STB);
            break;
        case SCPI_REG_ESR:
            if (val & SCPI_RegGet(SCPI_REG_ESE)) {
                SCPI_RegSetBits(SCPI_REG_STB, STB_ESR);
            } else {
                SCPI_RegClearBits(SCPI_REG_STB, STB_ESR);
            }
            break;
        case SCPI_REG_ESE:
            SCPI_RegUpdate(SCPI_REG_ESR);
            break;
        case SCPI_REG_QUES:
            if (val & SCPI_RegGet(SCPI_REG_QUESE)) {
                SCPI_RegSetBits(SCPI_REG_STB, STB_QES);
            } else {
                SCPI_RegClearBits(SCPI_REG_STB, STB_QES);
            }
            break;
        case SCPI_REG_QUESE:
            SCPI_RegUpdate(SCPI_REG_QUES);
            break;
        case SCPI_REG_OPER:
            if (val & SCPI_RegGet(SCPI_REG_OPERE)) {
                SCPI_RegSetBits(SCPI_REG_STB, STB_OPS);
            } else {
                SCPI_RegClearBits(SCPI_REG_STB, STB_OPS);
            }
            break;
        case SCPI_REG_OPERE:
            SCPI_RegUpdate(SCPI_REG_OPER);
            break;
        case SCPI_REG_COUNT:
            // nothing to do
            break;
    }
    // set updated register value
    regs[name] = val;
}
/**
 * Set register bits
 * @param name - register name
 * @param bits bit mask
 */
void SCPI_RegSetBits(scpi_reg_name_t name, scpi_reg_val_t bits) {
    SCPI_RegSet(name, SCPI_RegGet(name) | bits);
}
/**
 * Clear register bits
 * @param name - register name
 * @param bits bit mask
 */
void SCPI_RegClearBits(scpi_reg_name_t name, scpi_reg_val_t bits) {
    SCPI_RegSet(name, SCPI_RegGet(name) & ~bits);
}
/* ============ */
void SCPI_EventClear(void) {
    // TODO
    SCPI_RegSet(SCPI_REG_ESR, 0);
}
/**
 * *CLS
 * @param context
 * @return
 */
int SCPI_CoreCls(scpi_context_t * context) {
    (void) context;
    SCPI_EventClear();
    SCPI_ErrorClear(context);
    SCPI_RegSet(SCPI_REG_OPER, 0);
    SCPI_RegSet(SCPI_REG_QUES, 0);
    return 0;
}
/**
 * *ESE
 * @param context
 * @return
 */
int SCPI_CoreEse(scpi_context_t * context) {
    int32_t new_ESE;
    if (SCPI_ParamInt(context, &new_ESE, TRUE)) {
        SCPI_RegSet(SCPI_REG_ESE, new_ESE);
    }
    return 0;
}
/**
 * *ESE?
 * @param context
 * @return
 */
int SCPI_CoreEseQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_ESE));
    return 0;
}
/**
 * *ESR?
 * @param context
 * @return
 */
int SCPI_CoreEsrQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_ESR));
    SCPI_RegSet(SCPI_REG_ESR, 0);
    return 0;
}
/**
 * *IDN?
 * @param context
 * @return
 */
int SCPI_CoreIdnQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultString(context, SCPI_MANUFACTURE);
    SCPI_ResultString(context, SCPI_DEV_NAME);
    SCPI_ResultString(context, SCPI_DEV_VERSION);
    return 0;
}
/**
 * *OPC
 * @param context
 * @return
 */
int SCPI_CoreOpc(scpi_context_t * context) {
    (void) context;
    SCPI_RegSetBits(SCPI_REG_ESR, ESR_OPC);
    return 0;
}
/**
 * *OPC?
 * @param context
 * @return
 */
int SCPI_CoreOpcQ(scpi_context_t * context) {
    (void) context;
    // Operation is always completed
    SCPI_ResultInt(context, 1);
    return 0;
}
/**
 * *RST
 * @param context
 * @return
 */
int SCPI_CoreRst(scpi_context_t * context) {
    if (context && context->interface && context->interface->reset) {
        return context->interface->reset(context);
    }
    return 0;
}
/**
 * *SRE
 * @param context
 * @return
 */
int SCPI_CoreSre(scpi_context_t * context) {
    int32_t new_SRE;
    if (SCPI_ParamInt(context, &new_SRE, TRUE)) {
        SCPI_RegSet(SCPI_REG_SRE, new_SRE);
    }
    return 0;
}
/**
 * *SRE?
 * @param context
 * @return
 */
int SCPI_CoreSreQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_SRE));
    return 0;
}
/**
 * *STB?
 * @param context
 * @return
 */
int SCPI_CoreStbQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_STB));
    return 0;
}
/**
 * *TST?
 * @param context
 * @return
 */
int SCPI_CoreTstQ(scpi_context_t * context) {
    (void) context;
    int result = 0;
    if (context && context->interface && context->interface->test) {
        result = context->interface->test(context);
    }
    SCPI_ResultInt(context, result);
    return 0;
}
/**
 * *WAI
 * @param context
 * @return
 */
int SCPI_CoreWai(scpi_context_t * context) {
    (void) context;
    // NOP
    return 0;
}
scpi/scpi_ieee488.h
New file
@@ -0,0 +1,101 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_ieee488.h
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  Implementation of IEEE488.2 commands and state model
 *
 *
 */
#ifndef SCPI_IEEE488_H
#define    SCPI_IEEE488_H
#include "scpi.h"
#include <stdint.h>
typedef uint16_t scpi_reg_val_t;
typedef enum {
    SCPI_REG_STB = 0,   // Status Byte
    SCPI_REG_SRE,       // Service Request Enable Register
    SCPI_REG_ESR,       // Standard Event Status Register (ESR, SESR)
    SCPI_REG_ESE,       // Event Status Enable Register
    SCPI_REG_OPER,      // OPERation Status Register
    SCPI_REG_OPERE,     // OPERation Status Enable Register
    SCPI_REG_QUES,      // QUEStionable status register
    SCPI_REG_QUESE,     // QUEStionable status Enable Register
    /* last definition - number of registers */
    SCPI_REG_COUNT,
} scpi_reg_name_t;
int SCPI_CoreCls(scpi_context_t * context);
int SCPI_CoreEse(scpi_context_t * context);
int SCPI_CoreEseQ(scpi_context_t * context);
int SCPI_CoreEsrQ(scpi_context_t * context);
int SCPI_CoreIdnQ(scpi_context_t * context);
int SCPI_CoreOpc(scpi_context_t * context);
int SCPI_CoreOpcQ(scpi_context_t * context);
int SCPI_CoreRst(scpi_context_t * context);
int SCPI_CoreSre(scpi_context_t * context);
int SCPI_CoreSreQ(scpi_context_t * context);
int SCPI_CoreStbQ(scpi_context_t * context);
int SCPI_CoreTstQ(scpi_context_t * context);
int SCPI_CoreWai(scpi_context_t * context);
#define STB_R01 0x01                    // Not used
#define STB_PRO 0x02                    // Protection Event Flag
#define STB_QMA 0x04                    // Error/Event queue message available
#define STB_QES 0x08                    // Questionable status
#define STB_MAV 0x10                    // Message Available
#define STB_ESR 0x20                    // Standard Event Status Register
#define STB_SRQ 0x40                    // Service Request
#define STB_OPS 0x80                    // Operation Status Flag
#define ESR_OPC 0x01                    // Operation complete
#define ESR_REQ 0x02                    // Request Control
#define ESR_QER 0x04                    // Query Error
#define ESR_DER 0x08                    // Device Dependent Error
#define ESR_EER 0x10                    // Execution Error (e.g. range error)
#define ESR_CER 0x20                    // Command error (e.g. syntax error)
#define ESR_URQ 0x40                    // User Request
#define ESR_PON 0x80                    // Power On
scpi_reg_val_t SCPI_RegGet(scpi_reg_name_t name);
void SCPI_RegSet(scpi_reg_name_t name, scpi_reg_val_t val);
void SCPI_RegSetBits(scpi_reg_name_t name, scpi_reg_val_t bits);
void SCPI_RegClearBits(scpi_reg_name_t name, scpi_reg_val_t bits);
#endif    /* SCPI_IEEE488_H */
scpi/scpi_minimal.c
New file
@@ -0,0 +1,146 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_minimal.c
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  SCPI minimal implementation
 *
 *
 */
#include "scpi.h"
#include "scpi_minimal.h"
#include "scpi_constants.h"
#include "scpi_error.h"
#include "scpi_ieee488.h"
/**
 * Command stub function
 * @param context
 * @return
 */
int SCPI_Stub(scpi_context_t * context) {
    (void) context;
    return 0;
}
/**
 * Query command stub function
 * @param context
 * @return
 */
int SCPI_StubQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultString(context, "");
    return 0;
}
/**
 * SYSTem:VERSion?
 * @param context
 * @return
 */
int SCPI_SystemVersionQ(scpi_context_t * context) {
    (void) context;
    SCPI_ResultString(context, SCPI_DEV_VERSION);
    return 0;
}
/**
 * SYSTem:ERRor[:NEXT]?
 * @param context
 * @return
 */
int SCPI_SystemErrorNextQ(scpi_context_t * context) {
    (void) context;
    int16_t err = SCPI_ErrorPop(context);
    SCPI_ResultInt(context, err);
    SCPI_ResultText(context, SCPI_ErrorTranslate(err));
    return 0;
}
/**
 * STATus:QUEStionable[:EVENt]?
 * @param context
 * @return
 */
int SCPI_StatusQuestionableEventQ(scpi_context_t * context) {
    (void) context;
    // return value
    SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_QUES));
    // clear register
    SCPI_RegSet(SCPI_REG_QUES, 0);
    return 0;
}
/**
 * STATus:QUEStionable:ENABle?
 * @param context
 * @return
 */
int SCPI_StatusQuestionableEnableQ(scpi_context_t * context) {
    (void) context;
    // return value
    SCPI_ResultInt(context, SCPI_RegGet(SCPI_REG_QUESE));
    return 0;
}
/**
 * STATus:QUEStionable:ENABle
 * @param context
 * @return
 */
int SCPI_StatusQuestionableEnable(scpi_context_t * context) {
    int32_t new_QUESE;
    if (SCPI_ParamInt(context, &new_QUESE, TRUE)) {
        SCPI_RegSet(SCPI_REG_QUESE, new_QUESE);
    }
    return 0;
}
/**
 * STATus:PRESet
 * @param context
 * @return
 */
int SCPI_StatusPreset(scpi_context_t * context) {
    (void) context;
    // clear STATUS:...
    SCPI_RegSet(SCPI_REG_QUES, 0);
    return 0;
}
scpi/scpi_minimal.h
New file
@@ -0,0 +1,60 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_minimal.h
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  SCPI minimal implementation
 *
 *
 */
#ifndef SCPI_MINIMAL_H
#define    SCPI_MINIMAL_H
#ifdef    __cplusplus
extern "C" {
#endif
    int SCPI_Stub(scpi_context_t * context);
    int SCPI_StubQ(scpi_context_t * context);
    int SCPI_SystemVersionQ(scpi_context_t * context);
    int SCPI_SystemErrorNextQ(scpi_context_t * context);
    int SCPI_StatusQuestionableEventQ(scpi_context_t * context);
    int SCPI_StatusQuestionableEnableQ(scpi_context_t * context);
    int SCPI_StatusQuestionableEnable(scpi_context_t * context);
    int SCPI_StatusPreset(scpi_context_t * context);
#ifdef    __cplusplus
}
#endif
#endif    /* SCPI_MINIMAL_H */
scpi/scpi_utils.c
New file
@@ -0,0 +1,153 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_utils.c
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  Conversion routines and string manipulation routines
 *
 *
 */
#include "scpi.h"
#include "scpi_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
 * Find the first occurrence in str of a character in set.
 * @param str
 * @param size
 * @param set
 * @return
 */
char * strnpbrk(const char *str, size_t size, const char *set) {
        const char *scanp;
        long c, sc;
        const char * strend = str + size;
        while ((strend != str) && ((c = *str++) != 0)) {
            for (scanp = set; (sc = *scanp++) != '\0';)
                if (sc == c)
                    return ((char *) (str - 1));
        }
        return (NULL);
}
/**
 * Converts signed 32b integer value to string
 * @param val   integer value
 * @param str   converted textual representation
 * @param len   string buffer length
 * @return number of bytes written to str (without '\0')
 */
size_t longToStr(int32_t val, char * str, size_t len) {
    uint32_t x = 1000000000L;
    int_fast8_t digit;
    size_t pos = 0;
    if (val == 0) {
        if (pos < len) str[pos++] = '0';
    } else {
        if (val < 0) {
            val = -val;
            if (pos < len) str[pos++] = '-';
        }
        while ((val / x) == 0) {
            x /= 10;
        }
        do {
            digit = (uint8_t) (val / x);
            if (pos < len) str[pos++] = digit + '0';
            val -= digit * x;
            x /= 10;
        } while (x && (pos < len));
    }
    if (pos < len) str[pos] = 0;
    return pos;
}
/**
 * Converts double value to string
 * @param val   double value
 * @param str   converted textual representation
 * @param len   string buffer length
 * @return number of bytes written to str (without '\0')
 */
size_t doubleToStr(double val, char * str, size_t len) {
    return snprintf(str, len, "%lf", val);
}
/**
 * Converts string to signed 32bit integer representation
 * @param str   string value
 * @param val   32bit integer result
 * @return      number of bytes used in string
 */
size_t strToLong(const char * str, int32_t * val) {
    char * endptr;
    *val = strtol(str, &endptr, 0);
    return endptr - str;
}
/**
 * Converts string to double representation
 * @param str   string value
 * @param val   double result
 * @return      number of bytes used in string
 */
size_t strToDouble(const char * str, double * val) {
    char * endptr;
    *val = strtod(str, &endptr);
    return endptr - str;
}
/**
 * Compare two strings with exact length
 * @param str1
 * @param len1
 * @param str2
 * @param len2
 * @return TRUE if len1==len2 and "len" characters of both strings are equal
 */
bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) {
    if (len1 != len2) {
        return FALSE;
    }
    if (strncasecmp(str1, str2, len2) == 0) {
        return TRUE;
    }
    return FALSE;
}
scpi/scpi_utils.h
New file
@@ -0,0 +1,60 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   scpi_utils.h
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  Conversion routines and string manipulation routines
 *
 *
 */
#ifndef SCPI_UTILS_H
#define    SCPI_UTILS_H
#include "scpi.h"
#include <stdint.h>
#ifdef    __cplusplus
extern "C" {
#endif
    char * strnpbrk(const char *str, size_t size, const char *set);
    bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2);
    size_t longToStr(int32_t val, char * str, size_t len);
    size_t doubleToStr(double val, char * str, size_t len);
    size_t strToLong(const char * str, int32_t * val);
    size_t strToDouble(const char * str, double * val);
#ifdef    __cplusplus
}
#endif
#endif    /* SCPI_UTILS_H */
test-parser.c
New file
@@ -0,0 +1,184 @@
/*-
 * Copyright (c) 2012-2013 Jan Breuer,
 *
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @file   main.c
 * @date   Thu Nov 15 10:58:45 UTC 2012
 *
 * @brief  SCPI parser test
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "scpi/scpi.h"
#include "scpi/scpi_ieee488.h"
#include "scpi/scpi_error.h"
#include "scpi/scpi_constants.h"
#include "scpi/scpi_minimal.h"
int DMM_MeasureVoltageDcQ(scpi_context_t * context) {
    double param1, param2;
    fprintf(stderr, "meas:volt:dc "); // debug command name
    // read first parameter
    if (SCPI_ParamDouble(context, &param1, false)) {
        fprintf(stderr, "P1=%lf ", param1);
    }
    // read second paraeter
    if (SCPI_ParamDouble(context, &param2, false)) {
        fprintf(stderr, "P2=%lf ", param2);
    }
    fprintf(stderr, "\r\n");
    SCPI_ResultDouble(context, 0);
    return 0;
}
scpi_command_t scpi_commands[] = {
    /* Required IEEE488.2 Common Commands (see SCPI Standard V1999.0 ch4.1.1) */
    { .pattern = "*CLS", .callback = SCPI_CoreCls,},
    { .pattern = "*ESE", .callback = SCPI_CoreEse,},
    { .pattern = "*ESE?", .callback = SCPI_CoreEseQ,},
    { .pattern = "*ESR?", .callback = SCPI_CoreEsrQ,},
    { .pattern = "*IDN?", .callback = SCPI_CoreIdnQ,},
    { .pattern = "*OPC", .callback = SCPI_CoreOpc,},
    { .pattern = "*OPC?", .callback = SCPI_CoreOpcQ,},
    { .pattern = "*RST", .callback = SCPI_CoreRst,},
    { .pattern = "*SRE", .callback = SCPI_CoreSre,},
    { .pattern = "*SRE?", .callback = SCPI_CoreSreQ,},
    { .pattern = "*STB?", .callback = SCPI_CoreStbQ,},
    { .pattern = "*TST?", .callback = SCPI_CoreTstQ,},
    { .pattern = "*WAI", .callback = SCPI_CoreWai,},
    /* Required SCPI commands (see SCPI Standard V1999.0 ch 4.2.1) */
    {.pattern = "SYSTem:ERRor?", .callback = SCPI_SystemErrorNextQ,},
    {.pattern = "SYSTem:ERRor:NEXT?", .callback = SCPI_SystemErrorNextQ,},
    {.pattern = "SYSTem:VERSion?", .callback = SCPI_SystemVersionQ,},
    //{.pattern = "STATus:OPERation?", .callback = scpi_stub_callback,},
    //{.pattern = "STATus:OPERation:EVENt?", .callback = scpi_stub_callback,},
    //{.pattern = "STATus:OPERation:CONDition?", .callback = scpi_stub_callback,},
    //{.pattern = "STATus:OPERation:ENABle", .callback = scpi_stub_callback,},
    //{.pattern = "STATus:OPERation:ENABle?", .callback = scpi_stub_callback,},
    {.pattern = "STATus:QUEStionable?", .callback = SCPI_StatusQuestionableEventQ,},
    {.pattern = "STATus:QUEStionable:EVENt?", .callback = SCPI_StatusQuestionableEventQ,},
    //{.pattern = "STATus:QUEStionable:CONDition?", .callback = scpi_stub_callback,},
    {.pattern = "STATus:QUEStionable:ENABle", .callback = SCPI_StatusQuestionableEnable,},
    {.pattern = "STATus:QUEStionable:ENABle?", .callback = SCPI_StatusQuestionableEnableQ,},
    {.pattern = "STATus:PRESet", .callback = SCPI_StatusPreset,},
    /* DMM */
    {.pattern = "MEASure:VOLTage:DC?", .callback = DMM_MeasureVoltageDcQ,},
    {.pattern = "MEASure:VOLTage:DC:RATio?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:VOLTage:AC?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:CURRent:DC?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:CURRent:AC?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:RESistance?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:FRESistance?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:FREQuency?", .callback = SCPI_StubQ,},
    {.pattern = "MEASure:PERiod?", .callback = SCPI_StubQ,},
    SCPI_CMD_LIST_END
};
size_t SCPI_Write(scpi_context_t * context, const char * data, size_t len) {
    (void) context;
    return fwrite(data, 1, len, stdout);
}
int SCPI_Error(scpi_context_t * context, int_fast16_t err) {
    (void) context;
    fprintf(stderr, "**ERROR: %d, \"%s\"\r\n", (int32_t) err, SCPI_ErrorTranslate(err));
    return 0;
}
scpi_interface_t scpi_interface = {
    .write = SCPI_Write,
    .error = SCPI_Error,
    .reset = NULL,
    .test = NULL,
};
#define SCPI_BUFFER_LENGTH 256
char buffer[SCPI_BUFFER_LENGTH];
scpi_buffer_t scpi_buffer = {
    .length = SCPI_BUFFER_LENGTH,
    .data = buffer,
    //    .data = (char[SCPI_BUFFER_LENGTH]){},
};
scpi_context_t scpi_context;
/*
 *
 */
int main(int argc, char** argv) {
    (void) argc;
    (void) argv;
    int result;
    //printf("%.*s %s\r\n",  3, "asdadasdasdasdas", "b");
    SCPI_Init(&scpi_context, scpi_commands, &scpi_buffer, &scpi_interface);
    // // interactive demo
    //  char smbuffer[10];
    //  while(1) {
    //          fgets(smbuffer, 10, stdin);
    //          SCPI_Input(&scpi_context, smbuffer, strlen(smbuffer));
    //  }
    result = SCPI_Input(&scpi_context, "*CLS\r\n", 6);
    result = SCPI_Input(&scpi_context, "*RST\r\n", 6);
    result = SCPI_Input(&scpi_context, "MEAS:volt:DC? 12,50;*OPC\r\n", 26);
    result = SCPI_Input(&scpi_context, "*IDN?\r\n", 7);
    result = SCPI_Input(&scpi_context, "SYST:VERS?", 10);
    result = SCPI_Input(&scpi_context, "\r\n*ID", 5);
    result = SCPI_Input(&scpi_context, "N?", 2);
    result = SCPI_Input(&scpi_context, NULL, 0); // emulate command timeout
    result = SCPI_Input(&scpi_context, "*ESE\r\n", 6);
    result = SCPI_Input(&scpi_context, "*ESE 0x20\r\n", 11);
    result = SCPI_Input(&scpi_context, "IDN?\r\n", 6); // cause error -113, undefined header
    result = SCPI_Input(&scpi_context, "SYST:ERR?\r\n", 11);
    result = SCPI_Input(&scpi_context, "SYST:ERR?\r\n", 11);
    result = SCPI_Input(&scpi_context, "*STB?\r\n", 6);
    result = SCPI_Input(&scpi_context, "*ESR?\r\n", 6);
    result = SCPI_Input(&scpi_context, "*STB?\r\n", 6);
    return (EXIT_SUCCESS);
}